blob: 46cb5461b682435966704a43fabfb241efc28190 [file] [log] [blame]
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001/*
2 * Copyright (C) 2008 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.internal.app;
18
Romain Guy980a9382010-01-08 15:06:28 -080019import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070020
21import com.android.internal.R;
22
Alan Viverette62c79e92015-02-26 09:47:10 -080023import android.annotation.Nullable;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080024import android.app.AlertDialog;
25import android.content.Context;
26import android.content.DialogInterface;
Michael Kwan937035e2016-06-09 11:59:21 -070027import android.content.res.Configuration;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080028import android.content.res.TypedArray;
29import android.database.Cursor;
30import android.graphics.drawable.Drawable;
31import android.os.Handler;
32import android.os.Message;
33import android.text.TextUtils;
Romain Guy6fe2b222010-02-22 14:11:40 -080034import android.util.AttributeSet;
Adam Powell3320dcd2010-11-05 20:06:02 -070035import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080036import android.view.Gravity;
37import android.view.KeyEvent;
38import android.view.LayoutInflater;
39import android.view.View;
40import android.view.ViewGroup;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070041import android.view.ViewGroup.LayoutParams;
Alan Viverette35c3cb62014-10-30 13:51:21 -070042import android.view.ViewParent;
Alan Viverette62c79e92015-02-26 09:47:10 -080043import android.view.ViewStub;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.view.Window;
Vinod Krishnan119ba2c2014-05-05 14:58:15 -070045import android.view.WindowInsets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080047import android.widget.AdapterView;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070048import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080049import android.widget.ArrayAdapter;
50import android.widget.Button;
51import android.widget.CheckedTextView;
52import android.widget.CursorAdapter;
53import android.widget.FrameLayout;
54import android.widget.ImageView;
55import android.widget.LinearLayout;
56import android.widget.ListAdapter;
57import android.widget.ListView;
58import android.widget.ScrollView;
59import android.widget.SimpleCursorAdapter;
60import android.widget.TextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080061
62import java.lang.ref.WeakReference;
63
64public class AlertController {
Michael Kwan246caac2016-05-17 00:46:58 -070065 public static final int MICRO = 1;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080066
67 private final Context mContext;
68 private final DialogInterface mDialogInterface;
Michael Kwancc6e6f02016-05-16 12:54:57 -070069 protected final Window mWindow;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070070
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private CharSequence mTitle;
Michael Kwancc6e6f02016-05-16 12:54:57 -070072 protected CharSequence mMessage;
73 protected ListView mListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080074 private View mView;
75
Alan Viveretteec186702013-12-05 11:10:31 -080076 private int mViewLayoutResId;
77
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private int mViewSpacingLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 private int mViewSpacingTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private int mViewSpacingRight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080081 private int mViewSpacingBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private boolean mViewSpacingSpecified = false;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070083
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private Button mButtonPositive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080085 private CharSequence mButtonPositiveText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080086 private Message mButtonPositiveMessage;
87
88 private Button mButtonNegative;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080089 private CharSequence mButtonNegativeText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080090 private Message mButtonNegativeMessage;
91
92 private Button mButtonNeutral;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080093 private CharSequence mButtonNeutralText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080094 private Message mButtonNeutralMessage;
95
Michael Kwancc6e6f02016-05-16 12:54:57 -070096 protected ScrollView mScrollView;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070097
Alan Viverettea1a68042014-04-28 13:30:08 -070098 private int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private Drawable mIcon;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700100
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private ImageView mIconView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 private TextView mTitleView;
Michael Kwancc6e6f02016-05-16 12:54:57 -0700103 protected TextView mMessageView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private View mCustomTitleView;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private boolean mForceInverseBackground;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 private ListAdapter mAdapter;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800110 private int mCheckedItem = -1;
111
Adam Powellfcca00a2010-11-30 21:26:29 -0800112 private int mAlertDialogLayout;
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700113 private int mButtonPanelSideLayout;
Adam Powellfcca00a2010-11-30 21:26:29 -0800114 private int mListLayout;
115 private int mMultiChoiceItemLayout;
116 private int mSingleChoiceItemLayout;
117 private int mListItemLayout;
118
Alan Viverette2e750a12016-01-29 12:53:26 -0500119 private boolean mShowTitle;
120
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700121 private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700122
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800123 private Handler mHandler;
124
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800125 private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
126 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 public void onClick(View v) {
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800128 final Message m;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 if (v == mButtonPositive && mButtonPositiveMessage != null) {
130 m = Message.obtain(mButtonPositiveMessage);
131 } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
132 m = Message.obtain(mButtonNegativeMessage);
133 } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
134 m = Message.obtain(mButtonNeutralMessage);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800135 } else {
136 m = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800138
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800139 if (m != null) {
140 m.sendToTarget();
141 }
142
143 // Post a message so we dismiss after the above handlers are executed
144 mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
145 .sendToTarget();
146 }
147 };
148
149 private static final class ButtonHandler extends Handler {
150 // Button clicks have Message.what as the BUTTON{1,2,3} constant
151 private static final int MSG_DISMISS_DIALOG = 1;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700152
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800153 private WeakReference<DialogInterface> mDialog;
154
155 public ButtonHandler(DialogInterface dialog) {
Alan Viverette2e750a12016-01-29 12:53:26 -0500156 mDialog = new WeakReference<>(dialog);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800157 }
158
159 @Override
160 public void handleMessage(Message msg) {
161 switch (msg.what) {
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700162
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800163 case DialogInterface.BUTTON_POSITIVE:
164 case DialogInterface.BUTTON_NEGATIVE:
165 case DialogInterface.BUTTON_NEUTRAL:
166 ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
167 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700168
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800169 case MSG_DISMISS_DIALOG:
170 ((DialogInterface) msg.obj).dismiss();
171 }
172 }
173 }
174
Adam Powell3320dcd2010-11-05 20:06:02 -0700175 private static boolean shouldCenterSingleButton(Context context) {
Alan Viverette89900832015-04-08 12:45:54 -0700176 final TypedValue outValue = new TypedValue();
177 context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
Adam Powell3320dcd2010-11-05 20:06:02 -0700178 return outValue.data != 0;
179 }
180
Michael Kwancc6e6f02016-05-16 12:54:57 -0700181 public static final AlertController create(Context context, DialogInterface di, Window window) {
182 final TypedArray a = context.obtainStyledAttributes(
183 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
184 int controllerType = a.getInt(R.styleable.AlertDialog_controllerType, 0);
185 a.recycle();
186
187 switch (controllerType) {
Michael Kwan246caac2016-05-17 00:46:58 -0700188 case MICRO:
189 return new MicroAlertController(context, di, window);
Michael Kwancc6e6f02016-05-16 12:54:57 -0700190 default:
191 return new AlertController(context, di, window);
192 }
193 }
194
195 protected AlertController(Context context, DialogInterface di, Window window) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800196 mContext = context;
197 mDialogInterface = di;
198 mWindow = window;
199 mHandler = new ButtonHandler(di);
Adam Powellfcca00a2010-11-30 21:26:29 -0800200
Alan Viverette89900832015-04-08 12:45:54 -0700201 final TypedArray a = context.obtainStyledAttributes(null,
202 R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
Adam Powellfcca00a2010-11-30 21:26:29 -0800203
Alan Viverette89900832015-04-08 12:45:54 -0700204 mAlertDialogLayout = a.getResourceId(
205 R.styleable.AlertDialog_layout, R.layout.alert_dialog);
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700206 mButtonPanelSideLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700207 R.styleable.AlertDialog_buttonPanelSideLayout, 0);
Adam Powellfcca00a2010-11-30 21:26:29 -0800208 mListLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700209 R.styleable.AlertDialog_listLayout, R.layout.select_dialog);
210
Adam Powellfcca00a2010-11-30 21:26:29 -0800211 mMultiChoiceItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700212 R.styleable.AlertDialog_multiChoiceItemLayout,
213 R.layout.select_dialog_multichoice);
Adam Powellfcca00a2010-11-30 21:26:29 -0800214 mSingleChoiceItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700215 R.styleable.AlertDialog_singleChoiceItemLayout,
216 R.layout.select_dialog_singlechoice);
Adam Powellfcca00a2010-11-30 21:26:29 -0800217 mListItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700218 R.styleable.AlertDialog_listItemLayout,
219 R.layout.select_dialog_item);
Alan Viverette2e750a12016-01-29 12:53:26 -0500220 mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
Adam Powellfcca00a2010-11-30 21:26:29 -0800221
222 a.recycle();
Chris Banesccf8e662016-03-23 16:20:00 +0000223
224 /* We use a custom title so never request a window title */
225 window.requestFeature(Window.FEATURE_NO_TITLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800226 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700227
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800228 static boolean canTextInput(View v) {
229 if (v.onCheckIsTextEditor()) {
230 return true;
231 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700232
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800233 if (!(v instanceof ViewGroup)) {
234 return false;
235 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700236
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 ViewGroup vg = (ViewGroup)v;
238 int i = vg.getChildCount();
239 while (i > 0) {
240 i--;
241 v = vg.getChildAt(i);
242 if (canTextInput(v)) {
243 return true;
244 }
245 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700246
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800247 return false;
248 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700249
Eugene Suslacf00ade2017-04-10 11:51:58 -0700250 public void installContent(AlertParams params) {
251 params.apply(this);
252 installContent();
253 }
254
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800255 public void installContent() {
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700256 int contentView = selectContentView();
257 mWindow.setContentView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800258 setupView();
259 }
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700260
261 private int selectContentView() {
262 if (mButtonPanelSideLayout == 0) {
263 return mAlertDialogLayout;
264 }
265 if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
266 return mButtonPanelSideLayout;
267 }
268 // TODO: use layout hint side for long messages/lists
269 return mAlertDialogLayout;
270 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700271
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 public void setTitle(CharSequence title) {
273 mTitle = title;
274 if (mTitleView != null) {
275 mTitleView.setText(title);
276 }
277 }
278
279 /**
280 * @see AlertDialog.Builder#setCustomTitle(View)
281 */
282 public void setCustomTitle(View customTitleView) {
283 mCustomTitleView = customTitleView;
284 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700285
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800286 public void setMessage(CharSequence message) {
287 mMessage = message;
288 if (mMessageView != null) {
289 mMessageView.setText(message);
290 }
291 }
292
293 /**
Alan Viveretteec186702013-12-05 11:10:31 -0800294 * Set the view resource to display in the dialog.
295 */
296 public void setView(int layoutResId) {
297 mView = null;
298 mViewLayoutResId = layoutResId;
299 mViewSpacingSpecified = false;
300 }
301
302 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800303 * Set the view to display in the dialog.
304 */
305 public void setView(View view) {
306 mView = view;
Alan Viveretteec186702013-12-05 11:10:31 -0800307 mViewLayoutResId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800308 mViewSpacingSpecified = false;
309 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700310
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800311 /**
312 * Set the view to display in the dialog along with the spacing around that view
313 */
314 public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
315 int viewSpacingBottom) {
316 mView = view;
Alan Viveretteec186702013-12-05 11:10:31 -0800317 mViewLayoutResId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800318 mViewSpacingSpecified = true;
319 mViewSpacingLeft = viewSpacingLeft;
320 mViewSpacingTop = viewSpacingTop;
321 mViewSpacingRight = viewSpacingRight;
322 mViewSpacingBottom = viewSpacingBottom;
323 }
324
325 /**
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700326 * Sets a hint for the best button panel layout.
327 */
328 public void setButtonPanelLayoutHint(int layoutHint) {
329 mButtonPanelLayoutHint = layoutHint;
330 }
331
332 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800333 * Sets a click listener or a message to be sent when the button is clicked.
334 * You only need to pass one of {@code listener} or {@code msg}.
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700335 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800336 * @param whichButton Which button, can be one of
337 * {@link DialogInterface#BUTTON_POSITIVE},
338 * {@link DialogInterface#BUTTON_NEGATIVE}, or
339 * {@link DialogInterface#BUTTON_NEUTRAL}
340 * @param text The text to display in positive button.
341 * @param listener The {@link DialogInterface.OnClickListener} to use.
342 * @param msg The {@link Message} to be sent when clicked.
343 */
344 public void setButton(int whichButton, CharSequence text,
345 DialogInterface.OnClickListener listener, Message msg) {
346
347 if (msg == null && listener != null) {
348 msg = mHandler.obtainMessage(whichButton, listener);
349 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700350
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800351 switch (whichButton) {
352
353 case DialogInterface.BUTTON_POSITIVE:
354 mButtonPositiveText = text;
355 mButtonPositiveMessage = msg;
356 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700357
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800358 case DialogInterface.BUTTON_NEGATIVE:
359 mButtonNegativeText = text;
360 mButtonNegativeMessage = msg;
361 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700362
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800363 case DialogInterface.BUTTON_NEUTRAL:
364 mButtonNeutralText = text;
365 mButtonNeutralMessage = msg;
366 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700367
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800368 default:
369 throw new IllegalArgumentException("Button does not exist");
370 }
371 }
372
373 /**
Alan Viverettea1a68042014-04-28 13:30:08 -0700374 * Specifies the icon to display next to the alert title.
375 *
376 * @param resId the resource identifier of the drawable to use as the icon,
377 * or 0 for no icon
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800378 */
379 public void setIcon(int resId) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700380 mIcon = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800381 mIconId = resId;
Alan Viverettea1a68042014-04-28 13:30:08 -0700382
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800383 if (mIconView != null) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700384 if (resId != 0) {
Chris Banes476da772015-12-15 17:14:15 +0000385 mIconView.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 mIconView.setImageResource(mIconId);
Alan Viverettea1a68042014-04-28 13:30:08 -0700387 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 mIconView.setVisibility(View.GONE);
389 }
390 }
391 }
Alan Viverettea1a68042014-04-28 13:30:08 -0700392
393 /**
394 * Specifies the icon to display next to the alert title.
395 *
396 * @param icon the drawable to use as the icon or null for no icon
397 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800398 public void setIcon(Drawable icon) {
399 mIcon = icon;
Alan Viverettea1a68042014-04-28 13:30:08 -0700400 mIconId = 0;
401
402 if (mIconView != null) {
403 if (icon != null) {
Chris Banes12a47ac2015-12-17 10:00:26 +0000404 mIconView.setVisibility(View.VISIBLE);
Alan Viverettea1a68042014-04-28 13:30:08 -0700405 mIconView.setImageDrawable(icon);
406 } else {
407 mIconView.setVisibility(View.GONE);
408 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800409 }
410 }
411
blunden576e1df2012-09-10 23:29:04 +0200412 /**
413 * @param attrId the attributeId of the theme-specific drawable
414 * to resolve the resourceId for.
415 *
416 * @return resId the resourceId of the theme-specific drawable
417 */
418 public int getIconAttributeResId(int attrId) {
419 TypedValue out = new TypedValue();
420 mContext.getTheme().resolveAttribute(attrId, out, true);
421 return out.resourceId;
422 }
423
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800424 public void setInverseBackgroundForced(boolean forceInverseBackground) {
425 mForceInverseBackground = forceInverseBackground;
426 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700427
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800428 public ListView getListView() {
429 return mListView;
430 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700431
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 public Button getButton(int whichButton) {
433 switch (whichButton) {
434 case DialogInterface.BUTTON_POSITIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800435 return mButtonPositive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800436 case DialogInterface.BUTTON_NEGATIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800437 return mButtonNegative;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800438 case DialogInterface.BUTTON_NEUTRAL:
Romain Guy6fe2b222010-02-22 14:11:40 -0800439 return mButtonNeutral;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 default:
441 return null;
442 }
443 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700444
Romain Guy6fe2b222010-02-22 14:11:40 -0800445 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800446 public boolean onKeyDown(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800447 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800448 }
449
Romain Guy6fe2b222010-02-22 14:11:40 -0800450 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800451 public boolean onKeyUp(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800452 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800453 }
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700454
Alan Viverette62c79e92015-02-26 09:47:10 -0800455 /**
456 * Resolves whether a custom or default panel should be used. Removes the
457 * default panel if a custom panel should be used. If the resolved panel is
458 * a view stub, inflates before returning.
459 *
460 * @param customPanel the custom panel
461 * @param defaultPanel the default panel
462 * @return the panel to use
463 */
464 @Nullable
465 private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) {
466 if (customPanel == null) {
467 // Inflate the default panel, if needed.
468 if (defaultPanel instanceof ViewStub) {
469 defaultPanel = ((ViewStub) defaultPanel).inflate();
470 }
471
472 return (ViewGroup) defaultPanel;
473 }
474
475 // Remove the default panel entirely.
476 if (defaultPanel != null) {
477 final ViewParent parent = defaultPanel.getParent();
478 if (parent instanceof ViewGroup) {
479 ((ViewGroup) parent).removeView(defaultPanel);
480 }
481 }
482
483 // Inflate the custom panel, if needed.
484 if (customPanel instanceof ViewStub) {
485 customPanel = ((ViewStub) customPanel).inflate();
486 }
487
488 return (ViewGroup) customPanel;
489 }
490
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491 private void setupView() {
Alan Viverette62c79e92015-02-26 09:47:10 -0800492 final View parentPanel = mWindow.findViewById(R.id.parentPanel);
493 final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
494 final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
495 final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
496
497 // Install custom content before setting up the title or buttons so
498 // that we can handle panel overrides.
499 final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
500 setupCustomContent(customPanel);
501
502 final View customTopPanel = customPanel.findViewById(R.id.topPanel);
503 final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
504 final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
505
506 // Resolve the correct panels and remove the defaults, if needed.
507 final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
508 final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
509 final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
510
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800511 setupContent(contentPanel);
Alan Viverette62c79e92015-02-26 09:47:10 -0800512 setupButtons(buttonPanel);
513 setupTitle(topPanel);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700514
Alan Viverette62c79e92015-02-26 09:47:10 -0800515 final boolean hasCustomPanel = customPanel != null
516 && customPanel.getVisibility() != View.GONE;
517 final boolean hasTopPanel = topPanel != null
518 && topPanel.getVisibility() != View.GONE;
519 final boolean hasButtonPanel = buttonPanel != null
520 && buttonPanel.getVisibility() != View.GONE;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700521
Alan Viverette62c79e92015-02-26 09:47:10 -0800522 // Only display the text spacer if we don't have buttons.
523 if (!hasButtonPanel) {
524 if (contentPanel != null) {
525 final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
526 if (spacer != null) {
527 spacer.setVisibility(View.VISIBLE);
528 }
Adam Powelld044ad92014-09-15 14:30:23 -0700529 }
Dianne Hackbornef575752011-01-18 17:35:17 -0800530 mWindow.setCloseOnTouchOutsideIfNotSet(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800531 }
532
Alan Viverette62c79e92015-02-26 09:47:10 -0800533 if (hasTopPanel) {
Alan Viverette922e1c62015-05-05 17:18:27 -0700534 // Only clip scrolling content to padding if we have a title.
535 if (mScrollView != null) {
536 mScrollView.setClipToPadding(true);
537 }
538
539 // Only show the divider if we have a title.
Alan Viveretted6bfb822016-03-30 20:56:30 -0400540 View divider = null;
Alan Viverette922e1c62015-05-05 17:18:27 -0700541 if (mMessage != null || mListView != null || hasCustomPanel) {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400542 if (!hasCustomPanel) {
543 divider = topPanel.findViewById(R.id.titleDividerNoCustom);
544 }
545 if (divider == null) {
546 divider = topPanel.findViewById(R.id.titleDivider);
547 }
548
Alan Viverette62c79e92015-02-26 09:47:10 -0800549 } else {
550 divider = topPanel.findViewById(R.id.titleDividerTop);
551 }
552
553 if (divider != null) {
554 divider.setVisibility(View.VISIBLE);
555 }
Alan Viveretted6bfb822016-03-30 20:56:30 -0400556 } else {
557 if (contentPanel != null) {
558 final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
559 if (spacer != null) {
560 spacer.setVisibility(View.VISIBLE);
561 }
562 }
563 }
564
565 if (mListView instanceof RecycleListView) {
566 ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
Alan Viverette62c79e92015-02-26 09:47:10 -0800567 }
568
Alan Viverette922e1c62015-05-05 17:18:27 -0700569 // Update scroll indicators as needed.
570 if (!hasCustomPanel) {
571 final View content = mListView != null ? mListView : mScrollView;
572 if (content != null) {
573 final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
574 | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
575 content.setScrollIndicators(indicators,
576 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
577 }
578 }
579
Alan Viverette62c79e92015-02-26 09:47:10 -0800580 final TypedArray a = mContext.obtainStyledAttributes(
581 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
582 setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
583 hasTopPanel, hasCustomPanel, hasButtonPanel);
584 a.recycle();
585 }
586
587 private void setupCustomContent(ViewGroup customPanel) {
Alan Viveretteec186702013-12-05 11:10:31 -0800588 final View customView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800589 if (mView != null) {
Alan Viveretteec186702013-12-05 11:10:31 -0800590 customView = mView;
591 } else if (mViewLayoutResId != 0) {
592 final LayoutInflater inflater = LayoutInflater.from(mContext);
593 customView = inflater.inflate(mViewLayoutResId, customPanel, false);
594 } else {
595 customView = null;
596 }
597
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800598 final boolean hasCustomView = customView != null;
599 if (!hasCustomView || !canTextInput(customView)) {
Alan Viveretteec186702013-12-05 11:10:31 -0800600 mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
601 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
602 }
603
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800604 if (hasCustomView) {
Alan Viveretteec186702013-12-05 11:10:31 -0800605 final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
606 custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 if (mViewSpacingSpecified) {
Alan Viveretteec186702013-12-05 11:10:31 -0800609 custom.setPadding(
610 mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800611 }
Alan Viveretteec186702013-12-05 11:10:31 -0800612
The Android Open Source Project10592532009-03-18 17:39:46 -0700613 if (mListView != null) {
614 ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
615 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800616 } else {
Alan Viveretteec186702013-12-05 11:10:31 -0800617 customPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800619 }
620
Michael Kwan81450812016-07-19 09:48:45 -0700621 protected void setupTitle(ViewGroup topPanel) {
Alan Viverette2e750a12016-01-29 12:53:26 -0500622 if (mCustomTitleView != null && mShowTitle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800623 // Add the custom title view directly to the topPanel layout
Alan Viverette2e750a12016-01-29 12:53:26 -0500624 final LayoutParams lp = new LayoutParams(
Alan Viverette154c2c22014-10-29 15:46:06 -0700625 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700626
Adam Powell5ea0bc12011-07-21 13:07:05 -0700627 topPanel.addView(mCustomTitleView, 0, lp);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700628
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800629 // Hide the title template
Alan Viverette2e750a12016-01-29 12:53:26 -0500630 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 titleTemplate.setVisibility(View.GONE);
632 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 mIconView = (ImageView) mWindow.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800634
Alan Viverettea1a68042014-04-28 13:30:08 -0700635 final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
Alan Viverette2e750a12016-01-29 12:53:26 -0500636 if (hasTextTitle && mShowTitle) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700637 // Display the title if a title is supplied, else hide it.
638 mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 mTitleView.setText(mTitle);
Alan Viverettea1a68042014-04-28 13:30:08 -0700640
641 // Do this last so that if the user has supplied any icons we
642 // use them instead of the default ones. If the user has
643 // specified 0 then make it disappear.
644 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 mIconView.setImageResource(mIconId);
646 } else if (mIcon != null) {
647 mIconView.setImageDrawable(mIcon);
Alan Viverettea1a68042014-04-28 13:30:08 -0700648 } else {
649 // Apply the padding from the icon to ensure the title is
650 // aligned correctly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 mTitleView.setPadding(mIconView.getPaddingLeft(),
652 mIconView.getPaddingTop(),
653 mIconView.getPaddingRight(),
654 mIconView.getPaddingBottom());
655 mIconView.setVisibility(View.GONE);
656 }
657 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800658 // Hide the title template
Alan Viverettea1a68042014-04-28 13:30:08 -0700659 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800660 titleTemplate.setVisibility(View.GONE);
661 mIconView.setVisibility(View.GONE);
Martin Sjoline74e7e22010-06-22 21:42:11 +0200662 topPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800663 }
664 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800665 }
666
Michael Kwancc6e6f02016-05-16 12:54:57 -0700667 protected void setupContent(ViewGroup contentPanel) {
Alan Viverette62c79e92015-02-26 09:47:10 -0800668 mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800669 mScrollView.setFocusable(false);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700670
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800671 // Special case for users that only want to display a String
Alan Viverette62c79e92015-02-26 09:47:10 -0800672 mMessageView = (TextView) contentPanel.findViewById(R.id.message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800673 if (mMessageView == null) {
674 return;
675 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700676
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800677 if (mMessage != null) {
678 mMessageView.setText(mMessage);
679 } else {
680 mMessageView.setVisibility(View.GONE);
681 mScrollView.removeView(mMessageView);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700682
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 if (mListView != null) {
Alan Viverette35c3cb62014-10-30 13:51:21 -0700684 final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
685 final int childIndex = scrollParent.indexOfChild(mScrollView);
686 scrollParent.removeViewAt(childIndex);
687 scrollParent.addView(mListView, childIndex,
Alan Viverette154c2c22014-10-29 15:46:06 -0700688 new LayoutParams(MATCH_PARENT, MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 } else {
690 contentPanel.setVisibility(View.GONE);
691 }
692 }
Alan Viverette154c2c22014-10-29 15:46:06 -0700693 }
694
695 private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
696 if (upIndicator != null) {
697 upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
698 }
699 if (downIndicator != null) {
700 downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
701 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800702 }
703
Michael Kwan81450812016-07-19 09:48:45 -0700704 protected void setupButtons(ViewGroup buttonPanel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800705 int BIT_BUTTON_POSITIVE = 1;
706 int BIT_BUTTON_NEGATIVE = 2;
707 int BIT_BUTTON_NEUTRAL = 4;
708 int whichButtons = 0;
Alan Viverette62c79e92015-02-26 09:47:10 -0800709 mButtonPositive = (Button) buttonPanel.findViewById(R.id.button1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800710 mButtonPositive.setOnClickListener(mButtonHandler);
711
712 if (TextUtils.isEmpty(mButtonPositiveText)) {
713 mButtonPositive.setVisibility(View.GONE);
714 } else {
715 mButtonPositive.setText(mButtonPositiveText);
716 mButtonPositive.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800717 whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
718 }
719
Alan Viverette62c79e92015-02-26 09:47:10 -0800720 mButtonNegative = (Button) buttonPanel.findViewById(R.id.button2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800721 mButtonNegative.setOnClickListener(mButtonHandler);
722
723 if (TextUtils.isEmpty(mButtonNegativeText)) {
724 mButtonNegative.setVisibility(View.GONE);
725 } else {
726 mButtonNegative.setText(mButtonNegativeText);
727 mButtonNegative.setVisibility(View.VISIBLE);
728
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800729 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
730 }
731
Alan Viverette62c79e92015-02-26 09:47:10 -0800732 mButtonNeutral = (Button) buttonPanel.findViewById(R.id.button3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800733 mButtonNeutral.setOnClickListener(mButtonHandler);
734
735 if (TextUtils.isEmpty(mButtonNeutralText)) {
736 mButtonNeutral.setVisibility(View.GONE);
737 } else {
738 mButtonNeutral.setText(mButtonNeutralText);
739 mButtonNeutral.setVisibility(View.VISIBLE);
740
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
742 }
743
Adam Powell3320dcd2010-11-05 20:06:02 -0700744 if (shouldCenterSingleButton(mContext)) {
745 /*
746 * If we only have 1 button it should be centered on the layout and
747 * expand to fill 50% of the available space.
748 */
749 if (whichButtons == BIT_BUTTON_POSITIVE) {
750 centerButton(mButtonPositive);
751 } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
Sangkyu Lee3079e582013-02-15 21:11:18 +0900752 centerButton(mButtonNegative);
Adam Powell3320dcd2010-11-05 20:06:02 -0700753 } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
754 centerButton(mButtonNeutral);
755 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700757
Alan Viverette62c79e92015-02-26 09:47:10 -0800758 final boolean hasButtons = whichButtons != 0;
759 if (!hasButtons) {
760 buttonPanel.setVisibility(View.GONE);
761 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800762 }
763
764 private void centerButton(Button button) {
765 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
766 params.gravity = Gravity.CENTER_HORIZONTAL;
767 params.weight = 0.5f;
768 button.setLayoutParams(params);
769 View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700770 if (leftSpacer != null) {
771 leftSpacer.setVisibility(View.VISIBLE);
772 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800773 View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700774 if (rightSpacer != null) {
775 rightSpacer.setVisibility(View.VISIBLE);
776 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800777 }
778
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800779 private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
780 View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700781 int fullDark = 0;
782 int topDark = 0;
783 int centerDark = 0;
784 int bottomDark = 0;
785 int fullBright = 0;
786 int topBright = 0;
787 int centerBright = 0;
788 int bottomBright = 0;
789 int bottomMedium = 0;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700790
791 // If the needsDefaultBackgrounds attribute is set, we know we're
792 // inheriting from a framework style.
793 final boolean needsDefaultBackgrounds = a.getBoolean(
794 R.styleable.AlertDialog_needsDefaultBackgrounds, true);
795 if (needsDefaultBackgrounds) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700796 fullDark = R.drawable.popup_full_dark;
797 topDark = R.drawable.popup_top_dark;
798 centerDark = R.drawable.popup_center_dark;
799 bottomDark = R.drawable.popup_bottom_dark;
800 fullBright = R.drawable.popup_full_bright;
801 topBright = R.drawable.popup_top_bright;
802 centerBright = R.drawable.popup_center_bright;
803 bottomBright = R.drawable.popup_bottom_bright;
804 bottomMedium = R.drawable.popup_bottom_medium;
805 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700806
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700807 topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700808 topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700809 centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700810 centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700811
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800812 /* We now set the background of all of the sections of the alert.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800813 * First collect together each section that is being displayed along
814 * with whether it is on a light or dark background, then run through
815 * them setting their backgrounds. This is complicated because we need
816 * to correctly use the full, top, middle, and bottom graphics depending
817 * on how many views they are and where they appear.
818 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800819
820 final View[] views = new View[4];
821 final boolean[] light = new boolean[4];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800822 View lastView = null;
823 boolean lastLight = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800824
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800825 int pos = 0;
826 if (hasTitle) {
827 views[pos] = topPanel;
828 light[pos] = false;
829 pos++;
830 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800831
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800832 /* The contentPanel displays either a custom text message or
833 * a ListView. If it's text we should use the dark background
834 * for ListView we should use the light background. If neither
835 * are there the contentPanel will be hidden so set it as null.
836 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800837 views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel;
Romain Guy6fe2b222010-02-22 14:11:40 -0800838 light[pos] = mListView != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800839 pos++;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800840
841 if (hasCustomView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 views[pos] = customPanel;
843 light[pos] = mForceInverseBackground;
844 pos++;
845 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800846
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 if (hasButtons) {
848 views[pos] = buttonPanel;
849 light[pos] = true;
850 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800851
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800852 boolean setView = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800853 for (pos = 0; pos < views.length; pos++) {
854 final View v = views[pos];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800855 if (v == null) {
856 continue;
857 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800858
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 if (lastView != null) {
860 if (!setView) {
861 lastView.setBackgroundResource(lastLight ? topBright : topDark);
862 } else {
863 lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
864 }
865 setView = true;
866 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800867
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800868 lastView = v;
869 lastLight = light[pos];
870 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800871
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800872 if (lastView != null) {
873 if (setView) {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700874 bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
875 bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
876 bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800877
878 // ListViews will use the Bright background, but buttons use the
879 // Medium background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800880 lastView.setBackgroundResource(
881 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
882 } else {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700883 fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
884 fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800885
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800886 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
887 }
888 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800889
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800890 final ListView listView = mListView;
891 if (listView != null && mAdapter != null) {
892 listView.setAdapter(mAdapter);
893 final int checkedItem = mCheckedItem;
894 if (checkedItem > -1) {
Michael Kwan55e40302016-07-22 12:17:04 -0700895 listView.setItemChecked(checkedItem, true);
Michael Kwan44f854a2016-08-18 15:15:26 -0700896 listView.setSelectionFromTop(checkedItem,
897 a.getDimensionPixelSize(R.styleable.AlertDialog_selectionScrollOffset, 0));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800898 }
899 }
900 }
Romain Guy9c802c12009-03-25 15:07:31 -0700901
902 public static class RecycleListView extends ListView {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400903 private final int mPaddingTopNoTitle;
904 private final int mPaddingBottomNoButtons;
905
Romain Guy9c802c12009-03-25 15:07:31 -0700906 boolean mRecycleOnMeasure = true;
907
908 public RecycleListView(Context context) {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400909 this(context, null);
Romain Guy9c802c12009-03-25 15:07:31 -0700910 }
911
912 public RecycleListView(Context context, AttributeSet attrs) {
913 super(context, attrs);
Alan Viveretted6bfb822016-03-30 20:56:30 -0400914
915 final TypedArray ta = context.obtainStyledAttributes(
916 attrs, R.styleable.RecycleListView);
917 mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
918 R.styleable.RecycleListView_paddingBottomNoButtons, -1);
919 mPaddingTopNoTitle = ta.getDimensionPixelOffset(
920 R.styleable.RecycleListView_paddingTopNoTitle, -1);
Romain Guy9c802c12009-03-25 15:07:31 -0700921 }
922
Alan Viveretted6bfb822016-03-30 20:56:30 -0400923 public void setHasDecor(boolean hasTitle, boolean hasButtons) {
924 if (!hasButtons || !hasTitle) {
925 final int paddingLeft = getPaddingLeft();
926 final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
927 final int paddingRight = getPaddingRight();
928 final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
929 setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
930 }
Romain Guy9c802c12009-03-25 15:07:31 -0700931 }
932
933 @Override
934 protected boolean recycleOnMeasure() {
935 return mRecycleOnMeasure;
936 }
937 }
938
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800939 public static class AlertParams {
940 public final Context mContext;
941 public final LayoutInflater mInflater;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700942
Gilles Debunne076c7fb2010-10-04 12:00:02 -0700943 public int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800944 public Drawable mIcon;
blunden576e1df2012-09-10 23:29:04 +0200945 public int mIconAttrId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800946 public CharSequence mTitle;
947 public View mCustomTitleView;
948 public CharSequence mMessage;
949 public CharSequence mPositiveButtonText;
950 public DialogInterface.OnClickListener mPositiveButtonListener;
951 public CharSequence mNegativeButtonText;
952 public DialogInterface.OnClickListener mNegativeButtonListener;
953 public CharSequence mNeutralButtonText;
954 public DialogInterface.OnClickListener mNeutralButtonListener;
955 public boolean mCancelable;
956 public DialogInterface.OnCancelListener mOnCancelListener;
Adam Powell7f02dc52012-08-27 13:35:16 -0700957 public DialogInterface.OnDismissListener mOnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800958 public DialogInterface.OnKeyListener mOnKeyListener;
959 public CharSequence[] mItems;
960 public ListAdapter mAdapter;
961 public DialogInterface.OnClickListener mOnClickListener;
Alan Viveretteec186702013-12-05 11:10:31 -0800962 public int mViewLayoutResId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800963 public View mView;
964 public int mViewSpacingLeft;
965 public int mViewSpacingTop;
966 public int mViewSpacingRight;
967 public int mViewSpacingBottom;
968 public boolean mViewSpacingSpecified = false;
969 public boolean[] mCheckedItems;
970 public boolean mIsMultiChoice;
971 public boolean mIsSingleChoice;
972 public int mCheckedItem = -1;
973 public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
974 public Cursor mCursor;
975 public String mLabelColumn;
976 public String mIsCheckedColumn;
977 public boolean mForceInverseBackground;
978 public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
979 public OnPrepareListViewListener mOnPrepareListViewListener;
Romain Guy9c802c12009-03-25 15:07:31 -0700980 public boolean mRecycleOnMeasure = true;
981
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800982 /**
983 * Interface definition for a callback to be invoked before the ListView
984 * will be bound to an adapter.
985 */
986 public interface OnPrepareListViewListener {
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700987
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800988 /**
989 * Called before the ListView is bound to an adapter.
990 * @param listView The ListView that will be shown in the dialog.
991 */
992 void onPrepareListView(ListView listView);
993 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700994
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 public AlertParams(Context context) {
996 mContext = context;
997 mCancelable = true;
998 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
999 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001000
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001001 public void apply(AlertController dialog) {
1002 if (mCustomTitleView != null) {
1003 dialog.setCustomTitle(mCustomTitleView);
1004 } else {
1005 if (mTitle != null) {
1006 dialog.setTitle(mTitle);
1007 }
1008 if (mIcon != null) {
1009 dialog.setIcon(mIcon);
1010 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -08001011 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001012 dialog.setIcon(mIconId);
1013 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -08001014 if (mIconAttrId != 0) {
blunden576e1df2012-09-10 23:29:04 +02001015 dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
1016 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001017 }
1018 if (mMessage != null) {
1019 dialog.setMessage(mMessage);
1020 }
1021 if (mPositiveButtonText != null) {
1022 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
1023 mPositiveButtonListener, null);
1024 }
1025 if (mNegativeButtonText != null) {
1026 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
1027 mNegativeButtonListener, null);
1028 }
1029 if (mNeutralButtonText != null) {
1030 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
1031 mNeutralButtonListener, null);
1032 }
1033 if (mForceInverseBackground) {
1034 dialog.setInverseBackgroundForced(true);
1035 }
1036 // For a list, the client can either supply an array of items or an
1037 // adapter or a cursor
1038 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
1039 createListView(dialog);
1040 }
1041 if (mView != null) {
1042 if (mViewSpacingSpecified) {
1043 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
1044 mViewSpacingBottom);
1045 } else {
1046 dialog.setView(mView);
1047 }
Alan Viveretteec186702013-12-05 11:10:31 -08001048 } else if (mViewLayoutResId != 0) {
1049 dialog.setView(mViewLayoutResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001050 }
Alan Viveretteec186702013-12-05 11:10:31 -08001051
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001052 /*
1053 dialog.setCancelable(mCancelable);
1054 dialog.setOnCancelListener(mOnCancelListener);
1055 if (mOnKeyListener != null) {
1056 dialog.setOnKeyListener(mOnKeyListener);
1057 }
1058 */
1059 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001060
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001061 private void createListView(final AlertController dialog) {
Alan Viverette89900832015-04-08 12:45:54 -07001062 final RecycleListView listView =
1063 (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
1064 final ListAdapter adapter;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001065
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001066 if (mIsMultiChoice) {
1067 if (mCursor == null) {
1068 adapter = new ArrayAdapter<CharSequence>(
Adam Powellfcca00a2010-11-30 21:26:29 -08001069 mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001070 @Override
1071 public View getView(int position, View convertView, ViewGroup parent) {
1072 View view = super.getView(position, convertView, parent);
1073 if (mCheckedItems != null) {
1074 boolean isItemChecked = mCheckedItems[position];
1075 if (isItemChecked) {
Michael Kwan55e40302016-07-22 12:17:04 -07001076 listView.setItemChecked(position, true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 }
1078 }
1079 return view;
1080 }
1081 };
1082 } else {
1083 adapter = new CursorAdapter(mContext, mCursor, false) {
1084 private final int mLabelIndex;
1085 private final int mIsCheckedIndex;
1086
1087 {
1088 final Cursor cursor = getCursor();
1089 mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
1090 mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
1091 }
1092
1093 @Override
1094 public void bindView(View view, Context context, Cursor cursor) {
1095 CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
1096 text.setText(cursor.getString(mLabelIndex));
Michael Kwan55e40302016-07-22 12:17:04 -07001097 listView.setItemChecked(
1098 cursor.getPosition(),
1099 cursor.getInt(mIsCheckedIndex) == 1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001100 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001101
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 @Override
1103 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001104 return mInflater.inflate(dialog.mMultiChoiceItemLayout,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 parent, false);
1106 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001108 };
1109 }
1110 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001111 final int layout;
1112 if (mIsSingleChoice) {
1113 layout = dialog.mSingleChoiceItemLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001114 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001115 layout = dialog.mListItemLayout;
1116 }
1117
1118 if (mCursor != null) {
1119 adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
1120 new String[] { mLabelColumn }, new int[] { R.id.text1 });
1121 } else if (mAdapter != null) {
1122 adapter = mAdapter;
1123 } else {
1124 adapter = new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001125 }
1126 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001127
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001128 if (mOnPrepareListViewListener != null) {
1129 mOnPrepareListViewListener.onPrepareListView(listView);
1130 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001131
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001132 /* Don't directly set the adapter on the ListView as we might
1133 * want to add a footer to the ListView later.
1134 */
1135 dialog.mAdapter = adapter;
1136 dialog.mCheckedItem = mCheckedItem;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 if (mOnClickListener != null) {
1139 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001140 @Override
1141 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001142 mOnClickListener.onClick(dialog.mDialogInterface, position);
1143 if (!mIsSingleChoice) {
1144 dialog.mDialogInterface.dismiss();
1145 }
1146 }
1147 });
1148 } else if (mOnCheckboxClickListener != null) {
1149 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001150 @Override
1151 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001152 if (mCheckedItems != null) {
1153 mCheckedItems[position] = listView.isItemChecked(position);
1154 }
1155 mOnCheckboxClickListener.onClick(
1156 dialog.mDialogInterface, position, listView.isItemChecked(position));
1157 }
1158 });
1159 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001161 // Attach a given OnItemSelectedListener to the ListView
1162 if (mOnItemSelectedListener != null) {
1163 listView.setOnItemSelectedListener(mOnItemSelectedListener);
1164 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001165
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001166 if (mIsSingleChoice) {
1167 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
1168 } else if (mIsMultiChoice) {
1169 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
1170 }
Romain Guy9c802c12009-03-25 15:07:31 -07001171 listView.mRecycleOnMeasure = mRecycleOnMeasure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001172 dialog.mListView = listView;
1173 }
1174 }
1175
Alan Viverette2356c5e2014-05-22 22:43:59 -07001176 private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
1177 public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
1178 CharSequence[] objects) {
1179 super(context, resource, textViewResourceId, objects);
1180 }
1181
1182 @Override
1183 public boolean hasStableIds() {
1184 return true;
1185 }
1186
1187 @Override
1188 public long getItemId(int position) {
1189 return position;
1190 }
1191 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001192}