MaterialDialog.java 61 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563
  1. package com.afollestad.materialdialogs;
  2. import android.annotation.SuppressLint;
  3. import android.content.Context;
  4. import android.content.DialogInterface;
  5. import android.graphics.Paint;
  6. import android.graphics.Typeface;
  7. import android.graphics.drawable.Drawable;
  8. import android.os.Build;
  9. import android.os.Looper;
  10. import android.support.annotation.ArrayRes;
  11. import android.support.annotation.AttrRes;
  12. import android.support.annotation.ColorRes;
  13. import android.support.annotation.DimenRes;
  14. import android.support.annotation.DrawableRes;
  15. import android.support.annotation.LayoutRes;
  16. import android.support.annotation.NonNull;
  17. import android.support.annotation.Nullable;
  18. import android.support.annotation.StringRes;
  19. import android.support.v4.content.res.ResourcesCompat;
  20. import android.text.Editable;
  21. import android.text.TextUtils;
  22. import android.text.TextWatcher;
  23. import android.view.LayoutInflater;
  24. import android.view.View;
  25. import android.view.ViewTreeObserver;
  26. import android.view.WindowManager;
  27. import android.widget.AdapterView;
  28. import android.widget.CheckBox;
  29. import android.widget.EditText;
  30. import android.widget.FrameLayout;
  31. import android.widget.ImageView;
  32. import android.widget.LinearLayout;
  33. import android.widget.ListAdapter;
  34. import android.widget.ListView;
  35. import android.widget.ProgressBar;
  36. import android.widget.TextView;
  37. import com.afollestad.materialdialogs.internal.MDButton;
  38. import com.afollestad.materialdialogs.internal.MDRootLayout;
  39. import com.afollestad.materialdialogs.internal.MDTintHelper;
  40. import com.afollestad.materialdialogs.util.DialogUtils;
  41. import com.afollestad.materialdialogs.util.TypefaceHelper;
  42. import java.util.ArrayList;
  43. import java.util.Arrays;
  44. import java.util.Collections;
  45. import java.util.List;
  46. /**
  47. * @author Aidan Follestad (afollestad)
  48. */
  49. public class MaterialDialog extends DialogBase implements
  50. View.OnClickListener, AdapterView.OnItemClickListener {
  51. protected final MDRootLayout view;
  52. protected final Builder mBuilder;
  53. protected ListView listView;
  54. protected ImageView icon;
  55. protected TextView title;
  56. protected View titleFrame;
  57. protected FrameLayout customViewFrame;
  58. protected ProgressBar mProgress;
  59. protected TextView mProgressLabel;
  60. protected TextView mProgressMinMax;
  61. protected TextView content;
  62. protected EditText input;
  63. protected TextView inputMinMax;
  64. protected MDButton positiveButton;
  65. protected MDButton neutralButton;
  66. protected MDButton negativeButton;
  67. protected ListType listType;
  68. protected List<Integer> selectedIndicesList;
  69. public final Builder getBuilder() {
  70. return mBuilder;
  71. }
  72. @SuppressLint("InflateParams")
  73. protected MaterialDialog(Builder builder) {
  74. super(builder.context, DialogInit.getTheme(builder));
  75. mBuilder = builder;
  76. final LayoutInflater inflater = LayoutInflater.from(builder.context);
  77. view = (MDRootLayout) inflater.inflate(DialogInit.getInflateLayout(builder), null);
  78. DialogInit.init(this);
  79. }
  80. public final void setTypeface(TextView target, Typeface t) {
  81. if (t == null) return;
  82. int flags = target.getPaintFlags() | Paint.SUBPIXEL_TEXT_FLAG;
  83. target.setPaintFlags(flags);
  84. target.setTypeface(t);
  85. }
  86. protected final void checkIfListInitScroll() {
  87. if (listView == null)
  88. return;
  89. listView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
  90. @Override
  91. public void onGlobalLayout() {
  92. if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
  93. //noinspection deprecation
  94. listView.getViewTreeObserver().removeGlobalOnLayoutListener(this);
  95. } else {
  96. listView.getViewTreeObserver().removeOnGlobalLayoutListener(this);
  97. }
  98. if (listType == ListType.SINGLE || listType == ListType.MULTI) {
  99. int selectedIndex;
  100. if (listType == ListType.SINGLE) {
  101. if (mBuilder.selectedIndex < 0)
  102. return;
  103. selectedIndex = mBuilder.selectedIndex;
  104. } else {
  105. if (mBuilder.selectedIndices == null || mBuilder.selectedIndices.length == 0)
  106. return;
  107. List<Integer> indicesList = Arrays.asList(mBuilder.selectedIndices);
  108. Collections.sort(indicesList);
  109. selectedIndex = indicesList.get(0);
  110. }
  111. if (listView.getLastVisiblePosition() < selectedIndex) {
  112. final int totalVisible = listView.getLastVisiblePosition() - listView.getFirstVisiblePosition();
  113. // Scroll so that the selected index appears in the middle (vertically) of the ListView
  114. int scrollIndex = selectedIndex - (totalVisible / 2);
  115. if (scrollIndex < 0) scrollIndex = 0;
  116. final int fScrollIndex = scrollIndex;
  117. listView.post(new Runnable() {
  118. @Override
  119. public void run() {
  120. listView.requestFocus();
  121. listView.setSelection(fScrollIndex);
  122. }
  123. });
  124. }
  125. }
  126. }
  127. });
  128. }
  129. /**
  130. * Sets the dialog ListView's adapter and it's item click listener.
  131. */
  132. protected final void invalidateList() {
  133. if (listView == null)
  134. return;
  135. else if ((mBuilder.items == null || mBuilder.items.length == 0) && mBuilder.adapter == null)
  136. return;
  137. // Set up list with adapter
  138. listView.setAdapter(mBuilder.adapter);
  139. if (listType != null || mBuilder.listCallbackCustom != null)
  140. listView.setOnItemClickListener(this);
  141. }
  142. @Override
  143. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  144. if (mBuilder.listCallbackCustom != null) {
  145. // Custom adapter
  146. CharSequence text = null;
  147. if (view instanceof TextView)
  148. text = ((TextView) view).getText();
  149. mBuilder.listCallbackCustom.onSelection(this, view, position, text);
  150. } else if (listType == null || listType == ListType.REGULAR) {
  151. // Default adapter, non choice mode
  152. if (mBuilder.autoDismiss) {
  153. // If auto dismiss is enabled, dismiss the dialog when a list item is selected
  154. dismiss();
  155. }
  156. mBuilder.listCallback.onSelection(this, view, position, mBuilder.items[position]);
  157. } else {
  158. // Default adapter, choice mode
  159. if (listType == ListType.MULTI) {
  160. final boolean shouldBeChecked = !selectedIndicesList.contains(Integer.valueOf(position));
  161. final CheckBox cb = (CheckBox) ((LinearLayout) view).getChildAt(0);
  162. if (shouldBeChecked) {
  163. // Add the selection to the states first so the callback includes it (when alwaysCallMultiChoiceCallback)
  164. selectedIndicesList.add(position);
  165. if (mBuilder.alwaysCallMultiChoiceCallback) {
  166. // If the checkbox wasn't previously selected, and the callback returns true, add it to the states and check it
  167. if (sendMultichoiceCallback()) {
  168. cb.setChecked(true);
  169. } else {
  170. // The callback cancelled selection, remove it from the states
  171. selectedIndicesList.remove(Integer.valueOf(position));
  172. }
  173. } else {
  174. // The callback was not used to check if selection is allowed, just select it
  175. cb.setChecked(true);
  176. }
  177. } else {
  178. // The checkbox was unchecked
  179. selectedIndicesList.remove(Integer.valueOf(position));
  180. cb.setChecked(false);
  181. if (mBuilder.alwaysCallMultiChoiceCallback)
  182. sendMultichoiceCallback();
  183. }
  184. } else if (listType == ListType.SINGLE) {
  185. boolean allowSelection = true;
  186. if (mBuilder.autoDismiss && mBuilder.positiveText == null) {
  187. // If auto dismiss is enabled, and no action button is visible to approve the selection, dismiss the dialog
  188. dismiss();
  189. // Don't allow the selection to be updated since the dialog is being dismissed anyways
  190. allowSelection = false;
  191. // Update selected index and send callback
  192. mBuilder.selectedIndex = position;
  193. sendSingleChoiceCallback(view);
  194. } else if (mBuilder.alwaysCallSingleChoiceCallback) {
  195. int oldSelected = mBuilder.selectedIndex;
  196. // Temporarily set the new index so the callback uses the right one
  197. mBuilder.selectedIndex = position;
  198. // Only allow the radio button to be checked if the callback returns true
  199. allowSelection = sendSingleChoiceCallback(view);
  200. // Restore the old selected index, so the state is updated below
  201. mBuilder.selectedIndex = oldSelected;
  202. }
  203. // Update the checked states
  204. if (allowSelection && mBuilder.selectedIndex != position) {
  205. mBuilder.selectedIndex = position;
  206. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  207. }
  208. }
  209. }
  210. }
  211. public static class NotImplementedException extends Error {
  212. public NotImplementedException(@SuppressWarnings("SameParameterValue") String message) {
  213. super(message);
  214. }
  215. }
  216. public static class DialogException extends WindowManager.BadTokenException {
  217. public DialogException(@SuppressWarnings("SameParameterValue") String message) {
  218. super(message);
  219. }
  220. }
  221. protected final Drawable getListSelector() {
  222. if (mBuilder.listSelector != 0)
  223. return ResourcesCompat.getDrawable(mBuilder.context.getResources(), mBuilder.listSelector, null);
  224. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_list_selector);
  225. if (d != null) return d;
  226. return DialogUtils.resolveDrawable(getContext(), R.attr.md_list_selector);
  227. }
  228. /* package */ Drawable getButtonSelector(DialogAction which, boolean isStacked) {
  229. if (isStacked) {
  230. if (mBuilder.btnSelectorStacked != 0)
  231. return ResourcesCompat.getDrawable(mBuilder.context.getResources(), mBuilder.btnSelectorStacked, null);
  232. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_stacked_selector);
  233. if (d != null) return d;
  234. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_stacked_selector);
  235. } else {
  236. switch (which) {
  237. default: {
  238. if (mBuilder.btnSelectorPositive != 0)
  239. return ResourcesCompat.getDrawable(mBuilder.context.getResources(), mBuilder.btnSelectorPositive, null);
  240. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_positive_selector);
  241. if (d != null) return d;
  242. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_positive_selector);
  243. }
  244. case NEUTRAL: {
  245. if (mBuilder.btnSelectorNeutral != 0)
  246. return ResourcesCompat.getDrawable(mBuilder.context.getResources(), mBuilder.btnSelectorNeutral, null);
  247. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_neutral_selector);
  248. if (d != null) return d;
  249. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_neutral_selector);
  250. }
  251. case NEGATIVE: {
  252. if (mBuilder.btnSelectorNegative != 0)
  253. return ResourcesCompat.getDrawable(mBuilder.context.getResources(), mBuilder.btnSelectorNegative, null);
  254. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_negative_selector);
  255. if (d != null) return d;
  256. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_negative_selector);
  257. }
  258. }
  259. }
  260. }
  261. private boolean sendSingleChoiceCallback(View v) {
  262. CharSequence text = null;
  263. if (mBuilder.selectedIndex >= 0) {
  264. text = mBuilder.items[mBuilder.selectedIndex];
  265. }
  266. return mBuilder.listCallbackSingleChoice.onSelection(this, v, mBuilder.selectedIndex, text);
  267. }
  268. private boolean sendMultichoiceCallback() {
  269. Collections.sort(selectedIndicesList); // make sure the indicies are in order
  270. List<CharSequence> selectedTitles = new ArrayList<>();
  271. for (Integer i : selectedIndicesList) {
  272. selectedTitles.add(mBuilder.items[i]);
  273. }
  274. return mBuilder.listCallbackMultiChoice.onSelection(this,
  275. selectedIndicesList.toArray(new Integer[selectedIndicesList.size()]),
  276. selectedTitles.toArray(new CharSequence[selectedTitles.size()]));
  277. }
  278. @Override
  279. public final void onClick(View v) {
  280. DialogAction tag = (DialogAction) v.getTag();
  281. switch (tag) {
  282. case POSITIVE: {
  283. if (mBuilder.callback != null)
  284. mBuilder.callback.onPositive(this);
  285. if (mBuilder.listCallbackSingleChoice != null)
  286. sendSingleChoiceCallback(v);
  287. if (mBuilder.listCallbackMultiChoice != null)
  288. sendMultichoiceCallback();
  289. if (mBuilder.inputCallback != null && input != null && !mBuilder.alwaysCallInputCallback)
  290. mBuilder.inputCallback.onInput(this, input.getText());
  291. if (mBuilder.autoDismiss) dismiss();
  292. break;
  293. }
  294. case NEGATIVE: {
  295. if (mBuilder.callback != null)
  296. mBuilder.callback.onNegative(this);
  297. if (mBuilder.autoDismiss) dismiss();
  298. break;
  299. }
  300. case NEUTRAL: {
  301. if (mBuilder.callback != null)
  302. mBuilder.callback.onNeutral(this);
  303. if (mBuilder.autoDismiss) dismiss();
  304. break;
  305. }
  306. }
  307. }
  308. /**
  309. * The class used to construct a MaterialDialog.
  310. */
  311. public static class Builder {
  312. protected final Context context;
  313. protected CharSequence title;
  314. protected GravityEnum titleGravity = GravityEnum.START;
  315. protected GravityEnum contentGravity = GravityEnum.START;
  316. protected GravityEnum btnStackedGravity = GravityEnum.END;
  317. protected GravityEnum itemsGravity = GravityEnum.START;
  318. protected GravityEnum buttonsGravity = GravityEnum.START;
  319. protected int titleColor = -1;
  320. protected int contentColor = -1;
  321. protected CharSequence content;
  322. protected CharSequence[] items;
  323. protected CharSequence positiveText;
  324. protected CharSequence neutralText;
  325. protected CharSequence negativeText;
  326. protected View customView;
  327. protected int widgetColor;
  328. protected int positiveColor;
  329. protected int negativeColor;
  330. protected int neutralColor;
  331. protected ButtonCallback callback;
  332. protected ListCallback listCallback;
  333. protected ListCallbackSingleChoice listCallbackSingleChoice;
  334. protected ListCallbackMultiChoice listCallbackMultiChoice;
  335. protected ListCallback listCallbackCustom;
  336. protected boolean alwaysCallMultiChoiceCallback = false;
  337. protected boolean alwaysCallSingleChoiceCallback = false;
  338. protected Theme theme = Theme.LIGHT;
  339. protected boolean cancelable = true;
  340. protected float contentLineSpacingMultiplier = 1.2f;
  341. protected int selectedIndex = -1;
  342. protected Integer[] selectedIndices = null;
  343. protected boolean autoDismiss = true;
  344. protected Typeface regularFont;
  345. protected Typeface mediumFont;
  346. protected Drawable icon;
  347. protected boolean limitIconToDefaultSize;
  348. protected int maxIconSize = -1;
  349. protected ListAdapter adapter;
  350. protected OnDismissListener dismissListener;
  351. protected OnCancelListener cancelListener;
  352. protected OnKeyListener keyListener;
  353. protected OnShowListener showListener;
  354. protected boolean forceStacking;
  355. protected boolean wrapCustomViewInScroll;
  356. protected int dividerColor;
  357. protected int backgroundColor;
  358. protected int itemColor;
  359. protected boolean indeterminateProgress;
  360. protected boolean showMinMax;
  361. protected int progress = -2;
  362. protected int progressMax = 0;
  363. protected CharSequence inputPrefill;
  364. protected CharSequence inputHint;
  365. protected InputCallback inputCallback;
  366. protected boolean inputAllowEmpty;
  367. protected int inputType = -1;
  368. protected boolean alwaysCallInputCallback;
  369. protected int inputMaxLength = -1;
  370. protected int inputMaxLengthErrorColor = 0;
  371. protected boolean titleColorSet = false;
  372. protected boolean contentColorSet = false;
  373. protected boolean itemColorSet = false;
  374. @DrawableRes
  375. protected int listSelector;
  376. @DrawableRes
  377. protected int btnSelectorStacked;
  378. @DrawableRes
  379. protected int btnSelectorPositive;
  380. @DrawableRes
  381. protected int btnSelectorNeutral;
  382. @DrawableRes
  383. protected int btnSelectorNegative;
  384. public final Context getContext() {
  385. return context;
  386. }
  387. public final GravityEnum getItemsGravity() {
  388. return itemsGravity;
  389. }
  390. public final int getTitleColor() {
  391. return titleColor;
  392. }
  393. public final Typeface getRegularFont() {
  394. return regularFont;
  395. }
  396. public Builder(@NonNull Context context) {
  397. this.context = context;
  398. final int materialBlue = context.getResources().getColor(R.color.md_material_blue_600);
  399. // Retrieve default accent colors, which are used on the action buttons and progress bars
  400. this.widgetColor = DialogUtils.resolveColor(context, R.attr.colorAccent, materialBlue);
  401. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  402. this.widgetColor = DialogUtils.resolveColor(context, android.R.attr.colorAccent, this.widgetColor);
  403. }
  404. this.positiveColor = this.widgetColor;
  405. this.negativeColor = this.widgetColor;
  406. this.neutralColor = this.widgetColor;
  407. // Set the default theme based on the Activity theme's primary color darkness (more white or more black)
  408. final int primaryTextColor = DialogUtils.resolveColor(context, android.R.attr.textColorPrimary);
  409. this.theme = DialogUtils.isColorDark(primaryTextColor) ? Theme.LIGHT : Theme.DARK;
  410. // Load theme values from the ThemeSingleton if needed
  411. checkSingleton();
  412. // Retrieve gravity settings from global theme attributes if needed
  413. this.titleGravity = DialogUtils.resolveGravityEnum(context, R.attr.md_title_gravity, this.titleGravity);
  414. this.contentGravity = DialogUtils.resolveGravityEnum(context, R.attr.md_content_gravity, this.contentGravity);
  415. this.btnStackedGravity = DialogUtils.resolveGravityEnum(context, R.attr.md_btnstacked_gravity, this.btnStackedGravity);
  416. this.itemsGravity = DialogUtils.resolveGravityEnum(context, R.attr.md_items_gravity, this.itemsGravity);
  417. this.buttonsGravity = DialogUtils.resolveGravityEnum(context, R.attr.md_buttons_gravity, this.buttonsGravity);
  418. final String mediumFont = DialogUtils.resolveString(context, R.attr.md_medium_font);
  419. final String regularFont = DialogUtils.resolveString(context, R.attr.md_regular_font);
  420. typeface(mediumFont, regularFont);
  421. if (this.mediumFont == null) {
  422. try {
  423. this.mediumFont = Typeface.create("sans-serif-medium", Typeface.NORMAL);
  424. } catch (Throwable ignored) {
  425. }
  426. }
  427. if (this.regularFont == null) {
  428. try {
  429. this.regularFont = Typeface.create("sans-serif", Typeface.NORMAL);
  430. } catch (Throwable ignored) {
  431. }
  432. }
  433. if (this.mediumFont == null)
  434. this.mediumFont = this.regularFont;
  435. }
  436. private void checkSingleton() {
  437. if (ThemeSingleton.get(false) == null) return;
  438. ThemeSingleton s = ThemeSingleton.get();
  439. if (s.darkTheme)
  440. this.theme = Theme.DARK;
  441. if (s.titleColor != 0)
  442. this.titleColor = s.titleColor;
  443. if (s.contentColor != 0)
  444. this.contentColor = s.contentColor;
  445. if (s.positiveColor != 0)
  446. this.positiveColor = s.positiveColor;
  447. if (s.neutralColor != 0)
  448. this.neutralColor = s.neutralColor;
  449. if (s.negativeColor != 0)
  450. this.negativeColor = s.negativeColor;
  451. if (s.itemColor != 0)
  452. this.itemColor = s.itemColor;
  453. if (s.icon != null)
  454. this.icon = s.icon;
  455. if (s.backgroundColor != 0)
  456. this.backgroundColor = s.backgroundColor;
  457. if (s.dividerColor != 0)
  458. this.dividerColor = s.dividerColor;
  459. if (s.btnSelectorStacked != 0)
  460. this.btnSelectorStacked = s.btnSelectorStacked;
  461. if (s.listSelector != 0)
  462. this.listSelector = s.listSelector;
  463. if (s.btnSelectorPositive != 0)
  464. this.btnSelectorPositive = s.btnSelectorPositive;
  465. if (s.btnSelectorNeutral != 0)
  466. this.btnSelectorNeutral = s.btnSelectorNeutral;
  467. if (s.btnSelectorNegative != 0)
  468. this.btnSelectorNegative = s.btnSelectorNegative;
  469. if (s.widgetColor != 0)
  470. this.widgetColor = s.widgetColor;
  471. this.titleGravity = s.titleGravity;
  472. this.contentGravity = s.contentGravity;
  473. this.btnStackedGravity = s.btnStackedGravity;
  474. this.itemsGravity = s.itemsGravity;
  475. this.buttonsGravity = s.buttonsGravity;
  476. }
  477. public Builder title(@StringRes int titleRes) {
  478. title(this.context.getText(titleRes));
  479. return this;
  480. }
  481. public Builder title(@NonNull CharSequence title) {
  482. this.title = title;
  483. return this;
  484. }
  485. public Builder titleGravity(@NonNull GravityEnum gravity) {
  486. this.titleGravity = gravity;
  487. return this;
  488. }
  489. public Builder titleColor(int color) {
  490. this.titleColor = color;
  491. this.titleColorSet = true;
  492. return this;
  493. }
  494. public Builder titleColorRes(@ColorRes int colorRes) {
  495. titleColor(this.context.getResources().getColor(colorRes));
  496. return this;
  497. }
  498. public Builder titleColorAttr(@AttrRes int colorAttr) {
  499. titleColor(DialogUtils.resolveColor(this.context, colorAttr));
  500. return this;
  501. }
  502. /**
  503. * Sets the fonts used in the dialog. It's recommended that you use {@link #typeface(String, String)} instead,
  504. * to avoid duplicate Typeface allocations and high memory usage.
  505. *
  506. * @param medium The font used on titles and action buttons. Null uses device default.
  507. * @param regular The font used everywhere else, like on the content and list items. Null uses device default.
  508. * @return The Builder instance so you can chain calls to it.
  509. */
  510. public Builder typeface(Typeface medium, Typeface regular) {
  511. this.mediumFont = medium;
  512. this.regularFont = regular;
  513. return this;
  514. }
  515. /**
  516. * Sets the fonts used in the dialog, by file names. This also uses TypefaceHelper in order
  517. * to avoid any un-needed allocations (it recycles typefaces for you).
  518. *
  519. * @param medium The name of font in assets/fonts used on titles and action buttons (null uses device default). E.g. [your-project]/app/main/assets/fonts/[medium]
  520. * @param regular The name of font in assets/fonts used everywhere else, like content and list items (null uses device default). E.g. [your-project]/app/main/assets/fonts/[regular]
  521. * @return The Builder instance so you can chain calls to it.
  522. */
  523. public Builder typeface(String medium, String regular) {
  524. if (medium != null) {
  525. this.mediumFont = TypefaceHelper.get(this.context, medium);
  526. if (this.mediumFont == null)
  527. throw new IllegalArgumentException("No font asset found for " + medium);
  528. }
  529. if (regular != null) {
  530. this.regularFont = TypefaceHelper.get(this.context, regular);
  531. if (this.regularFont == null)
  532. throw new IllegalArgumentException("No font asset found for " + regular);
  533. }
  534. return this;
  535. }
  536. public Builder icon(@NonNull Drawable icon) {
  537. this.icon = icon;
  538. return this;
  539. }
  540. public Builder iconRes(@DrawableRes int icon) {
  541. this.icon = ResourcesCompat.getDrawable(context.getResources(), icon, null);
  542. return this;
  543. }
  544. public Builder iconAttr(@AttrRes int iconAttr) {
  545. this.icon = DialogUtils.resolveDrawable(context, iconAttr);
  546. return this;
  547. }
  548. public Builder content(@StringRes int contentRes) {
  549. content(this.context.getText(contentRes));
  550. return this;
  551. }
  552. public Builder content(@NonNull CharSequence content) {
  553. if (this.customView != null)
  554. throw new IllegalStateException("You cannot set content() when you're using a custom view.");
  555. this.content = content;
  556. return this;
  557. }
  558. public Builder content(@StringRes int contentRes, Object... formatArgs) {
  559. content(this.context.getString(contentRes, formatArgs));
  560. return this;
  561. }
  562. public Builder contentColor(int color) {
  563. this.contentColor = color;
  564. this.contentColorSet = true;
  565. return this;
  566. }
  567. public Builder contentColorRes(@ColorRes int colorRes) {
  568. contentColor(this.context.getResources().getColor(colorRes));
  569. return this;
  570. }
  571. public Builder contentColorAttr(@AttrRes int colorAttr) {
  572. contentColor(DialogUtils.resolveColor(this.context, colorAttr));
  573. return this;
  574. }
  575. public Builder contentGravity(@NonNull GravityEnum gravity) {
  576. this.contentGravity = gravity;
  577. return this;
  578. }
  579. public Builder contentLineSpacing(float multiplier) {
  580. this.contentLineSpacingMultiplier = multiplier;
  581. return this;
  582. }
  583. public Builder items(@ArrayRes int itemsRes) {
  584. items(this.context.getResources().getTextArray(itemsRes));
  585. return this;
  586. }
  587. public Builder items(@NonNull CharSequence[] items) {
  588. if (this.customView != null)
  589. throw new IllegalStateException("You cannot set items() when you're using a custom view.");
  590. this.items = items;
  591. return this;
  592. }
  593. public Builder itemsCallback(@NonNull ListCallback callback) {
  594. this.listCallback = callback;
  595. this.listCallbackSingleChoice = null;
  596. this.listCallbackMultiChoice = null;
  597. return this;
  598. }
  599. public Builder itemColor(int color) {
  600. this.itemColor = color;
  601. this.itemColorSet = true;
  602. return this;
  603. }
  604. public Builder itemColorRes(@ColorRes int colorRes) {
  605. return itemColor(this.context.getResources().getColor(colorRes));
  606. }
  607. public Builder itemColorAttr(@AttrRes int colorAttr) {
  608. return itemColor(DialogUtils.resolveColor(this.context, colorAttr));
  609. }
  610. public Builder itemsGravity(@NonNull GravityEnum gravity) {
  611. this.itemsGravity = gravity;
  612. return this;
  613. }
  614. public Builder buttonsGravity(@NonNull GravityEnum gravity) {
  615. this.buttonsGravity = gravity;
  616. return this;
  617. }
  618. /**
  619. * Pass anything below 0 (such as -1) for the selected index to leave all options unselected initially.
  620. * Otherwise pass the index of an item that will be selected initially.
  621. *
  622. * @param selectedIndex The checkbox index that will be selected initially.
  623. * @param callback The callback that will be called when the presses the positive button.
  624. * @return The Builder instance so you can chain calls to it.
  625. */
  626. public Builder itemsCallbackSingleChoice(int selectedIndex, @NonNull ListCallbackSingleChoice callback) {
  627. this.selectedIndex = selectedIndex;
  628. this.listCallback = null;
  629. this.listCallbackSingleChoice = callback;
  630. this.listCallbackMultiChoice = null;
  631. return this;
  632. }
  633. /**
  634. * By default, the single choice callback is only called when the user clicks the positive button
  635. * or if there are no buttons. Call this to force it to always call on item clicks even if the
  636. * positive button exists.
  637. *
  638. * @return The Builder instance so you can chain calls to it.
  639. */
  640. public Builder alwaysCallSingleChoiceCallback() {
  641. this.alwaysCallSingleChoiceCallback = true;
  642. return this;
  643. }
  644. /**
  645. * Pass null for the selected indices to leave all options unselected initially. Otherwise pass
  646. * an array of indices that will be selected initially.
  647. *
  648. * @param selectedIndices The radio button indices that will be selected initially.
  649. * @param callback The callback that will be called when the presses the positive button.
  650. * @return The Builder instance so you can chain calls to it.
  651. */
  652. public Builder itemsCallbackMultiChoice(Integer[] selectedIndices, @NonNull ListCallbackMultiChoice callback) {
  653. this.selectedIndices = selectedIndices;
  654. this.listCallback = null;
  655. this.listCallbackSingleChoice = null;
  656. this.listCallbackMultiChoice = callback;
  657. return this;
  658. }
  659. /**
  660. * By default, the multi choice callback is only called when the user clicks the positive button
  661. * or if there are no buttons. Call this to force it to always call on item clicks even if the
  662. * positive button exists.
  663. *
  664. * @return The Builder instance so you can chain calls to it.
  665. */
  666. public Builder alwaysCallMultiChoiceCallback() {
  667. this.alwaysCallMultiChoiceCallback = true;
  668. return this;
  669. }
  670. public Builder positiveText(@StringRes int postiveRes) {
  671. positiveText(this.context.getText(postiveRes));
  672. return this;
  673. }
  674. public Builder positiveText(@NonNull CharSequence message) {
  675. this.positiveText = message;
  676. return this;
  677. }
  678. public Builder positiveColor(int color) {
  679. this.positiveColor = color;
  680. return this;
  681. }
  682. public Builder positiveColorRes(@ColorRes int colorRes) {
  683. return positiveColor(this.context.getResources().getColor(colorRes));
  684. }
  685. public Builder positiveColorAttr(@AttrRes int colorAttr) {
  686. return positiveColor(DialogUtils.resolveColor(this.context, colorAttr));
  687. }
  688. public Builder neutralText(@StringRes int neutralRes) {
  689. return neutralText(this.context.getText(neutralRes));
  690. }
  691. public Builder neutralText(@NonNull CharSequence message) {
  692. this.neutralText = message;
  693. return this;
  694. }
  695. public Builder negativeColor(int color) {
  696. this.negativeColor = color;
  697. return this;
  698. }
  699. public Builder negativeColorRes(@ColorRes int colorRes) {
  700. return negativeColor(this.context.getResources().getColor(colorRes));
  701. }
  702. public Builder negativeColorAttr(@AttrRes int colorAttr) {
  703. return negativeColor(DialogUtils.resolveColor(this.context, colorAttr));
  704. }
  705. public Builder negativeText(@StringRes int negativeRes) {
  706. return negativeText(this.context.getText(negativeRes));
  707. }
  708. public Builder negativeText(@NonNull CharSequence message) {
  709. this.negativeText = message;
  710. return this;
  711. }
  712. public Builder neutralColor(int color) {
  713. this.neutralColor = color;
  714. return this;
  715. }
  716. public Builder neutralColorRes(@ColorRes int colorRes) {
  717. return neutralColor(this.context.getResources().getColor(colorRes));
  718. }
  719. public Builder neutralColorAttr(@AttrRes int colorAttr) {
  720. return neutralColor(DialogUtils.resolveColor(this.context, colorAttr));
  721. }
  722. public Builder listSelector(@DrawableRes int selectorRes) {
  723. this.listSelector = selectorRes;
  724. return this;
  725. }
  726. public Builder btnSelectorStacked(@DrawableRes int selectorRes) {
  727. this.btnSelectorStacked = selectorRes;
  728. return this;
  729. }
  730. public Builder btnSelector(@DrawableRes int selectorRes) {
  731. this.btnSelectorPositive = selectorRes;
  732. this.btnSelectorNeutral = selectorRes;
  733. this.btnSelectorNegative = selectorRes;
  734. return this;
  735. }
  736. public Builder btnSelector(@DrawableRes int selectorRes, @NonNull DialogAction which) {
  737. switch (which) {
  738. default:
  739. this.btnSelectorPositive = selectorRes;
  740. break;
  741. case NEUTRAL:
  742. this.btnSelectorNeutral = selectorRes;
  743. break;
  744. case NEGATIVE:
  745. this.btnSelectorNegative = selectorRes;
  746. break;
  747. }
  748. return this;
  749. }
  750. /**
  751. * Sets the gravity used for the text in stacked action buttons. By default, it's #{@link GravityEnum#END}.
  752. *
  753. * @param gravity The gravity to use.
  754. * @return The Builder instance so calls can be chained.
  755. */
  756. public Builder btnStackedGravity(@NonNull GravityEnum gravity) {
  757. this.btnStackedGravity = gravity;
  758. return this;
  759. }
  760. public Builder customView(@LayoutRes int layoutRes, boolean wrapInScrollView) {
  761. LayoutInflater li = LayoutInflater.from(this.context);
  762. return customView(li.inflate(layoutRes, null), wrapInScrollView);
  763. }
  764. public Builder customView(@NonNull View view, boolean wrapInScrollView) {
  765. if (this.content != null)
  766. throw new IllegalStateException("You cannot use customView() when you have content set.");
  767. else if (this.items != null)
  768. throw new IllegalStateException("You cannot use customView() when you have items set.");
  769. else if (this.inputCallback != null)
  770. throw new IllegalStateException("You cannot use customView() with an input dialog");
  771. else if (this.progress > -2 || this.indeterminateProgress)
  772. throw new IllegalStateException("You cannot use customView() with a progress dialog");
  773. this.customView = view;
  774. this.wrapCustomViewInScroll = wrapInScrollView;
  775. return this;
  776. }
  777. /**
  778. * Makes this dialog a progress dialog.
  779. *
  780. * @param indeterminate If true, an infinite circular spinner is shown. If false, a horizontal progress bar is shown that is incremented or set via the built MaterialDialog instance.
  781. * @param max When indeterminate is false, the max value the horizontal progress bar can get to.
  782. * @return An instance of the Builder so calls can be chained.
  783. */
  784. public Builder progress(boolean indeterminate, int max) {
  785. if (this.customView != null)
  786. throw new IllegalStateException("You cannot set progress() when you're using a custom view.");
  787. if (indeterminate) {
  788. this.indeterminateProgress = true;
  789. this.progress = -2;
  790. } else {
  791. this.indeterminateProgress = false;
  792. this.progress = -1;
  793. this.progressMax = max;
  794. }
  795. return this;
  796. }
  797. /**
  798. * Makes this dialog a progress dialog.
  799. *
  800. * @param indeterminate If true, an infinite circular spinner is shown. If false, a horizontal progress bar is shown that is incremented or set via the built MaterialDialog instance.
  801. * @param max When indeterminate is false, the max value the horizontal progress bar can get to.
  802. * @param showMinMax For determinate dialogs, the min and max will be displayed to the left (start) of the progress bar, e.g. 50/100.
  803. * @return An instance of the Builder so calls can be chained.
  804. */
  805. public Builder progress(boolean indeterminate, int max, boolean showMinMax) {
  806. this.showMinMax = showMinMax;
  807. return progress(indeterminate, max);
  808. }
  809. public Builder widgetColor(int color) {
  810. this.widgetColor = color;
  811. return this;
  812. }
  813. public Builder widgetColorRes(@ColorRes int colorRes) {
  814. return widgetColor(this.context.getResources().getColor(colorRes));
  815. }
  816. public Builder widgetColorAttr(@AttrRes int colorAttr) {
  817. return widgetColorRes(DialogUtils.resolveColor(this.context, colorAttr));
  818. }
  819. public Builder dividerColor(int color) {
  820. this.dividerColor = color;
  821. return this;
  822. }
  823. public Builder dividerColorRes(@ColorRes int colorRes) {
  824. return dividerColor(this.context.getResources().getColor(colorRes));
  825. }
  826. public Builder dividerColorAttr(@AttrRes int colorAttr) {
  827. return dividerColor(DialogUtils.resolveColor(this.context, colorAttr));
  828. }
  829. public Builder backgroundColor(int color) {
  830. this.backgroundColor = color;
  831. return this;
  832. }
  833. public Builder backgroundColorRes(@ColorRes int colorRes) {
  834. return backgroundColor(this.context.getResources().getColor(colorRes));
  835. }
  836. public Builder backgroundColorAttr(@AttrRes int colorAttr) {
  837. return backgroundColor(DialogUtils.resolveColor(this.context, colorAttr));
  838. }
  839. public Builder callback(@NonNull ButtonCallback callback) {
  840. this.callback = callback;
  841. return this;
  842. }
  843. public Builder theme(@NonNull Theme theme) {
  844. this.theme = theme;
  845. return this;
  846. }
  847. public Builder cancelable(boolean cancelable) {
  848. this.cancelable = cancelable;
  849. return this;
  850. }
  851. /**
  852. * This defaults to true. If set to false, the dialog will not automatically be dismissed
  853. * when an action button is pressed, and not automatically dismissed when the user selects
  854. * a list item.
  855. *
  856. * @param dismiss Whether or not to dismiss the dialog automatically.
  857. * @return The Builder instance so you can chain calls to it.
  858. */
  859. public Builder autoDismiss(boolean dismiss) {
  860. this.autoDismiss = dismiss;
  861. return this;
  862. }
  863. /**
  864. * Sets a custom {@link android.widget.ListAdapter} for the dialog's list
  865. *
  866. * @param adapter The adapter to set to the list.
  867. * @param callback The callback invoked when an item in the list is selected.
  868. * @return This Builder object to allow for chaining of calls to set methods
  869. */
  870. public Builder adapter(@NonNull ListAdapter adapter, ListCallback callback) {
  871. if (this.customView != null)
  872. throw new IllegalStateException("You cannot set adapter() when you're using a custom view.");
  873. this.adapter = adapter;
  874. this.listCallbackCustom = callback;
  875. return this;
  876. }
  877. /**
  878. * Limits the display size of a set icon to 48dp.
  879. */
  880. public Builder limitIconToDefaultSize() {
  881. this.limitIconToDefaultSize = true;
  882. return this;
  883. }
  884. public Builder maxIconSize(int maxIconSize) {
  885. this.maxIconSize = maxIconSize;
  886. return this;
  887. }
  888. public Builder maxIconSizeRes(@DimenRes int maxIconSizeRes) {
  889. return maxIconSize((int) this.context.getResources().getDimension(maxIconSizeRes));
  890. }
  891. public Builder showListener(@NonNull OnShowListener listener) {
  892. this.showListener = listener;
  893. return this;
  894. }
  895. public Builder dismissListener(@NonNull OnDismissListener listener) {
  896. this.dismissListener = listener;
  897. return this;
  898. }
  899. public Builder cancelListener(@NonNull OnCancelListener listener) {
  900. this.cancelListener = listener;
  901. return this;
  902. }
  903. public Builder keyListener(@NonNull OnKeyListener listener) {
  904. this.keyListener = listener;
  905. return this;
  906. }
  907. public Builder forceStacking(boolean stacked) {
  908. this.forceStacking = stacked;
  909. return this;
  910. }
  911. public Builder input(CharSequence hint, CharSequence prefill, boolean allowEmptyInput, @NonNull InputCallback callback) {
  912. if (this.customView != null)
  913. throw new IllegalStateException("You cannot set content() when you're using a custom view.");
  914. this.inputCallback = callback;
  915. this.inputHint = hint;
  916. this.inputPrefill = prefill;
  917. this.inputAllowEmpty = allowEmptyInput;
  918. return this;
  919. }
  920. public Builder input(CharSequence hint, CharSequence prefill, @NonNull InputCallback callback) {
  921. return input(hint, prefill, true, callback);
  922. }
  923. public Builder input(@StringRes int hint, @StringRes int prefill, boolean allowEmptyInput, @NonNull InputCallback callback) {
  924. return input(hint == 0 ? null : context.getText(hint), prefill == 0 ? null : context.getText(prefill), allowEmptyInput, callback);
  925. }
  926. public Builder input(@StringRes int hint, @StringRes int prefill, @NonNull InputCallback callback) {
  927. return input(hint, prefill, true, callback);
  928. }
  929. public Builder inputType(int type) {
  930. this.inputType = type;
  931. return this;
  932. }
  933. public Builder inputMaxLength(int maxLength) {
  934. return inputMaxLength(maxLength, 0);
  935. }
  936. /**
  937. * @param errorColor Pass in 0 for the default red error color (as specified in guidelines).
  938. */
  939. public Builder inputMaxLength(int maxLength, int errorColor) {
  940. if (maxLength < 1)
  941. throw new IllegalArgumentException("Max length for input dialogs cannot be less than 1.");
  942. this.inputMaxLength = maxLength;
  943. if (errorColor == 0) {
  944. inputMaxLengthErrorColor = context.getResources().getColor(R.color.md_edittext_error);
  945. } else {
  946. this.inputMaxLengthErrorColor = errorColor;
  947. }
  948. return this;
  949. }
  950. /**
  951. * Same as #{@link #inputMaxLength(int, int)}, but it takes a color resource ID for the error color.
  952. */
  953. public Builder inputMaxLengthRes(int maxLength, @ColorRes int errorColor) {
  954. return inputMaxLength(maxLength, context.getResources().getColor(errorColor));
  955. }
  956. public Builder alwaysCallInputCallback() {
  957. this.alwaysCallInputCallback = true;
  958. return this;
  959. }
  960. public MaterialDialog build() {
  961. return new MaterialDialog(this);
  962. }
  963. public MaterialDialog show() {
  964. MaterialDialog dialog = build();
  965. dialog.show();
  966. return dialog;
  967. }
  968. }
  969. @Override
  970. public void show() {
  971. if (Looper.myLooper() != Looper.getMainLooper())
  972. throw new IllegalStateException("Dialogs can only be shown from the UI thread.");
  973. try {
  974. super.show();
  975. } catch (WindowManager.BadTokenException e) {
  976. throw new DialogException("Bad window token, you cannot show a dialog before an Activity is created or after it's hidden.");
  977. }
  978. }
  979. /**
  980. * Retrieves the view of an action button, allowing you to modify properties such as whether or not it's enabled.
  981. * Use {@link #setActionButton(DialogAction, int)} to change text, since the view returned here is not
  982. * the view that displays text.
  983. *
  984. * @param which The action button of which to get the view for.
  985. * @return The view from the dialog's layout representing this action button.
  986. */
  987. public final View getActionButton(@NonNull DialogAction which) {
  988. switch (which) {
  989. default:
  990. return view.findViewById(R.id.buttonDefaultPositive);
  991. case NEUTRAL:
  992. return view.findViewById(R.id.buttonDefaultNeutral);
  993. case NEGATIVE:
  994. return view.findViewById(R.id.buttonDefaultNegative);
  995. }
  996. }
  997. /*
  998. *//**
  999. * This will not return buttons that are actually in the layout itself, since the layout doesn't
  1000. * contain buttons. This is only implemented to avoid crashing issues on Huawei devices. Huawei's
  1001. * stock OS requires this method in order to detect visible buttons.
  1002. *
  1003. * @deprecated Use getActionButton(com.afollestad.materialdialogs.DialogAction)} instead.
  1004. *//*
  1005. @Deprecated
  1006. @Override
  1007. public Button getButton(int whichButton) {
  1008. Log.w("MaterialDialog", "Warning: getButton() is a deprecated method that does not return valid references to action buttons.");
  1009. if (whichButton == AlertDialog.BUTTON_POSITIVE) {
  1010. return mBuilder.positiveText != null ? new Button(getContext()) : null;
  1011. } else if (whichButton == AlertDialog.BUTTON_NEUTRAL) {
  1012. return mBuilder.neutralText != null ? new Button(getContext()) : null;
  1013. } else {
  1014. return mBuilder.negativeText != null ? new Button(getContext()) : null;
  1015. }
  1016. }*/
  1017. /**
  1018. * Retrieves the view representing the dialog as a whole. Be careful with this.
  1019. */
  1020. public final View getView() {
  1021. return view;
  1022. }
  1023. @Nullable
  1024. public final ListView getListView() {
  1025. return listView;
  1026. }
  1027. @Nullable
  1028. public final EditText getInputEditText() {
  1029. return input;
  1030. }
  1031. /**
  1032. * Retrieves the TextView that contains the dialog title. If you want to update the
  1033. * title, use #{@link #setTitle(CharSequence)} instead.
  1034. */
  1035. public final TextView getTitleView() {
  1036. return title;
  1037. }
  1038. /**
  1039. * Retrieves the TextView that contains the dialog content. If you want to update the
  1040. * content (message), use #{@link #setContent(CharSequence)} instead.
  1041. */
  1042. @Nullable
  1043. public final TextView getContentView() {
  1044. return content;
  1045. }
  1046. /**
  1047. * Retrieves the custom view that was inflated or set to the MaterialDialog during building.
  1048. *
  1049. * @return The custom view that was passed into the Builder.
  1050. */
  1051. @Nullable
  1052. public final View getCustomView() {
  1053. return mBuilder.customView;
  1054. }
  1055. /**
  1056. * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
  1057. * Setting an action button's text to null is a shortcut for hiding it, too.
  1058. *
  1059. * @param which The action button to update.
  1060. * @param title The new title of the action button.
  1061. */
  1062. public final void setActionButton(@NonNull DialogAction which, CharSequence title) {
  1063. switch (which) {
  1064. default:
  1065. mBuilder.positiveText = title;
  1066. positiveButton.setText(title);
  1067. positiveButton.setVisibility(title == null ? View.GONE : View.VISIBLE);
  1068. break;
  1069. case NEUTRAL:
  1070. mBuilder.neutralText = title;
  1071. neutralButton.setText(title);
  1072. neutralButton.setVisibility(title == null ? View.GONE : View.VISIBLE);
  1073. break;
  1074. case NEGATIVE:
  1075. mBuilder.negativeText = title;
  1076. negativeButton.setText(title);
  1077. negativeButton.setVisibility(title == null ? View.GONE : View.VISIBLE);
  1078. break;
  1079. }
  1080. }
  1081. /**
  1082. * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
  1083. *
  1084. * @param which The action button to update.
  1085. * @param titleRes The string resource of the new title of the action button.
  1086. */
  1087. public final void setActionButton(DialogAction which, @StringRes int titleRes) {
  1088. setActionButton(which, getContext().getText(titleRes));
  1089. }
  1090. /**
  1091. * Gets whether or not the positive, neutral, or negative action button is visible.
  1092. *
  1093. * @return Whether or not 1 or more action buttons is visible.
  1094. */
  1095. public final boolean hasActionButtons() {
  1096. return numberOfActionButtons() > 0;
  1097. }
  1098. /**
  1099. * Gets the number of visible action buttons.
  1100. *
  1101. * @return 0 through 3, depending on how many should be or are visible.
  1102. */
  1103. public final int numberOfActionButtons() {
  1104. int number = 0;
  1105. if (mBuilder.positiveText != null && positiveButton.getVisibility() == View.VISIBLE)
  1106. number++;
  1107. if (mBuilder.neutralText != null && neutralButton.getVisibility() == View.VISIBLE)
  1108. number++;
  1109. if (mBuilder.negativeText != null && negativeButton.getVisibility() == View.VISIBLE)
  1110. number++;
  1111. return number;
  1112. }
  1113. /**
  1114. * Updates the dialog's title.
  1115. */
  1116. public final void setTitle(@NonNull CharSequence title) {
  1117. this.title.setText(title);
  1118. }
  1119. public void setIcon(@DrawableRes int resId) {
  1120. icon.setImageResource(resId);
  1121. icon.setVisibility(resId != 0 ? View.VISIBLE : View.GONE);
  1122. }
  1123. public void setIcon(Drawable d) {
  1124. icon.setImageDrawable(d);
  1125. icon.setVisibility(d != null ? View.VISIBLE : View.GONE);
  1126. }
  1127. public void setIconAttribute(@AttrRes int attrId) {
  1128. Drawable d = DialogUtils.resolveDrawable(mBuilder.context, attrId);
  1129. icon.setImageDrawable(d);
  1130. icon.setVisibility(d != null ? View.VISIBLE : View.GONE);
  1131. }
  1132. public final void setContent(CharSequence content) {
  1133. this.content.setText(content);
  1134. this.content.setVisibility(TextUtils.isEmpty(content) ? View.GONE : View.VISIBLE);
  1135. }
  1136. /**
  1137. * @deprecated Use setContent() instead.
  1138. */
  1139. @Deprecated
  1140. public void setMessage(CharSequence message) {
  1141. setContent(message);
  1142. }
  1143. public final void setItems(CharSequence[] items) {
  1144. if (mBuilder.adapter == null)
  1145. throw new IllegalStateException("This MaterialDialog instance does not yet have an adapter set to it. You cannot use setItems().");
  1146. if (mBuilder.adapter instanceof MaterialDialogAdapter) {
  1147. mBuilder.adapter = new MaterialDialogAdapter(this,
  1148. ListType.getLayoutForType(listType), R.id.title, items);
  1149. } else {
  1150. throw new IllegalStateException("When using a custom adapter, setItems() cannot be used. Set items through the adapter instead.");
  1151. }
  1152. mBuilder.items = items;
  1153. listView.setAdapter(mBuilder.adapter);
  1154. }
  1155. public final int getCurrentProgress() {
  1156. if (mProgress == null) return -1;
  1157. return mProgress.getProgress();
  1158. }
  1159. public final void incrementProgress(int by) {
  1160. if (mBuilder.progress <= -2)
  1161. throw new IllegalStateException("Cannot use incrementProgress() on this dialog.");
  1162. setProgress(getCurrentProgress() + by);
  1163. }
  1164. public final void setProgress(int progress) {
  1165. if (Looper.myLooper() != Looper.getMainLooper())
  1166. throw new IllegalStateException("You can only set the dialog's progress from the UI thread.");
  1167. else if (mBuilder.progress <= -2)
  1168. throw new IllegalStateException("Cannot use setProgress() on this dialog.");
  1169. mProgress.setProgress(progress);
  1170. int percentage = (int) (((float) getCurrentProgress() / (float) getMaxProgress()) * 100f);
  1171. mProgressLabel.setText(percentage + "%");
  1172. if (mProgressMinMax != null)
  1173. mProgressMinMax.setText(getCurrentProgress() + "/" + getMaxProgress());
  1174. }
  1175. public final void setMaxProgress(int max) {
  1176. if (Looper.myLooper() != Looper.getMainLooper())
  1177. throw new IllegalStateException("You can only set the dialog's progress from the UI thread.");
  1178. else if (mBuilder.progress <= -2)
  1179. throw new IllegalStateException("Cannot use setMaxProgress() on this dialog.");
  1180. mProgress.setMax(max);
  1181. }
  1182. public final boolean isIndeterminateProgress() {
  1183. return mBuilder.indeterminateProgress;
  1184. }
  1185. public final int getMaxProgress() {
  1186. if (mProgress == null) return -1;
  1187. return mProgress.getMax();
  1188. }
  1189. public final boolean isCancelled() {
  1190. return !isShowing();
  1191. }
  1192. /**
  1193. * Convenience method for getting the currently selected index of a single choice list.
  1194. *
  1195. * @return Currently selected index of a single choice list, or -1 if not showing a single choice list
  1196. */
  1197. public int getSelectedIndex() {
  1198. if (mBuilder.listCallbackSingleChoice != null) {
  1199. return mBuilder.selectedIndex;
  1200. } else {
  1201. return -1;
  1202. }
  1203. }
  1204. /**
  1205. * Convenience method for getting the currently selected indices of a multi choice list
  1206. *
  1207. * @return Currently selected index of a multi choice list, or null if not showing a multi choice list
  1208. */
  1209. @Nullable
  1210. public Integer[] getSelectedIndices() {
  1211. if (mBuilder.listCallbackMultiChoice != null) {
  1212. return selectedIndicesList.toArray(new Integer[selectedIndicesList.size()]);
  1213. } else {
  1214. return null;
  1215. }
  1216. }
  1217. /**
  1218. * Convenience method for setting the currently selected index of a single choice list.
  1219. * This only works if you are not using a custom adapter; if you're using a custom adapter,
  1220. * an IllegalStateException is thrown. Note that this does not call the respective single choice callback.
  1221. *
  1222. * @param index The index of the list item to check.
  1223. */
  1224. public void setSelectedIndex(int index) {
  1225. mBuilder.selectedIndex = index;
  1226. if (mBuilder.adapter != null && mBuilder.adapter instanceof MaterialDialogAdapter) {
  1227. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  1228. } else {
  1229. throw new IllegalStateException("You can only use setSelectedIndex() with the default adapter implementation.");
  1230. }
  1231. }
  1232. /**
  1233. * Convenience method for setting the currently selected indices of a multi choice list.
  1234. * This only works if you are not using a custom adapter; if you're using a custom adapter,
  1235. * an IllegalStateException is thrown. Note that this does not call the respective multi choice callback.
  1236. *
  1237. * @param indices The indices of the list items to check.
  1238. */
  1239. public void setSelectedIndices(@NonNull Integer[] indices) {
  1240. mBuilder.selectedIndices = indices;
  1241. selectedIndicesList = new ArrayList<>(Arrays.asList(indices));
  1242. if (mBuilder.adapter != null && mBuilder.adapter instanceof MaterialDialogAdapter) {
  1243. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  1244. } else {
  1245. throw new IllegalStateException("You can only use setSelectedIndices() with the default adapter implementation.");
  1246. }
  1247. }
  1248. @Override
  1249. public final void onShow(DialogInterface dialog) {
  1250. if (input != null) {
  1251. DialogUtils.showKeyboard(this, mBuilder);
  1252. if (input.getText().length() > 0)
  1253. input.setSelection(input.getText().length());
  1254. }
  1255. super.onShow(dialog);
  1256. }
  1257. protected void setInternalInputCallback() {
  1258. if (input == null) return;
  1259. input.addTextChangedListener(new TextWatcher() {
  1260. @Override
  1261. public void beforeTextChanged(CharSequence s, int start, int count, int after) {
  1262. }
  1263. @Override
  1264. public void onTextChanged(CharSequence s, int start, int before, int count) {
  1265. if (mBuilder.alwaysCallInputCallback)
  1266. mBuilder.inputCallback.onInput(MaterialDialog.this, s);
  1267. final int length = s.toString().trim().length();
  1268. if (!mBuilder.inputAllowEmpty) {
  1269. final View positiveAb = getActionButton(DialogAction.POSITIVE);
  1270. positiveAb.setEnabled(length > 0);
  1271. }
  1272. invalidateInputMinMaxIndicator(length);
  1273. }
  1274. @Override
  1275. public void afterTextChanged(Editable s) {
  1276. }
  1277. });
  1278. }
  1279. protected void invalidateInputMinMaxIndicator(int currentLength) {
  1280. if (inputMinMax != null) {
  1281. inputMinMax.setText(currentLength + "/" + mBuilder.inputMaxLength);
  1282. final boolean overMax = currentLength > mBuilder.inputMaxLength;
  1283. final int colorText = overMax ? mBuilder.inputMaxLengthErrorColor : mBuilder.contentColor;
  1284. final int colorWidget = overMax ? mBuilder.inputMaxLengthErrorColor : mBuilder.widgetColor;
  1285. inputMinMax.setTextColor(colorText);
  1286. MDTintHelper.setTint(input, colorWidget);
  1287. final View positiveAb = getActionButton(DialogAction.POSITIVE);
  1288. if (positiveAb.isEnabled()) positiveAb.setEnabled(!overMax);
  1289. }
  1290. }
  1291. @Override
  1292. protected void onStop() {
  1293. super.onStop();
  1294. if (input != null)
  1295. DialogUtils.hideKeyboard(this, mBuilder);
  1296. }
  1297. protected enum ListType {
  1298. REGULAR, SINGLE, MULTI;
  1299. public static int getLayoutForType(ListType type) {
  1300. switch (type) {
  1301. case REGULAR:
  1302. return R.layout.md_listitem;
  1303. case SINGLE:
  1304. return R.layout.md_listitem_singlechoice;
  1305. case MULTI:
  1306. return R.layout.md_listitem_multichoice;
  1307. default:
  1308. throw new IllegalArgumentException("Not a valid list type");
  1309. }
  1310. }
  1311. }
  1312. /**
  1313. * A callback used for regular list dialogs.
  1314. */
  1315. public interface ListCallback {
  1316. void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text);
  1317. }
  1318. /**
  1319. * A callback used for multi choice (check box) list dialogs.
  1320. */
  1321. public interface ListCallbackSingleChoice {
  1322. /**
  1323. * Return true to allow the radio button to be checked, if the alwaysCallSingleChoice() option is used.
  1324. *
  1325. * @param dialog The dialog of which a list item was selected.
  1326. * @param which The index of the item that was selected.
  1327. * @param text The text of the item that was selected.
  1328. * @return True to allow the radio button to be selected.
  1329. */
  1330. boolean onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text);
  1331. }
  1332. /**
  1333. * A callback used for multi choice (check box) list dialogs.
  1334. */
  1335. public interface ListCallbackMultiChoice {
  1336. /**
  1337. * Return true to allow the check box to be checked, if the alwaysCallSingleChoice() option is used.
  1338. *
  1339. * @param dialog The dialog of which a list item was selected.
  1340. * @param which The indices of the items that were selected.
  1341. * @param text The text of the items that were selected.
  1342. * @return True to allow the checkbox to be selected.
  1343. */
  1344. boolean onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text);
  1345. }
  1346. /**
  1347. * Override these as needed, so no needing to sub empty methods from an interface
  1348. */
  1349. public static abstract class ButtonCallback {
  1350. public void onPositive(MaterialDialog dialog) {
  1351. }
  1352. public void onNegative(MaterialDialog dialog) {
  1353. }
  1354. public void onNeutral(MaterialDialog dialog) {
  1355. }
  1356. public ButtonCallback() {
  1357. super();
  1358. }
  1359. @Override
  1360. protected final Object clone() throws CloneNotSupportedException {
  1361. return super.clone();
  1362. }
  1363. @Override
  1364. public final boolean equals(Object o) {
  1365. return super.equals(o);
  1366. }
  1367. @Override
  1368. protected final void finalize() throws Throwable {
  1369. super.finalize();
  1370. }
  1371. @Override
  1372. public final int hashCode() {
  1373. return super.hashCode();
  1374. }
  1375. @Override
  1376. public final String toString() {
  1377. return super.toString();
  1378. }
  1379. }
  1380. public interface InputCallback {
  1381. void onInput(MaterialDialog dialog, CharSequence input);
  1382. }
  1383. }