MaterialDialog.java 69 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773
  1. package com.afollestad.materialdialogs;
  2. import android.annotation.SuppressLint;
  3. import android.annotation.TargetApi;
  4. import android.app.AlertDialog;
  5. import android.content.Context;
  6. import android.content.DialogInterface;
  7. import android.content.res.ColorStateList;
  8. import android.content.res.Resources;
  9. import android.content.res.TypedArray;
  10. import android.graphics.Color;
  11. import android.graphics.Rect;
  12. import android.graphics.Typeface;
  13. import android.graphics.drawable.Drawable;
  14. import android.os.Build;
  15. import android.os.Looper;
  16. import android.support.annotation.ArrayRes;
  17. import android.support.annotation.AttrRes;
  18. import android.support.annotation.ColorRes;
  19. import android.support.annotation.DrawableRes;
  20. import android.support.annotation.LayoutRes;
  21. import android.support.annotation.NonNull;
  22. import android.support.annotation.Nullable;
  23. import android.support.annotation.StringRes;
  24. import android.text.method.LinkMovementMethod;
  25. import android.view.ContextThemeWrapper;
  26. import android.view.Gravity;
  27. import android.view.LayoutInflater;
  28. import android.view.View;
  29. import android.view.ViewGroup;
  30. import android.view.ViewTreeObserver;
  31. import android.webkit.WebView;
  32. import android.widget.AdapterView;
  33. import android.widget.ArrayAdapter;
  34. import android.widget.Button;
  35. import android.widget.CheckBox;
  36. import android.widget.FrameLayout;
  37. import android.widget.ImageView;
  38. import android.widget.LinearLayout;
  39. import android.widget.ListAdapter;
  40. import android.widget.ListView;
  41. import android.widget.RadioButton;
  42. import android.widget.RelativeLayout;
  43. import android.widget.ScrollView;
  44. import android.widget.TextView;
  45. import com.afollestad.materialdialogs.base.DialogBase;
  46. import com.afollestad.materialdialogs.util.DialogUtils;
  47. import com.afollestad.materialdialogs.util.RecyclerUtil;
  48. import com.afollestad.materialdialogs.util.TypefaceHelper;
  49. import java.util.ArrayList;
  50. import java.util.Arrays;
  51. import java.util.List;
  52. /**
  53. * @author Aidan Follestad (afollestad)
  54. */
  55. public class MaterialDialog extends DialogBase implements View.OnClickListener {
  56. protected final View view;
  57. protected final Builder mBuilder;
  58. protected ListView listView;
  59. protected ImageView icon;
  60. protected TextView title;
  61. protected View titleFrame;
  62. protected FrameLayout customViewFrame;
  63. protected View positiveButton;
  64. protected View neutralButton;
  65. protected View negativeButton;
  66. protected boolean isStacked;
  67. protected final int defaultItemColor;
  68. protected ListType listType;
  69. protected List<Integer> selectedIndicesList;
  70. private static ContextThemeWrapper getTheme(Builder builder) {
  71. TypedArray a = builder.context.getTheme().obtainStyledAttributes(new int[]{R.attr.md_dark_theme});
  72. boolean darkTheme = builder.theme == Theme.DARK;
  73. if (!darkTheme) {
  74. try {
  75. darkTheme = a.getBoolean(0, false);
  76. builder.theme = darkTheme ? Theme.DARK : Theme.LIGHT;
  77. } finally {
  78. a.recycle();
  79. }
  80. }
  81. return new ContextThemeWrapper(builder.context, darkTheme ? R.style.MD_Dark : R.style.MD_Light);
  82. }
  83. @SuppressLint("InflateParams")
  84. protected MaterialDialog(Builder builder) {
  85. super(getTheme(builder));
  86. mBuilder = builder;
  87. if (mBuilder.regularFont == null)
  88. mBuilder.regularFont = TypefaceHelper.get(getContext(), "Roboto-Regular");
  89. if (mBuilder.mediumFont == null)
  90. mBuilder.mediumFont = TypefaceHelper.get(getContext(), "Roboto-Medium");
  91. this.view = LayoutInflater.from(getContext()).inflate(R.layout.md_dialog, null);
  92. this.setCancelable(builder.cancelable);
  93. if (mBuilder.backgroundColor == 0)
  94. mBuilder.backgroundColor = DialogUtils.resolveColor(mBuilder.context, R.attr.md_background_color);
  95. if (mBuilder.backgroundColor != 0)
  96. this.view.setBackgroundColor(mBuilder.backgroundColor);
  97. mBuilder.positiveColor = DialogUtils.resolveColor(mBuilder.context, R.attr.md_positive_color, mBuilder.positiveColor);
  98. mBuilder.neutralColor = DialogUtils.resolveColor(mBuilder.context, R.attr.md_negative_color, mBuilder.neutralColor);
  99. mBuilder.negativeColor = DialogUtils.resolveColor(mBuilder.context, R.attr.md_neutral_color, mBuilder.negativeColor);
  100. title = (TextView) view.findViewById(R.id.title);
  101. icon = (ImageView) view.findViewById(R.id.icon);
  102. titleFrame = view.findViewById(R.id.titleFrame);
  103. final TextView content = (TextView) view.findViewById(R.id.content);
  104. content.setText(builder.content);
  105. content.setMovementMethod(new LinkMovementMethod());
  106. setTypeface(content, mBuilder.regularFont);
  107. content.setLineSpacing(0f, builder.contentLineSpacingMultiplier);
  108. if (mBuilder.positiveColor == 0) {
  109. content.setLinkTextColor(DialogUtils.resolveColor(getContext(), android.R.attr.textColorPrimary));
  110. } else {
  111. content.setLinkTextColor(mBuilder.positiveColor);
  112. }
  113. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  114. //noinspection ResourceType
  115. title.setTextAlignment(gravityToAlignment(builder.titleGravity));
  116. } else {
  117. title.setGravity(gravityIntToGravity(builder.titleGravity));
  118. }
  119. if (builder.contentColor != -1) {
  120. content.setTextColor(builder.contentColor);
  121. } else {
  122. final int fallback = DialogUtils.resolveColor(getContext(), android.R.attr.textColorSecondary);
  123. final int contentColor = DialogUtils.resolveColor(getContext(), R.attr.md_content_color, fallback);
  124. content.setTextColor(contentColor);
  125. }
  126. if (builder.itemColor != 0) {
  127. defaultItemColor = builder.itemColor;
  128. } else if (builder.theme == Theme.LIGHT) {
  129. defaultItemColor = Color.BLACK;
  130. } else {
  131. defaultItemColor = Color.WHITE;
  132. }
  133. if (mBuilder.customView != null) {
  134. invalidateCustomViewAssociations();
  135. FrameLayout frame = (FrameLayout) view.findViewById(R.id.customViewFrame);
  136. customViewFrame = frame;
  137. View innerView = mBuilder.customView;
  138. if (mBuilder.wrapCustomViewInScroll) {
  139. /* Apply the frame padding to the content, this allows the ScrollView to draw it's
  140. overscroll glow without clipping */
  141. final Resources r = getContext().getResources();
  142. final int frameMargin = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
  143. final ScrollView sv = new ScrollView(getContext());
  144. int paddingTop;
  145. int paddingBottom;
  146. if (titleFrame.getVisibility() != View.GONE)
  147. paddingTop = r.getDimensionPixelSize(R.dimen.md_content_vertical_padding);
  148. else
  149. paddingTop = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
  150. if (hasActionButtons())
  151. paddingBottom = r.getDimensionPixelSize(R.dimen.md_content_vertical_padding);
  152. else
  153. paddingBottom = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
  154. sv.setPadding(0, paddingTop, 0, paddingBottom);
  155. sv.setClipToPadding(false);
  156. ScrollView.LayoutParams innerViewLayoutParams=
  157. new ScrollView.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  158. ViewGroup.LayoutParams.WRAP_CONTENT);
  159. innerViewLayoutParams.setMargins(frameMargin, 0, frameMargin, 0);
  160. sv.addView(innerView, innerViewLayoutParams);
  161. innerView = sv;
  162. }
  163. frame.addView(innerView, new ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT,
  164. ViewGroup.LayoutParams.WRAP_CONTENT));
  165. } else {
  166. invalidateCustomViewAssociations();
  167. }
  168. boolean adapterProvided = mBuilder.adapter != null;
  169. if (mBuilder.items != null && mBuilder.items.length > 0 || adapterProvided) {
  170. listView = (ListView) view.findViewById(R.id.contentListView);
  171. listView.setSelector(getListSelector());
  172. if (!adapterProvided) {
  173. // Determine list type
  174. if (mBuilder.listCallbackSingle != null) {
  175. listType = ListType.SINGLE;
  176. } else if (mBuilder.listCallbackMulti != null) {
  177. listType = ListType.MULTI;
  178. if (mBuilder.selectedIndices != null) {
  179. selectedIndicesList = new ArrayList<>(Arrays.asList(mBuilder.selectedIndices));
  180. } else {
  181. selectedIndicesList = new ArrayList<>();
  182. }
  183. } else {
  184. listType = ListType.REGULAR;
  185. }
  186. mBuilder.adapter = new MaterialDialogAdapter(mBuilder.context,
  187. ListType.getLayoutForType(listType), R.id.title, mBuilder.items);
  188. }
  189. }
  190. if (builder.icon != null) {
  191. icon.setVisibility(View.VISIBLE);
  192. icon.setImageDrawable(builder.icon);
  193. } else {
  194. Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_icon);
  195. if (d != null) {
  196. icon.setVisibility(View.VISIBLE);
  197. icon.setImageDrawable(d);
  198. } else {
  199. icon.setVisibility(View.GONE);
  200. }
  201. }
  202. if (builder.title == null || builder.title.toString().trim().length() == 0) {
  203. titleFrame.setVisibility(View.GONE);
  204. } else {
  205. title.setText(builder.title);
  206. setTypeface(title, mBuilder.mediumFont);
  207. if (builder.titleColor != -1) {
  208. title.setTextColor(builder.titleColor);
  209. } else {
  210. final int fallback = DialogUtils.resolveColor(getContext(), android.R.attr.textColorPrimary);
  211. title.setTextColor(DialogUtils.resolveColor(getContext(), R.attr.md_title_color, fallback));
  212. }
  213. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  214. //noinspection ResourceType
  215. content.setTextAlignment(gravityToAlignment(builder.contentGravity));
  216. } else {
  217. content.setGravity(gravityIntToGravity(builder.contentGravity));
  218. }
  219. }
  220. if (builder.showListener != null) {
  221. setOnShowListener(builder.showListener);
  222. }
  223. if (builder.cancelListener != null) {
  224. setOnCancelListener(builder.cancelListener);
  225. }
  226. if (builder.dismissListener != null) {
  227. setOnDismissListener(builder.dismissListener);
  228. }
  229. if (builder.keyListener != null) {
  230. setOnKeyListener(builder.keyListener);
  231. }
  232. updateFramePadding();
  233. invalidateActions();
  234. setOnShowListenerInternal();
  235. setViewInternal(view);
  236. view.getViewTreeObserver().addOnGlobalLayoutListener(
  237. new ViewTreeObserver.OnGlobalLayoutListener() {
  238. @Override
  239. public void onGlobalLayout() {
  240. if (view.getMeasuredWidth() > 0) {
  241. invalidateCustomViewAssociations();
  242. }
  243. }
  244. });
  245. if (builder.theme == Theme.LIGHT && Build.VERSION.SDK_INT <= Build.VERSION_CODES.GINGERBREAD_MR1) {
  246. setInverseBackgroundForced(true);
  247. title.setTextColor(Color.BLACK);
  248. content.setTextColor(Color.BLACK);
  249. }
  250. }
  251. private static int gravityIntToGravity(GravityEnum gravity) {
  252. switch (gravity) {
  253. case CENTER:
  254. return Gravity.CENTER_HORIZONTAL;
  255. case END:
  256. return Gravity.END;
  257. default:
  258. return Gravity.START;
  259. }
  260. }
  261. @TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
  262. private static int gravityToAlignment(GravityEnum gravity) {
  263. switch (gravity) {
  264. case CENTER:
  265. return View.TEXT_ALIGNMENT_CENTER;
  266. case END:
  267. return View.TEXT_ALIGNMENT_VIEW_END;
  268. default:
  269. return View.TEXT_ALIGNMENT_VIEW_START;
  270. }
  271. }
  272. @Override
  273. public void onShow(DialogInterface dialog) {
  274. super.onShow(dialog); // calls any external show listeners
  275. checkIfStackingNeeded();
  276. invalidateCustomViewAssociations();
  277. }
  278. /**
  279. * To account for scrolling content and overscroll glows, the frame padding/margins sometimes
  280. * must be set on inner views. This is dependent on the visibility of the title bar and action
  281. * buttons. This method determines where the padding or margins are needed and applies them.
  282. */
  283. private void updateFramePadding() {
  284. Resources r = getContext().getResources();
  285. int frameMargin = r.getDimensionPixelSize(R.dimen.md_dialog_frame_margin);
  286. View contentScrollView = view.findViewById(R.id.contentScrollView);
  287. int paddingTop = contentScrollView.getPaddingTop();
  288. int paddingBottom = contentScrollView.getPaddingBottom();
  289. if (!hasActionButtons())
  290. paddingBottom = frameMargin;
  291. if (titleFrame.getVisibility() == View.GONE)
  292. paddingTop = frameMargin;
  293. contentScrollView.setPadding(contentScrollView.getPaddingLeft(), paddingTop,
  294. contentScrollView.getPaddingRight(), paddingBottom);
  295. if (listView != null) {
  296. // Padding below title is reduced for divider.
  297. final int titlePaddingBottom = (int) mBuilder.context.getResources().getDimension(R.dimen.md_title_frame_margin_bottom_list);
  298. titleFrame.setPadding(titleFrame.getPaddingLeft(),
  299. titleFrame.getPaddingTop(),
  300. titleFrame.getPaddingRight(),
  301. titlePaddingBottom);
  302. }
  303. }
  304. /**
  305. * Invalidates visibility of views for the presence of a custom view or list content
  306. */
  307. private void invalidateCustomViewAssociations() {
  308. if (view.getMeasuredWidth() == 0) {
  309. return;
  310. }
  311. View contentScrollView = view.findViewById(R.id.contentScrollView);
  312. TextView content = (TextView) view.findViewById(R.id.content);
  313. final int contentHorizontalPadding = (int) mBuilder.context.getResources()
  314. .getDimension(R.dimen.md_dialog_frame_margin);
  315. content.setPadding(contentHorizontalPadding, 0, contentHorizontalPadding, 0);
  316. if (mBuilder.customView != null) {
  317. contentScrollView.setVisibility(View.GONE);
  318. customViewFrame.setVisibility(View.VISIBLE);
  319. boolean topScroll = canViewOrChildScroll(customViewFrame.getChildAt(0), false);
  320. boolean bottomScroll = canViewOrChildScroll(customViewFrame.getChildAt(0), true);
  321. setDividerVisibility(topScroll, bottomScroll);
  322. } else if ((mBuilder.items != null && mBuilder.items.length > 0) || mBuilder.adapter != null) {
  323. contentScrollView.setVisibility(View.GONE);
  324. boolean canScroll = titleFrame.getVisibility() == View.VISIBLE && canListViewScroll();
  325. setDividerVisibility(canScroll, canScroll);
  326. } else {
  327. contentScrollView.setVisibility(View.VISIBLE);
  328. boolean canScroll = canContentScroll();
  329. if (canScroll) {
  330. final int contentVerticalPadding = (int) mBuilder.context.getResources()
  331. .getDimension(R.dimen.md_title_frame_margin_bottom);
  332. content.setPadding(contentHorizontalPadding, contentVerticalPadding,
  333. contentHorizontalPadding, contentVerticalPadding);
  334. // Same effect as when there's a ListView. Padding below title is reduced for divider.
  335. final int titlePaddingBottom = (int) mBuilder.context.getResources().getDimension(R.dimen.md_title_frame_margin_bottom_list);
  336. titleFrame.setPadding(titleFrame.getPaddingLeft(),
  337. titleFrame.getPaddingTop(),
  338. titleFrame.getPaddingRight(),
  339. titlePaddingBottom);
  340. }
  341. setDividerVisibility(canScroll, canScroll);
  342. }
  343. }
  344. /**
  345. * Set the visibility of the bottom divider and adjusts the layout margin,
  346. * when the divider is visible the button bar bottom margin (8dp from
  347. * http://www.google.com/design/spec/components/dialogs.html#dialogs-specs )
  348. * is removed as it makes the button bar look off balanced with different amounts of padding
  349. * above and below the divider.
  350. */
  351. private void setDividerVisibility(boolean topVisible, boolean bottomVisible) {
  352. topVisible = topVisible && titleFrame.getVisibility() == View.VISIBLE;
  353. bottomVisible = bottomVisible && hasActionButtons();
  354. if (mBuilder.dividerColor == 0)
  355. mBuilder.dividerColor = DialogUtils.resolveColor(mBuilder.context, R.attr.md_divider_color);
  356. if (mBuilder.dividerColor == 0)
  357. mBuilder.dividerColor = DialogUtils.resolveColor(getContext(), R.attr.md_divider);
  358. View titleBarDivider = view.findViewById(R.id.titleBarDivider);
  359. if (topVisible) {
  360. titleBarDivider.setVisibility(View.VISIBLE);
  361. titleBarDivider.setBackgroundColor(mBuilder.dividerColor);
  362. } else {
  363. titleBarDivider.setVisibility(View.GONE);
  364. }
  365. View buttonBarDivider = view.findViewById(R.id.buttonBarDivider);
  366. if (bottomVisible) {
  367. buttonBarDivider.setVisibility(View.VISIBLE);
  368. buttonBarDivider.setBackgroundColor(mBuilder.dividerColor);
  369. setVerticalMargins(view.findViewById(R.id.buttonStackedFrame), 0, 0);
  370. setVerticalMargins(view.findViewById(R.id.buttonDefaultFrame), 0, 0);
  371. } else {
  372. Resources r = getContext().getResources();
  373. buttonBarDivider.setVisibility(View.GONE);
  374. final int bottomMargin = r.getDimensionPixelSize(R.dimen.md_button_frame_vertical_padding);
  375. /* Only enable the bottom margin if our available window space can hold the margin,
  376. we don't want to enable this and cause the content to scroll, which is bad
  377. experience itself but it also causes a vibrating window as this will keep getting
  378. enabled/disabled over and over again.
  379. */
  380. Rect maxWindowFrame = new Rect();
  381. getWindow().getDecorView().getWindowVisibleDisplayFrame(maxWindowFrame);
  382. int currentHeight = getWindow().getDecorView().getMeasuredHeight();
  383. if (currentHeight + bottomMargin < maxWindowFrame.height()) {
  384. setVerticalMargins(view.findViewById(R.id.buttonStackedFrame),
  385. bottomMargin, bottomMargin);
  386. setVerticalMargins(view.findViewById(R.id.buttonDefaultFrame),
  387. bottomMargin, bottomMargin);
  388. }
  389. }
  390. }
  391. /**
  392. * Constructs the dialog's list content and sets up click listeners.
  393. */
  394. private void invalidateList() {
  395. if ((mBuilder.items == null || mBuilder.items.length == 0) && mBuilder.adapter == null)
  396. return;
  397. // Hide content
  398. view.findViewById(R.id.contentScrollView).setVisibility(View.GONE);
  399. view.findViewById(R.id.customViewFrame).setVisibility(View.GONE);
  400. // Set up list with adapter
  401. FrameLayout listViewContainer = (FrameLayout) view.findViewById(R.id.contentListViewFrame);
  402. listViewContainer.setVisibility(View.VISIBLE);
  403. listView.setAdapter(mBuilder.adapter);
  404. if (listType != null) {
  405. // Only set listener for 1st-party adapter, leave custom adapter implementation to user with getListView()
  406. listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
  407. @Override
  408. public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
  409. if (listType == ListType.MULTI) {
  410. // Keep our selected items up to date
  411. boolean isChecked = !((CheckBox) view.findViewById(R.id.control)).isChecked(); // Inverted because the view's click listener is called before the check is toggled
  412. boolean previouslySelected = selectedIndicesList.contains(position);
  413. if (isChecked) {
  414. if (!previouslySelected) {
  415. selectedIndicesList.add(position);
  416. }
  417. } else if (previouslySelected) {
  418. selectedIndicesList.remove(Integer.valueOf(position));
  419. }
  420. } else if (listType == ListType.SINGLE) {
  421. // Keep our selected item up to date
  422. if (mBuilder.selectedIndex != position) {
  423. mBuilder.selectedIndex = position;
  424. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  425. }
  426. }
  427. onClick(view);
  428. }
  429. });
  430. }
  431. }
  432. /**
  433. * Find the view touching the bottom of this ViewGroup. Non visible children are ignored,
  434. * however getChildDrawingOrder is not taking into account for simplicity and because it behaves
  435. * inconsistently across platform versions.
  436. *
  437. * @return View touching the bottom of this viewgroup or null
  438. */
  439. @Nullable
  440. private static View getBottomView(ViewGroup viewGroup) {
  441. View bottomView = null;
  442. for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
  443. View child = viewGroup.getChildAt(i);
  444. if (child.getVisibility() == View.VISIBLE && child.getBottom() == viewGroup.getBottom()) {
  445. bottomView = child;
  446. break;
  447. }
  448. }
  449. return bottomView;
  450. }
  451. @Nullable
  452. private static View getTopView(ViewGroup viewGroup) {
  453. View topView = null;
  454. for (int i = viewGroup.getChildCount() - 1; i >= 0; i--) {
  455. View child = viewGroup.getChildAt(i);
  456. if (child.getVisibility() == View.VISIBLE && child.getTop() == viewGroup.getTop()) {
  457. topView = child;
  458. break;
  459. }
  460. }
  461. return topView;
  462. }
  463. private static boolean canViewOrChildScroll(View view, boolean atBottom) {
  464. if (view == null || !(view instanceof ViewGroup)) {
  465. return false;
  466. }
  467. /* Is the bottom view something that scrolls? */
  468. if (view instanceof ScrollView) {
  469. ScrollView sv = (ScrollView) view;
  470. if (sv.getChildCount() == 0)
  471. return false;
  472. final int childHeight = sv.getChildAt(0).getMeasuredHeight();
  473. return sv.getMeasuredHeight() < childHeight;
  474. } else if (view instanceof AdapterView) {
  475. return canAdapterViewScroll((AdapterView) view);
  476. } else if (view instanceof WebView) {
  477. return canWebViewScroll((WebView) view);
  478. } else if (isRecyclerView(view)) {
  479. return RecyclerUtil.canRecyclerViewScroll(view);
  480. } else {
  481. if (atBottom) {
  482. return canViewOrChildScroll(getBottomView((ViewGroup) view), true);
  483. } else {
  484. return canViewOrChildScroll(getTopView((ViewGroup) view), false);
  485. }
  486. }
  487. }
  488. private static boolean isRecyclerView(View view) {
  489. boolean isRecyclerView = false;
  490. try {
  491. Class.forName("android.support.v7.widget.RecyclerView");
  492. // We got here, so now we can safely check
  493. isRecyclerView = RecyclerUtil.isRecyclerView(view);
  494. } catch (ClassNotFoundException ignored) {
  495. }
  496. return isRecyclerView;
  497. }
  498. private static boolean canWebViewScroll(WebView view) {
  499. return view.getMeasuredHeight() > view.getContentHeight();
  500. }
  501. private static boolean canAdapterViewScroll(AdapterView lv) {
  502. /* Force it to layout it's children */
  503. if (lv.getLastVisiblePosition() == -1)
  504. return false;
  505. /* We can scroll if the first or last item is not visible */
  506. boolean firstItemVisible = lv.getFirstVisiblePosition() == 0;
  507. boolean lastItemVisible = lv.getLastVisiblePosition() == lv.getCount() - 1;
  508. if (firstItemVisible && lastItemVisible) {
  509. /* Or the first item's top is above or own top */
  510. if (lv.getChildAt(0).getTop() < lv.getPaddingTop())
  511. return true;
  512. /* or the last item's bottom is beyond our own bottom */
  513. return lv.getChildAt(lv.getChildCount() - 1).getBottom() >
  514. lv.getHeight() - lv.getPaddingBottom();
  515. }
  516. return true;
  517. }
  518. private boolean canListViewScroll() {
  519. return canAdapterViewScroll(listView);
  520. }
  521. public static class NotImplementedException extends Error {
  522. public NotImplementedException(String message) {
  523. super(message);
  524. }
  525. }
  526. /**
  527. * Detects whether or not the content TextView can be scrolled.
  528. */
  529. private boolean canContentScroll() {
  530. final ScrollView scrollView = (ScrollView) view.findViewById(R.id.contentScrollView);
  531. final int childHeight = view.findViewById(R.id.content).getMeasuredHeight();
  532. return scrollView.getMeasuredHeight() < childHeight;
  533. }
  534. /**
  535. * Measures the action button's and their text to decide whether or not the button should be stacked.
  536. */
  537. private void checkIfStackingNeeded() {
  538. if (numberOfActionButtons() <= 1) {
  539. return;
  540. } else if (mBuilder.forceStacking) {
  541. isStacked = true;
  542. invalidateActions();
  543. return;
  544. }
  545. isStacked = false;
  546. int buttonsWidth = 0;
  547. positiveButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
  548. neutralButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
  549. negativeButton.measure(View.MeasureSpec.UNSPECIFIED, View.MeasureSpec.UNSPECIFIED);
  550. if (mBuilder.positiveText != null) buttonsWidth += positiveButton.getMeasuredWidth();
  551. if (mBuilder.neutralText != null) buttonsWidth += neutralButton.getMeasuredWidth();
  552. if (mBuilder.negativeText != null) buttonsWidth += negativeButton.getMeasuredWidth();
  553. final int buttonFrameWidth = view.findViewById(R.id.buttonDefaultFrame).getWidth();
  554. isStacked = buttonsWidth > buttonFrameWidth;
  555. invalidateActions();
  556. }
  557. private Drawable getListSelector() {
  558. if (mBuilder.listSelector != 0)
  559. return mBuilder.context.getResources().getDrawable(mBuilder.listSelector);
  560. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_list_selector);
  561. if (d != null) return d;
  562. return DialogUtils.resolveDrawable(getContext(), R.attr.md_list_selector);
  563. }
  564. private Drawable getButtonSelector(DialogAction which) {
  565. if (isStacked) {
  566. if (mBuilder.btnSelectorStacked != 0)
  567. return mBuilder.context.getResources().getDrawable(mBuilder.btnSelectorStacked);
  568. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_stacked_selector);
  569. if (d != null) return d;
  570. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_stacked_selector);
  571. } else {
  572. switch (which) {
  573. default: {
  574. if (mBuilder.btnSelectorPositive != 0)
  575. return mBuilder.context.getResources().getDrawable(mBuilder.btnSelectorPositive);
  576. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_positive_selector);
  577. if (d != null) return d;
  578. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_positive_selector);
  579. }
  580. case NEUTRAL: {
  581. if (mBuilder.btnSelectorNeutral != 0)
  582. return mBuilder.context.getResources().getDrawable(mBuilder.btnSelectorNeutral);
  583. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_neutral_selector);
  584. if (d != null) return d;
  585. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_neutral_selector);
  586. }
  587. case NEGATIVE: {
  588. if (mBuilder.btnSelectorNegative != 0)
  589. return mBuilder.context.getResources().getDrawable(mBuilder.btnSelectorNegative);
  590. final Drawable d = DialogUtils.resolveDrawable(mBuilder.context, R.attr.md_btn_negative_selector);
  591. if (d != null) return d;
  592. return DialogUtils.resolveDrawable(getContext(), R.attr.md_btn_negative_selector);
  593. }
  594. }
  595. }
  596. }
  597. /**
  598. * Invalidates the positive/neutral/negative action buttons. Decides whether they should be visible
  599. * and sets their properties (such as height, text color, etc.).
  600. */
  601. private boolean invalidateActions() {
  602. if (!hasActionButtons()) {
  603. // If the dialog is a plain list dialog, no buttons are shown.
  604. view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.GONE);
  605. view.findViewById(R.id.buttonStackedFrame).setVisibility(View.GONE);
  606. invalidateList();
  607. return false;
  608. }
  609. if (isStacked) {
  610. view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.GONE);
  611. view.findViewById(R.id.buttonStackedFrame).setVisibility(View.VISIBLE);
  612. } else {
  613. view.findViewById(R.id.buttonDefaultFrame).setVisibility(View.VISIBLE);
  614. view.findViewById(R.id.buttonStackedFrame).setVisibility(View.GONE);
  615. }
  616. positiveButton = view.findViewById(
  617. isStacked ? R.id.buttonStackedPositive : R.id.buttonDefaultPositive);
  618. if (mBuilder.positiveText != null) {
  619. TextView positiveTextView = (TextView) ((FrameLayout) positiveButton).getChildAt(0);
  620. setTypeface(positiveTextView, mBuilder.mediumFont);
  621. positiveTextView.setText(mBuilder.positiveText);
  622. positiveTextView.setTextColor(getActionTextStateList(mBuilder.positiveColor));
  623. setBackgroundCompat(positiveButton, getButtonSelector(DialogAction.POSITIVE));
  624. positiveButton.setTag(POSITIVE);
  625. positiveButton.setOnClickListener(this);
  626. } else {
  627. positiveButton.setVisibility(View.GONE);
  628. }
  629. neutralButton = view.findViewById(
  630. isStacked ? R.id.buttonStackedNeutral : R.id.buttonDefaultNeutral);
  631. if (mBuilder.neutralText != null) {
  632. TextView neutralTextView = (TextView) ((FrameLayout) neutralButton).getChildAt(0);
  633. setTypeface(neutralTextView, mBuilder.mediumFont);
  634. neutralButton.setVisibility(View.VISIBLE);
  635. neutralTextView.setTextColor(getActionTextStateList(mBuilder.neutralColor));
  636. setBackgroundCompat(neutralButton, getButtonSelector(DialogAction.NEUTRAL));
  637. neutralTextView.setText(mBuilder.neutralText);
  638. neutralButton.setTag(NEUTRAL);
  639. neutralButton.setOnClickListener(this);
  640. } else {
  641. neutralButton.setVisibility(View.GONE);
  642. }
  643. negativeButton = view.findViewById(
  644. isStacked ? R.id.buttonStackedNegative : R.id.buttonDefaultNegative);
  645. if (mBuilder.negativeText != null) {
  646. TextView negativeTextView = (TextView) ((FrameLayout) negativeButton).getChildAt(0);
  647. setTypeface(negativeTextView, mBuilder.mediumFont);
  648. negativeButton.setVisibility(View.VISIBLE);
  649. negativeTextView.setTextColor(getActionTextStateList(mBuilder.negativeColor));
  650. setBackgroundCompat(negativeButton, getButtonSelector(DialogAction.NEGATIVE));
  651. negativeTextView.setText(mBuilder.negativeText);
  652. negativeButton.setTag(NEGATIVE);
  653. negativeButton.setOnClickListener(this);
  654. if (!isStacked) {
  655. RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(
  656. RelativeLayout.LayoutParams.WRAP_CONTENT, (int) getContext().getResources().getDimension(R.dimen.md_button_height));
  657. if (mBuilder.positiveText != null) {
  658. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  659. params.addRule(RelativeLayout.START_OF, R.id.buttonDefaultPositive);
  660. } else {
  661. params.addRule(RelativeLayout.LEFT_OF, R.id.buttonDefaultPositive);
  662. }
  663. } else {
  664. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR1) {
  665. params.addRule(RelativeLayout.ALIGN_PARENT_END);
  666. } else {
  667. params.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
  668. }
  669. }
  670. negativeButton.setLayoutParams(params);
  671. }
  672. } else {
  673. negativeButton.setVisibility(View.GONE);
  674. }
  675. invalidateList();
  676. return true;
  677. }
  678. private void sendSingleChoiceCallback(View v) {
  679. CharSequence text = null;
  680. if (mBuilder.selectedIndex >= 0) {
  681. text = mBuilder.items[mBuilder.selectedIndex];
  682. }
  683. mBuilder.listCallbackSingle.onSelection(this, v, mBuilder.selectedIndex, text);
  684. }
  685. private void sendMultichoiceCallback() {
  686. List<CharSequence> selectedTitles = new ArrayList<>();
  687. for (Integer i : selectedIndicesList) {
  688. selectedTitles.add(mBuilder.items[i]);
  689. }
  690. mBuilder.listCallbackMulti.onSelection(this,
  691. selectedIndicesList.toArray(new Integer[selectedIndicesList.size()]),
  692. selectedTitles.toArray(new CharSequence[selectedTitles.size()]));
  693. }
  694. @Override
  695. public final void onClick(View v) {
  696. String tag = (String) v.getTag();
  697. switch (tag) {
  698. case POSITIVE: {
  699. if (mBuilder.callback != null)
  700. mBuilder.callback.onPositive(this);
  701. if (mBuilder.listCallbackSingle != null)
  702. sendSingleChoiceCallback(v);
  703. if (mBuilder.listCallbackMulti != null)
  704. sendMultichoiceCallback();
  705. if (mBuilder.autoDismiss) dismiss();
  706. break;
  707. }
  708. case NEGATIVE: {
  709. if (mBuilder.callback != null)
  710. mBuilder.callback.onNegative(this);
  711. if (mBuilder.autoDismiss) dismiss();
  712. break;
  713. }
  714. case NEUTRAL: {
  715. if (mBuilder.callback != null)
  716. mBuilder.callback.onNeutral(this);
  717. if (mBuilder.autoDismiss) dismiss();
  718. break;
  719. }
  720. default: {
  721. String[] split = tag.split(":");
  722. int index = Integer.parseInt(split[0]);
  723. if (mBuilder.listCallback != null) {
  724. if (mBuilder.autoDismiss)
  725. dismiss();
  726. mBuilder.listCallback.onSelection(this, v, index, split[1]);
  727. } else if (mBuilder.listCallbackSingle != null) {
  728. RadioButton cb = (RadioButton) ((LinearLayout) v).getChildAt(0);
  729. if (!cb.isChecked())
  730. cb.setChecked(true);
  731. if (mBuilder.autoDismiss && mBuilder.positiveText == null) {
  732. dismiss();
  733. sendSingleChoiceCallback(v);
  734. } else if (mBuilder.alwaysCallSingleChoiceCallback) {
  735. sendSingleChoiceCallback(v);
  736. }
  737. } else if (mBuilder.listCallbackMulti != null) {
  738. CheckBox cb = (CheckBox) ((LinearLayout) v).getChildAt(0);
  739. cb.setChecked(!cb.isChecked());
  740. if (mBuilder.alwaysCallMultiChoiceCallback) {
  741. sendMultichoiceCallback();
  742. }
  743. } else if (mBuilder.autoDismiss) dismiss();
  744. break;
  745. }
  746. }
  747. }
  748. /**
  749. * The class used to construct a MaterialDialog.
  750. */
  751. public static class Builder {
  752. protected final Context context;
  753. protected CharSequence title;
  754. protected GravityEnum titleGravity = GravityEnum.START;
  755. protected GravityEnum contentGravity = GravityEnum.START;
  756. protected int titleColor = -1;
  757. protected int contentColor = -1;
  758. protected CharSequence content;
  759. protected CharSequence[] items;
  760. protected CharSequence positiveText;
  761. protected CharSequence neutralText;
  762. protected CharSequence negativeText;
  763. protected View customView;
  764. protected int positiveColor;
  765. protected int negativeColor;
  766. protected int neutralColor;
  767. protected ButtonCallback callback;
  768. protected ListCallback listCallback;
  769. protected ListCallback listCallbackSingle;
  770. protected ListCallbackMulti listCallbackMulti;
  771. protected boolean alwaysCallMultiChoiceCallback = false;
  772. protected boolean alwaysCallSingleChoiceCallback = false;
  773. protected Theme theme = Theme.LIGHT;
  774. protected boolean cancelable = true;
  775. protected float contentLineSpacingMultiplier = 1.3f;
  776. protected int selectedIndex = -1;
  777. protected Integer[] selectedIndices = null;
  778. protected boolean autoDismiss = true;
  779. protected Typeface regularFont;
  780. protected Typeface mediumFont;
  781. protected Drawable icon;
  782. protected ListAdapter adapter;
  783. protected OnDismissListener dismissListener;
  784. protected OnCancelListener cancelListener;
  785. protected OnKeyListener keyListener;
  786. protected OnShowListener showListener;
  787. protected boolean forceStacking;
  788. protected boolean wrapCustomViewInScroll;
  789. protected int dividerColor;
  790. protected int backgroundColor;
  791. protected int itemColor;
  792. @DrawableRes
  793. protected int listSelector;
  794. @DrawableRes
  795. protected int btnSelectorStacked;
  796. @DrawableRes
  797. protected int btnSelectorPositive;
  798. @DrawableRes
  799. protected int btnSelectorNeutral;
  800. @DrawableRes
  801. protected int btnSelectorNegative;
  802. public Builder(@NonNull Context context) {
  803. this.context = context;
  804. final int materialBlue = context.getResources().getColor(R.color.md_material_blue_600);
  805. if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
  806. TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{android.R.attr.colorAccent});
  807. try {
  808. this.positiveColor = a.getColor(0, materialBlue);
  809. this.negativeColor = a.getColor(0, materialBlue);
  810. this.neutralColor = a.getColor(0, materialBlue);
  811. } catch (Exception e) {
  812. this.positiveColor = materialBlue;
  813. this.negativeColor = materialBlue;
  814. this.neutralColor = materialBlue;
  815. } finally {
  816. a.recycle();
  817. }
  818. } else {
  819. TypedArray a = context.getTheme().obtainStyledAttributes(new int[]{R.attr.colorAccent});
  820. try {
  821. this.positiveColor = a.getColor(0, materialBlue);
  822. this.negativeColor = a.getColor(0, materialBlue);
  823. this.neutralColor = a.getColor(0, materialBlue);
  824. } catch (Exception e) {
  825. this.positiveColor = materialBlue;
  826. this.negativeColor = materialBlue;
  827. this.neutralColor = materialBlue;
  828. } finally {
  829. a.recycle();
  830. }
  831. }
  832. checkSingleton();
  833. }
  834. private void checkSingleton() {
  835. if (ThemeSingleton.get(false) == null) return;
  836. ThemeSingleton s = ThemeSingleton.get();
  837. theme(s.darkTheme ? Theme.DARK : Theme.LIGHT);
  838. if (s.titleColor != 0)
  839. this.titleColor = s.titleColor;
  840. if (s.contentColor != 0)
  841. this.contentColor = s.contentColor;
  842. if (s.positiveColor != 0)
  843. this.positiveColor = s.positiveColor;
  844. if (s.neutralColor != 0)
  845. this.neutralColor = s.neutralColor;
  846. if (s.negativeColor != 0)
  847. this.negativeColor = s.negativeColor;
  848. if (s.itemColor != 0)
  849. this.itemColor = s.itemColor;
  850. if (s.icon != null)
  851. this.icon = s.icon;
  852. if (s.backgroundColor != 0)
  853. this.backgroundColor = s.backgroundColor;
  854. if (s.dividerColor != 0)
  855. this.dividerColor = s.dividerColor;
  856. if (s.btnSelectorStacked != 0)
  857. this.btnSelectorStacked = s.btnSelectorStacked;
  858. if (s.listSelector != 0)
  859. this.listSelector = s.listSelector;
  860. if (s.btnSelectorPositive != 0)
  861. this.btnSelectorPositive = s.btnSelectorPositive;
  862. if (s.btnSelectorNeutral != 0)
  863. this.btnSelectorNeutral = s.btnSelectorNeutral;
  864. if (s.btnSelectorNegative != 0)
  865. this.btnSelectorNegative = s.btnSelectorNegative;
  866. }
  867. public Builder title(@StringRes int titleRes) {
  868. title(this.context.getString(titleRes));
  869. return this;
  870. }
  871. public Builder title(@NonNull CharSequence title) {
  872. this.title = title;
  873. return this;
  874. }
  875. public Builder titleGravity(@NonNull GravityEnum gravity) {
  876. this.titleGravity = gravity;
  877. return this;
  878. }
  879. public Builder titleColor(int color) {
  880. this.titleColor = color;
  881. return this;
  882. }
  883. public Builder titleColorRes(@ColorRes int colorRes) {
  884. titleColor(this.context.getResources().getColor(colorRes));
  885. return this;
  886. }
  887. public Builder titleColorAttr(@AttrRes int colorAttr) {
  888. titleColor(DialogUtils.resolveColor(this.context, colorAttr));
  889. return this;
  890. }
  891. /**
  892. * Sets the fonts used in the dialog.
  893. *
  894. * @param medium The font used on titles and action buttons. Null uses the default.
  895. * @param regular The font used everywhere else, like on the content and list items. Null uses the default.
  896. * @return The Builder instance so you can chain calls to it.
  897. */
  898. public Builder typeface(@NonNull Typeface medium, @NonNull Typeface regular) {
  899. this.mediumFont = medium;
  900. this.regularFont = regular;
  901. return this;
  902. }
  903. public Builder icon(@NonNull Drawable icon) {
  904. this.icon = icon;
  905. return this;
  906. }
  907. public Builder iconRes(@DrawableRes int icon) {
  908. this.icon = context.getResources().getDrawable(icon);
  909. return this;
  910. }
  911. public Builder iconAttr(@AttrRes int iconAttr) {
  912. this.icon = DialogUtils.resolveDrawable(context, iconAttr);
  913. return this;
  914. }
  915. public Builder contentColor(int color) {
  916. this.contentColor = color;
  917. return this;
  918. }
  919. public Builder contentColorRes(@ColorRes int colorRes) {
  920. contentColor(this.context.getResources().getColor(colorRes));
  921. return this;
  922. }
  923. public Builder contentColorAttr(@AttrRes int colorAttr) {
  924. contentColor(DialogUtils.resolveColor(this.context, colorAttr));
  925. return this;
  926. }
  927. public Builder content(@StringRes int contentRes) {
  928. content(this.context.getString(contentRes));
  929. return this;
  930. }
  931. public Builder content(CharSequence content) {
  932. this.content = content;
  933. return this;
  934. }
  935. public Builder content(@StringRes int contentRes, Object... formatArgs) {
  936. content(this.context.getString(contentRes, formatArgs));
  937. return this;
  938. }
  939. public Builder contentGravity(@NonNull GravityEnum gravity) {
  940. this.contentGravity = gravity;
  941. return this;
  942. }
  943. public Builder contentLineSpacing(float multiplier) {
  944. this.contentLineSpacingMultiplier = multiplier;
  945. return this;
  946. }
  947. public Builder items(@ArrayRes int itemsRes) {
  948. items(this.context.getResources().getTextArray(itemsRes));
  949. return this;
  950. }
  951. public Builder items(@NonNull CharSequence[] items) {
  952. this.items = items;
  953. return this;
  954. }
  955. public Builder itemsCallback(@NonNull ListCallback callback) {
  956. this.listCallback = callback;
  957. this.listCallbackSingle = null;
  958. this.listCallbackMulti = null;
  959. return this;
  960. }
  961. /**
  962. * Pass anything below 0 (such as -1) for the selected index to leave all options unselected initially.
  963. * Otherwise pass the index of an item that will be selected initially.
  964. *
  965. * @param selectedIndex The checkbox index that will be selected initially.
  966. * @param callback The callback that will be called when the presses the positive button.
  967. * @return The Builder instance so you can chain calls to it.
  968. */
  969. public Builder itemsCallbackSingleChoice(int selectedIndex, @NonNull ListCallback callback) {
  970. this.selectedIndex = selectedIndex;
  971. this.listCallback = null;
  972. this.listCallbackSingle = callback;
  973. this.listCallbackMulti = null;
  974. return this;
  975. }
  976. /**
  977. * By default, the single choice callback is only called when the user clicks the positive button
  978. * or if there are no buttons. Call this to force it to always call on item clicks even if the
  979. * positive button exists.
  980. *
  981. * @return The Builder instance so you can chain calls to it.
  982. */
  983. public Builder alwaysCallSingleChoiceCallback() {
  984. this.alwaysCallSingleChoiceCallback = true;
  985. return this;
  986. }
  987. /**
  988. * Pass null for the selected indices to leave all options unselected initially. Otherwise pass
  989. * an array of indices that will be selected initially.
  990. *
  991. * @param selectedIndices The radio button indices that will be selected initially.
  992. * @param callback The callback that will be called when the presses the positive button.
  993. * @return The Builder instance so you can chain calls to it.
  994. */
  995. public Builder itemsCallbackMultiChoice(@NonNull Integer[] selectedIndices, @NonNull ListCallbackMulti callback) {
  996. this.selectedIndices = selectedIndices;
  997. this.listCallback = null;
  998. this.listCallbackSingle = null;
  999. this.listCallbackMulti = callback;
  1000. return this;
  1001. }
  1002. /**
  1003. * By default, the multi choice callback is only called when the user clicks the positive button
  1004. * or if there are no buttons. Call this to force it to always call on item clicks even if the
  1005. * positive button exists.
  1006. *
  1007. * @return The Builder instance so you can chain calls to it.
  1008. */
  1009. public Builder alwaysCallMultiChoiceCallback() {
  1010. this.alwaysCallMultiChoiceCallback = true;
  1011. return this;
  1012. }
  1013. public Builder positiveText(@StringRes int postiveRes) {
  1014. positiveText(this.context.getString(postiveRes));
  1015. return this;
  1016. }
  1017. public Builder positiveText(@NonNull CharSequence message) {
  1018. this.positiveText = message;
  1019. return this;
  1020. }
  1021. public Builder neutralText(@StringRes int neutralRes) {
  1022. return neutralText(this.context.getString(neutralRes));
  1023. }
  1024. public Builder neutralText(@NonNull CharSequence message) {
  1025. this.neutralText = message;
  1026. return this;
  1027. }
  1028. public Builder negativeText(@StringRes int negativeRes) {
  1029. return negativeText(this.context.getString(negativeRes));
  1030. }
  1031. public Builder negativeText(@NonNull CharSequence message) {
  1032. this.negativeText = message;
  1033. return this;
  1034. }
  1035. public Builder listSelector(@DrawableRes int selectorRes) {
  1036. this.listSelector = selectorRes;
  1037. return this;
  1038. }
  1039. public Builder btnSelectorStacked(@DrawableRes int selectorRes) {
  1040. this.btnSelectorStacked = selectorRes;
  1041. return this;
  1042. }
  1043. public Builder btnSelector(@DrawableRes int selectorRes) {
  1044. this.btnSelectorPositive = selectorRes;
  1045. this.btnSelectorNeutral = selectorRes;
  1046. this.btnSelectorNegative = selectorRes;
  1047. return this;
  1048. }
  1049. public Builder btnSelector(@DrawableRes int selectorRes, @NonNull DialogAction which) {
  1050. switch (which) {
  1051. default:
  1052. this.btnSelectorPositive = selectorRes;
  1053. break;
  1054. case NEUTRAL:
  1055. this.btnSelectorNeutral = selectorRes;
  1056. break;
  1057. case NEGATIVE:
  1058. this.btnSelectorNegative = selectorRes;
  1059. break;
  1060. }
  1061. return this;
  1062. }
  1063. /**
  1064. * Use {@link #customView(int, boolean)} instead.
  1065. */
  1066. @Deprecated
  1067. public Builder customView(@LayoutRes int layoutRes) {
  1068. return customView(layoutRes, true);
  1069. }
  1070. public Builder customView(@LayoutRes int layoutRes, boolean wrapInScrollView) {
  1071. LayoutInflater li = LayoutInflater.from(this.context);
  1072. return customView(li.inflate(layoutRes, null), wrapInScrollView);
  1073. }
  1074. /**
  1075. * Use {@link #customView(android.view.View, boolean)} instead.
  1076. */
  1077. @Deprecated
  1078. public Builder customView(@NonNull View view) {
  1079. return customView(view, true);
  1080. }
  1081. public Builder customView(@NonNull View view, boolean wrapInScrollView) {
  1082. this.customView = view;
  1083. this.wrapCustomViewInScroll = wrapInScrollView;
  1084. return this;
  1085. }
  1086. public Builder positiveColor(int color) {
  1087. this.positiveColor = color;
  1088. return this;
  1089. }
  1090. public Builder positiveColorRes(@ColorRes int colorRes) {
  1091. positiveColor(this.context.getResources().getColor(colorRes));
  1092. return this;
  1093. }
  1094. public Builder positiveColorAttr(@AttrRes int colorAttr) {
  1095. positiveColor(DialogUtils.resolveColor(this.context, colorAttr));
  1096. return this;
  1097. }
  1098. public Builder negativeColor(int color) {
  1099. this.negativeColor = color;
  1100. return this;
  1101. }
  1102. public Builder negativeColorRes(@ColorRes int colorRes) {
  1103. negativeColor(this.context.getResources().getColor(colorRes));
  1104. return this;
  1105. }
  1106. public Builder negativeColorAttr(@AttrRes int colorAttr) {
  1107. negativeColor(DialogUtils.resolveColor(this.context, colorAttr));
  1108. return this;
  1109. }
  1110. public Builder neutralColor(int color) {
  1111. this.neutralColor = color;
  1112. return this;
  1113. }
  1114. public Builder neutralColorRes(@ColorRes int colorRes) {
  1115. return neutralColor(this.context.getResources().getColor(colorRes));
  1116. }
  1117. public Builder neutralColorAttr(@AttrRes int colorAttr) {
  1118. return neutralColor(DialogUtils.resolveColor(this.context, colorAttr));
  1119. }
  1120. public Builder dividerColor(int color) {
  1121. this.dividerColor = color;
  1122. return this;
  1123. }
  1124. public Builder dividerColorRes(@ColorRes int colorRes) {
  1125. return dividerColor(this.context.getResources().getColor(colorRes));
  1126. }
  1127. public Builder dividerColorAttr(@AttrRes int colorAttr) {
  1128. return dividerColor(DialogUtils.resolveColor(this.context, colorAttr));
  1129. }
  1130. public Builder backgroundColor(int color) {
  1131. this.backgroundColor = color;
  1132. return this;
  1133. }
  1134. public Builder backgroundColorRes(@ColorRes int colorRes) {
  1135. return backgroundColor(this.context.getResources().getColor(colorRes));
  1136. }
  1137. public Builder backgroundColorAttr(@AttrRes int colorAttr) {
  1138. return backgroundColor(DialogUtils.resolveColor(this.context, colorAttr));
  1139. }
  1140. public Builder itemColor(int color) {
  1141. this.itemColor = color;
  1142. return this;
  1143. }
  1144. public Builder itemColorRes(@ColorRes int colorRes) {
  1145. return itemColor(this.context.getResources().getColor(colorRes));
  1146. }
  1147. public Builder itemColorAttr(@AttrRes int colorAttr) {
  1148. return itemColor(DialogUtils.resolveColor(this.context, colorAttr));
  1149. }
  1150. public Builder callback(@NonNull ButtonCallback callback) {
  1151. this.callback = callback;
  1152. return this;
  1153. }
  1154. public Builder theme(@NonNull Theme theme) {
  1155. this.theme = theme;
  1156. return this;
  1157. }
  1158. public Builder cancelable(boolean cancelable) {
  1159. this.cancelable = cancelable;
  1160. return this;
  1161. }
  1162. /**
  1163. * This defaults to true. If set to false, the dialog will not automatically be dismissed
  1164. * when an action button is pressed, and not automatically dismissed when the user selects
  1165. * a list item.
  1166. *
  1167. * @param dismiss Whether or not to dismiss the dialog automatically.
  1168. * @return The Builder instance so you can chain calls to it.
  1169. */
  1170. public Builder autoDismiss(boolean dismiss) {
  1171. this.autoDismiss = dismiss;
  1172. return this;
  1173. }
  1174. /**
  1175. * Sets a custom {@link android.widget.ListAdapter} for the dialog's list
  1176. *
  1177. * @return This Builder object to allow for chaining of calls to set methods
  1178. */
  1179. public Builder adapter(@NonNull ListAdapter adapter) {
  1180. this.adapter = adapter;
  1181. return this;
  1182. }
  1183. public Builder showListener(@NonNull OnShowListener listener) {
  1184. this.showListener = listener;
  1185. return this;
  1186. }
  1187. public Builder dismissListener(@NonNull OnDismissListener listener) {
  1188. this.dismissListener = listener;
  1189. return this;
  1190. }
  1191. public Builder cancelListener(@NonNull OnCancelListener listener) {
  1192. this.cancelListener = listener;
  1193. return this;
  1194. }
  1195. public Builder keyListener(@NonNull OnKeyListener listener) {
  1196. this.keyListener = listener;
  1197. return this;
  1198. }
  1199. public Builder forceStacking(boolean stacked) {
  1200. this.forceStacking = stacked;
  1201. return this;
  1202. }
  1203. public MaterialDialog build() {
  1204. return new MaterialDialog(this);
  1205. }
  1206. public MaterialDialog show() {
  1207. MaterialDialog dialog = build();
  1208. dialog.show();
  1209. return dialog;
  1210. }
  1211. }
  1212. @Override
  1213. public void show() {
  1214. if (Looper.myLooper() != Looper.getMainLooper())
  1215. throw new IllegalStateException("Dialogs can only be shown from the UI thread.");
  1216. super.show();
  1217. }
  1218. private ColorStateList getActionTextStateList(int newPrimaryColor) {
  1219. final int fallBackButtonColor = DialogUtils.resolveColor(getContext(), android.R.attr.textColorPrimary);
  1220. if (newPrimaryColor == 0) newPrimaryColor = fallBackButtonColor;
  1221. int[][] states = new int[][]{
  1222. new int[]{-android.R.attr.state_enabled}, // disabled
  1223. new int[]{} // enabled
  1224. };
  1225. int[] colors = new int[]{
  1226. DialogUtils.adjustAlpha(newPrimaryColor, 0.4f),
  1227. newPrimaryColor
  1228. };
  1229. return new ColorStateList(states, colors);
  1230. }
  1231. /**
  1232. * Retrieves the view of an action button, allowing you to modify properties such as whether or not it's enabled.
  1233. * Use {@link #setActionButton(DialogAction, int)} to change text, since the view returned here is not
  1234. * the view that displays text.
  1235. *
  1236. * @param which The action button of which to get the view for.
  1237. * @return The view from the dialog's layout representing this action button.
  1238. */
  1239. public final View getActionButton(@NonNull DialogAction which) {
  1240. if (isStacked) {
  1241. switch (which) {
  1242. default:
  1243. return view.findViewById(R.id.buttonStackedPositive);
  1244. case NEUTRAL:
  1245. return view.findViewById(R.id.buttonStackedNeutral);
  1246. case NEGATIVE:
  1247. return view.findViewById(R.id.buttonStackedNegative);
  1248. }
  1249. } else {
  1250. switch (which) {
  1251. default:
  1252. return view.findViewById(R.id.buttonDefaultPositive);
  1253. case NEUTRAL:
  1254. return view.findViewById(R.id.buttonDefaultNeutral);
  1255. case NEGATIVE:
  1256. return view.findViewById(R.id.buttonDefaultNegative);
  1257. }
  1258. }
  1259. }
  1260. /**
  1261. * This will not return buttons that are actually in the layout itself, since the layout doesn't
  1262. * contain buttons. This is only implemented to avoid crashing issues on Huawei devices. Huawei's
  1263. * stock OS requires this method in order to detect visible buttons.
  1264. *
  1265. * @deprecated Use getActionButton(com.afollestad.materialdialogs.DialogAction)} instead.
  1266. */
  1267. @Deprecated
  1268. @Override
  1269. public Button getButton(int whichButton) {
  1270. if (whichButton == AlertDialog.BUTTON_POSITIVE) {
  1271. return mBuilder.positiveText != null ? new Button(getContext()) : null;
  1272. } else if (whichButton == AlertDialog.BUTTON_NEUTRAL) {
  1273. return mBuilder.neutralText != null ? new Button(getContext()) : null;
  1274. } else {
  1275. return mBuilder.negativeText != null ? new Button(getContext()) : null;
  1276. }
  1277. }
  1278. /**
  1279. * Retrieves the frame view containing the title and icon. You can manually change visibility and retrieve children.
  1280. */
  1281. public final View getTitleFrame() {
  1282. return titleFrame;
  1283. }
  1284. /**
  1285. * Retrieves the custom view that was inflated or set to the MaterialDialog during building.
  1286. *
  1287. * @return The custom view that was passed into the Builder.
  1288. */
  1289. public final View getCustomView() {
  1290. return mBuilder.customView;
  1291. }
  1292. /**
  1293. * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
  1294. *
  1295. * @param which The action button to update.
  1296. * @param title The new title of the action button.
  1297. */
  1298. public final void setActionButton(@NonNull DialogAction which, CharSequence title) {
  1299. switch (which) {
  1300. default:
  1301. mBuilder.positiveText = title;
  1302. break;
  1303. case NEUTRAL:
  1304. mBuilder.neutralText = title;
  1305. break;
  1306. case NEGATIVE:
  1307. mBuilder.negativeText = title;
  1308. break;
  1309. }
  1310. invalidateActions();
  1311. }
  1312. /**
  1313. * Updates an action button's title, causing invalidation to check if the action buttons should be stacked.
  1314. *
  1315. * @param which The action button to update.
  1316. * @param titleRes The string resource of the new title of the action button.
  1317. */
  1318. public final void setActionButton(DialogAction which, @StringRes int titleRes) {
  1319. setActionButton(which, getContext().getString(titleRes));
  1320. }
  1321. /**
  1322. * Gets whether or not the positive, neutral, or negative action button is visible.
  1323. *
  1324. * @return Whether or not 1 or more action buttons is visible.
  1325. */
  1326. public final boolean hasActionButtons() {
  1327. return numberOfActionButtons() > 0;
  1328. }
  1329. /**
  1330. * Gets the number of visible action buttons.
  1331. *
  1332. * @return 0 through 3, depending on how many should be or are visible.
  1333. */
  1334. public final int numberOfActionButtons() {
  1335. int number = 0;
  1336. if (mBuilder.positiveText != null) number++;
  1337. if (mBuilder.neutralText != null) number++;
  1338. if (mBuilder.negativeText != null) number++;
  1339. return number;
  1340. }
  1341. /**
  1342. * Updates the dialog's title.
  1343. */
  1344. public final void setTitle(@NonNull CharSequence title) {
  1345. this.title.setText(title);
  1346. }
  1347. @Override
  1348. public void setIcon(@DrawableRes int resId) {
  1349. icon.setImageResource(resId);
  1350. icon.setVisibility(resId != 0 ? View.VISIBLE : View.GONE);
  1351. }
  1352. @Override
  1353. public void setIcon(@NonNull Drawable d) {
  1354. icon.setImageDrawable(d);
  1355. icon.setVisibility(d != null ? View.VISIBLE : View.GONE);
  1356. }
  1357. @Override
  1358. public void setIconAttribute(@AttrRes int attrId) {
  1359. Drawable d = DialogUtils.resolveDrawable(mBuilder.context, attrId);
  1360. icon.setImageDrawable(d);
  1361. icon.setVisibility(d != null ? View.VISIBLE : View.GONE);
  1362. }
  1363. public final void setContent(CharSequence content) {
  1364. ((TextView) view.findViewById(R.id.content)).setText(content);
  1365. invalidateCustomViewAssociations(); // invalidates padding in content area scroll (if needed)
  1366. }
  1367. public final void setItems(CharSequence[] items) {
  1368. if (mBuilder.adapter == null)
  1369. throw new IllegalStateException("This MaterialDialog instance does not yet have an adapter set to it. You cannot use setItems().");
  1370. if (mBuilder.adapter instanceof MaterialDialogAdapter) {
  1371. mBuilder.adapter = new MaterialDialogAdapter(mBuilder.context,
  1372. ListType.getLayoutForType(listType), R.id.title, items);
  1373. } else {
  1374. throw new IllegalStateException("When using a custom adapter, setItems() cannot be used. Set items through the adapter instead.");
  1375. }
  1376. mBuilder.items = items;
  1377. listView.setAdapter(mBuilder.adapter);
  1378. invalidateCustomViewAssociations();
  1379. }
  1380. /**
  1381. * Use this to customize any list-specific logic for this dialog (OnItemClickListener, OnLongItemClickListener, etc.)
  1382. *
  1383. * @return The ListView instance used by this dialog, or null if not using a list.
  1384. */
  1385. @Nullable
  1386. public ListView getListView() {
  1387. return listView;
  1388. }
  1389. /**
  1390. * Convenience method for getting the currently selected index of a single choice list.
  1391. *
  1392. * @return Currently selected index of a single choice list, or -1 if not showing a single choice list
  1393. */
  1394. public int getSelectedIndex() {
  1395. if (mBuilder.listCallbackSingle != null) {
  1396. return mBuilder.selectedIndex;
  1397. } else {
  1398. return -1;
  1399. }
  1400. }
  1401. /**
  1402. * Convenience method for getting the currently selected indices of a multi choice list
  1403. *
  1404. * @return Currently selected index of a multi choice list, or null if not showing a multi choice list
  1405. */
  1406. @Nullable
  1407. public Integer[] getSelectedIndices() {
  1408. if (mBuilder.listCallbackMulti != null) {
  1409. return selectedIndicesList.toArray(new Integer[selectedIndicesList.size()]);
  1410. } else {
  1411. return null;
  1412. }
  1413. }
  1414. /**
  1415. * Convenience method for setting the currently selected index of a single choice list.
  1416. * This only works if you are not using a custom adapter; if you're using a custom adapter,
  1417. * an IllegalStateException is thrown. Note that this does not call the respective single choice callback.
  1418. *
  1419. * @param index The index of the list item to check.
  1420. */
  1421. public void setSelectedIndex(int index) {
  1422. mBuilder.selectedIndex = index;
  1423. if (mBuilder.adapter != null && mBuilder.adapter instanceof MaterialDialogAdapter) {
  1424. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  1425. } else {
  1426. throw new IllegalStateException("You can only use setSelectedIndex() with the default adapter implementation.");
  1427. }
  1428. }
  1429. /**
  1430. * Convenience method for setting the currently selected indices of a multi choice list.
  1431. * This only works if you are not using a custom adapter; if you're using a custom adapter,
  1432. * an IllegalStateException is thrown. Note that this does not call the respective multi choice callback.
  1433. *
  1434. * @param indices The indices of the list items to check.
  1435. */
  1436. public void setSelectedIndices(@NonNull Integer[] indices) {
  1437. mBuilder.selectedIndices = indices;
  1438. selectedIndicesList = new ArrayList<>(Arrays.asList(indices));
  1439. if (mBuilder.adapter != null && mBuilder.adapter instanceof MaterialDialogAdapter) {
  1440. ((MaterialDialogAdapter) mBuilder.adapter).notifyDataSetChanged();
  1441. } else {
  1442. throw new IllegalStateException("You can only use setSelectedIndices() with the default adapter implementation.");
  1443. }
  1444. }
  1445. private class MaterialDialogAdapter extends ArrayAdapter<CharSequence> {
  1446. final int itemColor;
  1447. public MaterialDialogAdapter(Context context, int resource, int textViewResourceId, CharSequence[] objects) {
  1448. super(context, resource, textViewResourceId, objects);
  1449. itemColor = DialogUtils.resolveColor(getContext(), R.attr.md_item_color, defaultItemColor);
  1450. }
  1451. @Override
  1452. public boolean hasStableIds() {
  1453. return true;
  1454. }
  1455. @Override
  1456. public long getItemId(int position) {
  1457. return position;
  1458. }
  1459. @SuppressLint("WrongViewCast")
  1460. @Override
  1461. public View getView(final int index, View convertView, ViewGroup parent) {
  1462. final View view = super.getView(index, convertView, parent);
  1463. TextView tv = (TextView) view.findViewById(R.id.title);
  1464. switch (listType) {
  1465. case SINGLE: {
  1466. @SuppressLint("CutPasteId")
  1467. RadioButton radio = (RadioButton) view.findViewById(R.id.control);
  1468. radio.setChecked(mBuilder.selectedIndex == index);
  1469. break;
  1470. }
  1471. case MULTI: {
  1472. @SuppressLint("CutPasteId")
  1473. CheckBox checkbox = (CheckBox) view.findViewById(R.id.control);
  1474. checkbox.setChecked(selectedIndicesList.contains(index));
  1475. break;
  1476. }
  1477. }
  1478. tv.setText(mBuilder.items[index]);
  1479. tv.setTextColor(itemColor);
  1480. setTypeface(tv, mBuilder.regularFont);
  1481. view.setTag(index + ":" + mBuilder.items[index]);
  1482. return view;
  1483. }
  1484. }
  1485. private static enum ListType {
  1486. REGULAR, SINGLE, MULTI;
  1487. public static int getLayoutForType(ListType type) {
  1488. switch (type) {
  1489. case REGULAR:
  1490. return R.layout.md_listitem;
  1491. case SINGLE:
  1492. return R.layout.md_listitem_singlechoice;
  1493. case MULTI:
  1494. return R.layout.md_listitem_multichoice;
  1495. default:
  1496. // Shouldn't be possible
  1497. throw new IllegalArgumentException("Not a valid list type");
  1498. }
  1499. }
  1500. }
  1501. public static interface ListCallback {
  1502. void onSelection(MaterialDialog dialog, View itemView, int which, CharSequence text);
  1503. }
  1504. public static interface ListCallbackMulti {
  1505. void onSelection(MaterialDialog dialog, Integer[] which, CharSequence[] text);
  1506. }
  1507. /**
  1508. * @deprecated Use the new {@link com.afollestad.materialdialogs.MaterialDialog.ButtonCallback}
  1509. */
  1510. @Deprecated
  1511. public abstract static class SimpleCallback extends ButtonCallback {
  1512. @Override
  1513. public abstract void onPositive(MaterialDialog dialog);
  1514. }
  1515. /**
  1516. * @deprecated Use the new {@link com.afollestad.materialdialogs.MaterialDialog.ButtonCallback}
  1517. */
  1518. @Deprecated
  1519. public abstract static class Callback extends SimpleCallback {
  1520. @Override
  1521. public abstract void onNegative(MaterialDialog dialog);
  1522. }
  1523. /**
  1524. * @deprecated Use the new {@link com.afollestad.materialdialogs.MaterialDialog.ButtonCallback}
  1525. */
  1526. @Deprecated
  1527. public abstract static class FullCallback extends Callback {
  1528. @Override
  1529. public abstract void onNeutral(MaterialDialog dialog);
  1530. }
  1531. /**
  1532. * Override these as needed, so no needing to sub empty methods from an interface
  1533. */
  1534. public static abstract class ButtonCallback {
  1535. public void onPositive(MaterialDialog dialog) {
  1536. }
  1537. public void onNegative(MaterialDialog dialog) {
  1538. }
  1539. public void onNeutral(MaterialDialog dialog) {
  1540. }
  1541. public ButtonCallback() {
  1542. super();
  1543. }
  1544. @Override
  1545. protected final Object clone() throws CloneNotSupportedException {
  1546. return super.clone();
  1547. }
  1548. @Override
  1549. public final boolean equals(Object o) {
  1550. return super.equals(o);
  1551. }
  1552. @Override
  1553. protected final void finalize() throws Throwable {
  1554. super.finalize();
  1555. }
  1556. @Override
  1557. public final int hashCode() {
  1558. return super.hashCode();
  1559. }
  1560. @Override
  1561. public final String toString() {
  1562. return super.toString();
  1563. }
  1564. }
  1565. }