blob: 753c0691222e0715503dba1b8ad52830ba5a803e [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;
27import android.content.res.TypedArray;
28import android.database.Cursor;
29import android.graphics.drawable.Drawable;
30import android.os.Handler;
31import android.os.Message;
32import android.text.TextUtils;
Romain Guy6fe2b222010-02-22 14:11:40 -080033import android.util.AttributeSet;
Adam Powell3320dcd2010-11-05 20:06:02 -070034import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080035import android.view.Gravity;
36import android.view.KeyEvent;
37import android.view.LayoutInflater;
38import android.view.View;
39import android.view.ViewGroup;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070040import android.view.ViewGroup.LayoutParams;
Alan Viverette35c3cb62014-10-30 13:51:21 -070041import android.view.ViewParent;
Alan Viverette62c79e92015-02-26 09:47:10 -080042import android.view.ViewStub;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080043import android.view.Window;
Vinod Krishnan119ba2c2014-05-05 14:58:15 -070044import android.view.WindowInsets;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080045import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080046import android.widget.AdapterView;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070047import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080048import android.widget.ArrayAdapter;
49import android.widget.Button;
50import android.widget.CheckedTextView;
51import android.widget.CursorAdapter;
52import android.widget.FrameLayout;
53import android.widget.ImageView;
54import android.widget.LinearLayout;
55import android.widget.ListAdapter;
56import android.widget.ListView;
57import android.widget.ScrollView;
58import android.widget.SimpleCursorAdapter;
59import android.widget.TextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080060
61import java.lang.ref.WeakReference;
62
63public class AlertController {
64
65 private final Context mContext;
66 private final DialogInterface mDialogInterface;
67 private final Window mWindow;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070068
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080069 private CharSequence mTitle;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080070 private CharSequence mMessage;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080071 private ListView mListView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080072 private View mView;
73
Alan Viveretteec186702013-12-05 11:10:31 -080074 private int mViewLayoutResId;
75
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080076 private int mViewSpacingLeft;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080077 private int mViewSpacingTop;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080078 private int mViewSpacingRight;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080079 private int mViewSpacingBottom;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080080 private boolean mViewSpacingSpecified = false;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070081
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080082 private Button mButtonPositive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080083 private CharSequence mButtonPositiveText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080084 private Message mButtonPositiveMessage;
85
86 private Button mButtonNegative;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080087 private CharSequence mButtonNegativeText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080088 private Message mButtonNegativeMessage;
89
90 private Button mButtonNeutral;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080091 private CharSequence mButtonNeutralText;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080092 private Message mButtonNeutralMessage;
93
94 private ScrollView mScrollView;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070095
Alan Viverettea1a68042014-04-28 13:30:08 -070096 private int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080097 private Drawable mIcon;
Alan Viveretteb17c6c12014-09-03 16:27:51 -070098
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080099 private ImageView mIconView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800100 private TextView mTitleView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800101 private TextView mMessageView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800102 private View mCustomTitleView;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700103
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800104 private boolean mForceInverseBackground;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700105
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800106 private ListAdapter mAdapter;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700107
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800108 private int mCheckedItem = -1;
109
Adam Powellfcca00a2010-11-30 21:26:29 -0800110 private int mAlertDialogLayout;
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700111 private int mButtonPanelSideLayout;
Adam Powellfcca00a2010-11-30 21:26:29 -0800112 private int mListLayout;
113 private int mMultiChoiceItemLayout;
114 private int mSingleChoiceItemLayout;
115 private int mListItemLayout;
116
Alan Viverette2e750a12016-01-29 12:53:26 -0500117 private boolean mShowTitle;
118
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700119 private int mButtonPanelLayoutHint = AlertDialog.LAYOUT_HINT_NONE;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700120
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800121 private Handler mHandler;
122
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800123 private final View.OnClickListener mButtonHandler = new View.OnClickListener() {
124 @Override
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800125 public void onClick(View v) {
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800126 final Message m;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800127 if (v == mButtonPositive && mButtonPositiveMessage != null) {
128 m = Message.obtain(mButtonPositiveMessage);
129 } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
130 m = Message.obtain(mButtonNegativeMessage);
131 } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
132 m = Message.obtain(mButtonNeutralMessage);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800133 } else {
134 m = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800135 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800136
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800137 if (m != null) {
138 m.sendToTarget();
139 }
140
141 // Post a message so we dismiss after the above handlers are executed
142 mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
143 .sendToTarget();
144 }
145 };
146
147 private static final class ButtonHandler extends Handler {
148 // Button clicks have Message.what as the BUTTON{1,2,3} constant
149 private static final int MSG_DISMISS_DIALOG = 1;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700150
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800151 private WeakReference<DialogInterface> mDialog;
152
153 public ButtonHandler(DialogInterface dialog) {
Alan Viverette2e750a12016-01-29 12:53:26 -0500154 mDialog = new WeakReference<>(dialog);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800155 }
156
157 @Override
158 public void handleMessage(Message msg) {
159 switch (msg.what) {
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700160
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800161 case DialogInterface.BUTTON_POSITIVE:
162 case DialogInterface.BUTTON_NEGATIVE:
163 case DialogInterface.BUTTON_NEUTRAL:
164 ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
165 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700166
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800167 case MSG_DISMISS_DIALOG:
168 ((DialogInterface) msg.obj).dismiss();
169 }
170 }
171 }
172
Adam Powell3320dcd2010-11-05 20:06:02 -0700173 private static boolean shouldCenterSingleButton(Context context) {
Alan Viverette89900832015-04-08 12:45:54 -0700174 final TypedValue outValue = new TypedValue();
175 context.getTheme().resolveAttribute(R.attr.alertDialogCenterButtons, outValue, true);
Adam Powell3320dcd2010-11-05 20:06:02 -0700176 return outValue.data != 0;
177 }
178
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800179 public AlertController(Context context, DialogInterface di, Window window) {
180 mContext = context;
181 mDialogInterface = di;
182 mWindow = window;
183 mHandler = new ButtonHandler(di);
Adam Powellfcca00a2010-11-30 21:26:29 -0800184
Alan Viverette89900832015-04-08 12:45:54 -0700185 final TypedArray a = context.obtainStyledAttributes(null,
186 R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
Adam Powellfcca00a2010-11-30 21:26:29 -0800187
Alan Viverette89900832015-04-08 12:45:54 -0700188 mAlertDialogLayout = a.getResourceId(
189 R.styleable.AlertDialog_layout, R.layout.alert_dialog);
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700190 mButtonPanelSideLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700191 R.styleable.AlertDialog_buttonPanelSideLayout, 0);
Adam Powellfcca00a2010-11-30 21:26:29 -0800192 mListLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700193 R.styleable.AlertDialog_listLayout, R.layout.select_dialog);
194
Adam Powellfcca00a2010-11-30 21:26:29 -0800195 mMultiChoiceItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700196 R.styleable.AlertDialog_multiChoiceItemLayout,
197 R.layout.select_dialog_multichoice);
Adam Powellfcca00a2010-11-30 21:26:29 -0800198 mSingleChoiceItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700199 R.styleable.AlertDialog_singleChoiceItemLayout,
200 R.layout.select_dialog_singlechoice);
Adam Powellfcca00a2010-11-30 21:26:29 -0800201 mListItemLayout = a.getResourceId(
Alan Viverette89900832015-04-08 12:45:54 -0700202 R.styleable.AlertDialog_listItemLayout,
203 R.layout.select_dialog_item);
Alan Viverette2e750a12016-01-29 12:53:26 -0500204 mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true);
Adam Powellfcca00a2010-11-30 21:26:29 -0800205
206 a.recycle();
Chris Banesccf8e662016-03-23 16:20:00 +0000207
208 /* We use a custom title so never request a window title */
209 window.requestFeature(Window.FEATURE_NO_TITLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700211
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800212 static boolean canTextInput(View v) {
213 if (v.onCheckIsTextEditor()) {
214 return true;
215 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700216
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800217 if (!(v instanceof ViewGroup)) {
218 return false;
219 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700220
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800221 ViewGroup vg = (ViewGroup)v;
222 int i = vg.getChildCount();
223 while (i > 0) {
224 i--;
225 v = vg.getChildAt(i);
226 if (canTextInput(v)) {
227 return true;
228 }
229 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700230
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800231 return false;
232 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700233
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800234 public void installContent() {
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700235 int contentView = selectContentView();
236 mWindow.setContentView(contentView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800237 setupView();
238 }
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700239
240 private int selectContentView() {
241 if (mButtonPanelSideLayout == 0) {
242 return mAlertDialogLayout;
243 }
244 if (mButtonPanelLayoutHint == AlertDialog.LAYOUT_HINT_SIDE) {
245 return mButtonPanelSideLayout;
246 }
247 // TODO: use layout hint side for long messages/lists
248 return mAlertDialogLayout;
249 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700250
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800251 public void setTitle(CharSequence title) {
252 mTitle = title;
253 if (mTitleView != null) {
254 mTitleView.setText(title);
255 }
256 }
257
258 /**
259 * @see AlertDialog.Builder#setCustomTitle(View)
260 */
261 public void setCustomTitle(View customTitleView) {
262 mCustomTitleView = customTitleView;
263 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700264
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800265 public void setMessage(CharSequence message) {
266 mMessage = message;
267 if (mMessageView != null) {
268 mMessageView.setText(message);
269 }
270 }
271
272 /**
Alan Viveretteec186702013-12-05 11:10:31 -0800273 * Set the view resource to display in the dialog.
274 */
275 public void setView(int layoutResId) {
276 mView = null;
277 mViewLayoutResId = layoutResId;
278 mViewSpacingSpecified = false;
279 }
280
281 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800282 * Set the view to display in the dialog.
283 */
284 public void setView(View view) {
285 mView = view;
Alan Viveretteec186702013-12-05 11:10:31 -0800286 mViewLayoutResId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800287 mViewSpacingSpecified = false;
288 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700289
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800290 /**
291 * Set the view to display in the dialog along with the spacing around that view
292 */
293 public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
294 int viewSpacingBottom) {
295 mView = view;
Alan Viveretteec186702013-12-05 11:10:31 -0800296 mViewLayoutResId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800297 mViewSpacingSpecified = true;
298 mViewSpacingLeft = viewSpacingLeft;
299 mViewSpacingTop = viewSpacingTop;
300 mViewSpacingRight = viewSpacingRight;
301 mViewSpacingBottom = viewSpacingBottom;
302 }
303
304 /**
Craig Stout4c0cb8a2014-04-04 13:03:10 -0700305 * Sets a hint for the best button panel layout.
306 */
307 public void setButtonPanelLayoutHint(int layoutHint) {
308 mButtonPanelLayoutHint = layoutHint;
309 }
310
311 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800312 * Sets a click listener or a message to be sent when the button is clicked.
313 * You only need to pass one of {@code listener} or {@code msg}.
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700314 *
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800315 * @param whichButton Which button, can be one of
316 * {@link DialogInterface#BUTTON_POSITIVE},
317 * {@link DialogInterface#BUTTON_NEGATIVE}, or
318 * {@link DialogInterface#BUTTON_NEUTRAL}
319 * @param text The text to display in positive button.
320 * @param listener The {@link DialogInterface.OnClickListener} to use.
321 * @param msg The {@link Message} to be sent when clicked.
322 */
323 public void setButton(int whichButton, CharSequence text,
324 DialogInterface.OnClickListener listener, Message msg) {
325
326 if (msg == null && listener != null) {
327 msg = mHandler.obtainMessage(whichButton, listener);
328 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700329
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800330 switch (whichButton) {
331
332 case DialogInterface.BUTTON_POSITIVE:
333 mButtonPositiveText = text;
334 mButtonPositiveMessage = msg;
335 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700336
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800337 case DialogInterface.BUTTON_NEGATIVE:
338 mButtonNegativeText = text;
339 mButtonNegativeMessage = msg;
340 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700341
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800342 case DialogInterface.BUTTON_NEUTRAL:
343 mButtonNeutralText = text;
344 mButtonNeutralMessage = msg;
345 break;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700346
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800347 default:
348 throw new IllegalArgumentException("Button does not exist");
349 }
350 }
351
352 /**
Alan Viverettea1a68042014-04-28 13:30:08 -0700353 * Specifies the icon to display next to the alert title.
354 *
355 * @param resId the resource identifier of the drawable to use as the icon,
356 * or 0 for no icon
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800357 */
358 public void setIcon(int resId) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700359 mIcon = null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800360 mIconId = resId;
Alan Viverettea1a68042014-04-28 13:30:08 -0700361
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800362 if (mIconView != null) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700363 if (resId != 0) {
Chris Banes476da772015-12-15 17:14:15 +0000364 mIconView.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800365 mIconView.setImageResource(mIconId);
Alan Viverettea1a68042014-04-28 13:30:08 -0700366 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800367 mIconView.setVisibility(View.GONE);
368 }
369 }
370 }
Alan Viverettea1a68042014-04-28 13:30:08 -0700371
372 /**
373 * Specifies the icon to display next to the alert title.
374 *
375 * @param icon the drawable to use as the icon or null for no icon
376 */
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800377 public void setIcon(Drawable icon) {
378 mIcon = icon;
Alan Viverettea1a68042014-04-28 13:30:08 -0700379 mIconId = 0;
380
381 if (mIconView != null) {
382 if (icon != null) {
Chris Banes12a47ac2015-12-17 10:00:26 +0000383 mIconView.setVisibility(View.VISIBLE);
Alan Viverettea1a68042014-04-28 13:30:08 -0700384 mIconView.setImageDrawable(icon);
385 } else {
386 mIconView.setVisibility(View.GONE);
387 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800388 }
389 }
390
blunden576e1df2012-09-10 23:29:04 +0200391 /**
392 * @param attrId the attributeId of the theme-specific drawable
393 * to resolve the resourceId for.
394 *
395 * @return resId the resourceId of the theme-specific drawable
396 */
397 public int getIconAttributeResId(int attrId) {
398 TypedValue out = new TypedValue();
399 mContext.getTheme().resolveAttribute(attrId, out, true);
400 return out.resourceId;
401 }
402
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800403 public void setInverseBackgroundForced(boolean forceInverseBackground) {
404 mForceInverseBackground = forceInverseBackground;
405 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700406
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800407 public ListView getListView() {
408 return mListView;
409 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700410
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800411 public Button getButton(int whichButton) {
412 switch (whichButton) {
413 case DialogInterface.BUTTON_POSITIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800414 return mButtonPositive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 case DialogInterface.BUTTON_NEGATIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800416 return mButtonNegative;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800417 case DialogInterface.BUTTON_NEUTRAL:
Romain Guy6fe2b222010-02-22 14:11:40 -0800418 return mButtonNeutral;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 default:
420 return null;
421 }
422 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700423
Romain Guy6fe2b222010-02-22 14:11:40 -0800424 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800425 public boolean onKeyDown(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800426 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800427 }
428
Romain Guy6fe2b222010-02-22 14:11:40 -0800429 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800430 public boolean onKeyUp(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800431 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800432 }
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700433
Alan Viverette62c79e92015-02-26 09:47:10 -0800434 /**
435 * Resolves whether a custom or default panel should be used. Removes the
436 * default panel if a custom panel should be used. If the resolved panel is
437 * a view stub, inflates before returning.
438 *
439 * @param customPanel the custom panel
440 * @param defaultPanel the default panel
441 * @return the panel to use
442 */
443 @Nullable
444 private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) {
445 if (customPanel == null) {
446 // Inflate the default panel, if needed.
447 if (defaultPanel instanceof ViewStub) {
448 defaultPanel = ((ViewStub) defaultPanel).inflate();
449 }
450
451 return (ViewGroup) defaultPanel;
452 }
453
454 // Remove the default panel entirely.
455 if (defaultPanel != null) {
456 final ViewParent parent = defaultPanel.getParent();
457 if (parent instanceof ViewGroup) {
458 ((ViewGroup) parent).removeView(defaultPanel);
459 }
460 }
461
462 // Inflate the custom panel, if needed.
463 if (customPanel instanceof ViewStub) {
464 customPanel = ((ViewStub) customPanel).inflate();
465 }
466
467 return (ViewGroup) customPanel;
468 }
469
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800470 private void setupView() {
Alan Viverette62c79e92015-02-26 09:47:10 -0800471 final View parentPanel = mWindow.findViewById(R.id.parentPanel);
472 final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel);
473 final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel);
474 final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel);
475
476 // Install custom content before setting up the title or buttons so
477 // that we can handle panel overrides.
478 final ViewGroup customPanel = (ViewGroup) parentPanel.findViewById(R.id.customPanel);
479 setupCustomContent(customPanel);
480
481 final View customTopPanel = customPanel.findViewById(R.id.topPanel);
482 final View customContentPanel = customPanel.findViewById(R.id.contentPanel);
483 final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel);
484
485 // Resolve the correct panels and remove the defaults, if needed.
486 final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel);
487 final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel);
488 final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel);
489
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800490 setupContent(contentPanel);
Alan Viverette62c79e92015-02-26 09:47:10 -0800491 setupButtons(buttonPanel);
492 setupTitle(topPanel);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700493
Alan Viverette62c79e92015-02-26 09:47:10 -0800494 final boolean hasCustomPanel = customPanel != null
495 && customPanel.getVisibility() != View.GONE;
496 final boolean hasTopPanel = topPanel != null
497 && topPanel.getVisibility() != View.GONE;
498 final boolean hasButtonPanel = buttonPanel != null
499 && buttonPanel.getVisibility() != View.GONE;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700500
Alan Viverette62c79e92015-02-26 09:47:10 -0800501 // Only display the text spacer if we don't have buttons.
502 if (!hasButtonPanel) {
503 if (contentPanel != null) {
504 final View spacer = contentPanel.findViewById(R.id.textSpacerNoButtons);
505 if (spacer != null) {
506 spacer.setVisibility(View.VISIBLE);
507 }
Adam Powelld044ad92014-09-15 14:30:23 -0700508 }
Dianne Hackbornef575752011-01-18 17:35:17 -0800509 mWindow.setCloseOnTouchOutsideIfNotSet(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800510 }
511
Alan Viverette62c79e92015-02-26 09:47:10 -0800512 if (hasTopPanel) {
Alan Viverette922e1c62015-05-05 17:18:27 -0700513 // Only clip scrolling content to padding if we have a title.
514 if (mScrollView != null) {
515 mScrollView.setClipToPadding(true);
516 }
517
518 // Only show the divider if we have a title.
Alan Viverette62c79e92015-02-26 09:47:10 -0800519 final View divider;
Alan Viverette922e1c62015-05-05 17:18:27 -0700520 if (mMessage != null || mListView != null || hasCustomPanel) {
Alan Viverette62c79e92015-02-26 09:47:10 -0800521 divider = topPanel.findViewById(R.id.titleDivider);
522 } else {
523 divider = topPanel.findViewById(R.id.titleDividerTop);
524 }
525
526 if (divider != null) {
527 divider.setVisibility(View.VISIBLE);
528 }
529 }
530
Alan Viverette922e1c62015-05-05 17:18:27 -0700531 // Update scroll indicators as needed.
532 if (!hasCustomPanel) {
533 final View content = mListView != null ? mListView : mScrollView;
534 if (content != null) {
535 final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
536 | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
537 content.setScrollIndicators(indicators,
538 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
539 }
540 }
541
Alan Viverette62c79e92015-02-26 09:47:10 -0800542 final TypedArray a = mContext.obtainStyledAttributes(
543 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
544 setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
545 hasTopPanel, hasCustomPanel, hasButtonPanel);
546 a.recycle();
547 }
548
549 private void setupCustomContent(ViewGroup customPanel) {
Alan Viveretteec186702013-12-05 11:10:31 -0800550 final View customView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800551 if (mView != null) {
Alan Viveretteec186702013-12-05 11:10:31 -0800552 customView = mView;
553 } else if (mViewLayoutResId != 0) {
554 final LayoutInflater inflater = LayoutInflater.from(mContext);
555 customView = inflater.inflate(mViewLayoutResId, customPanel, false);
556 } else {
557 customView = null;
558 }
559
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800560 final boolean hasCustomView = customView != null;
561 if (!hasCustomView || !canTextInput(customView)) {
Alan Viveretteec186702013-12-05 11:10:31 -0800562 mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
563 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
564 }
565
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800566 if (hasCustomView) {
Alan Viveretteec186702013-12-05 11:10:31 -0800567 final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
568 custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
569
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800570 if (mViewSpacingSpecified) {
Alan Viveretteec186702013-12-05 11:10:31 -0800571 custom.setPadding(
572 mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800573 }
Alan Viveretteec186702013-12-05 11:10:31 -0800574
The Android Open Source Project10592532009-03-18 17:39:46 -0700575 if (mListView != null) {
576 ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
577 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800578 } else {
Alan Viveretteec186702013-12-05 11:10:31 -0800579 customPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800580 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800581 }
582
Alan Viverette62c79e92015-02-26 09:47:10 -0800583 private void setupTitle(ViewGroup topPanel) {
Alan Viverette2e750a12016-01-29 12:53:26 -0500584 if (mCustomTitleView != null && mShowTitle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800585 // Add the custom title view directly to the topPanel layout
Alan Viverette2e750a12016-01-29 12:53:26 -0500586 final LayoutParams lp = new LayoutParams(
Alan Viverette154c2c22014-10-29 15:46:06 -0700587 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700588
Adam Powell5ea0bc12011-07-21 13:07:05 -0700589 topPanel.addView(mCustomTitleView, 0, lp);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700590
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800591 // Hide the title template
Alan Viverette2e750a12016-01-29 12:53:26 -0500592 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800593 titleTemplate.setVisibility(View.GONE);
594 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 mIconView = (ImageView) mWindow.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800596
Alan Viverettea1a68042014-04-28 13:30:08 -0700597 final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
Alan Viverette2e750a12016-01-29 12:53:26 -0500598 if (hasTextTitle && mShowTitle) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700599 // Display the title if a title is supplied, else hide it.
600 mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800601 mTitleView.setText(mTitle);
Alan Viverettea1a68042014-04-28 13:30:08 -0700602
603 // Do this last so that if the user has supplied any icons we
604 // use them instead of the default ones. If the user has
605 // specified 0 then make it disappear.
606 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800607 mIconView.setImageResource(mIconId);
608 } else if (mIcon != null) {
609 mIconView.setImageDrawable(mIcon);
Alan Viverettea1a68042014-04-28 13:30:08 -0700610 } else {
611 // Apply the padding from the icon to ensure the title is
612 // aligned correctly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613 mTitleView.setPadding(mIconView.getPaddingLeft(),
614 mIconView.getPaddingTop(),
615 mIconView.getPaddingRight(),
616 mIconView.getPaddingBottom());
617 mIconView.setVisibility(View.GONE);
618 }
619 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800620 // Hide the title template
Alan Viverettea1a68042014-04-28 13:30:08 -0700621 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800622 titleTemplate.setVisibility(View.GONE);
623 mIconView.setVisibility(View.GONE);
Martin Sjoline74e7e22010-06-22 21:42:11 +0200624 topPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800625 }
626 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800627 }
628
Alan Viverette154c2c22014-10-29 15:46:06 -0700629 private void setupContent(ViewGroup contentPanel) {
Alan Viverette62c79e92015-02-26 09:47:10 -0800630 mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800631 mScrollView.setFocusable(false);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700632
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800633 // Special case for users that only want to display a String
Alan Viverette62c79e92015-02-26 09:47:10 -0800634 mMessageView = (TextView) contentPanel.findViewById(R.id.message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800635 if (mMessageView == null) {
636 return;
637 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700638
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 if (mMessage != null) {
640 mMessageView.setText(mMessage);
641 } else {
642 mMessageView.setVisibility(View.GONE);
643 mScrollView.removeView(mMessageView);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700644
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800645 if (mListView != null) {
Alan Viverette35c3cb62014-10-30 13:51:21 -0700646 final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
647 final int childIndex = scrollParent.indexOfChild(mScrollView);
648 scrollParent.removeViewAt(childIndex);
649 scrollParent.addView(mListView, childIndex,
Alan Viverette154c2c22014-10-29 15:46:06 -0700650 new LayoutParams(MATCH_PARENT, MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800651 } else {
652 contentPanel.setVisibility(View.GONE);
653 }
654 }
Alan Viverette154c2c22014-10-29 15:46:06 -0700655 }
656
657 private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
658 if (upIndicator != null) {
659 upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
660 }
661 if (downIndicator != null) {
662 downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
663 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800664 }
665
Alan Viverette62c79e92015-02-26 09:47:10 -0800666 private void setupButtons(ViewGroup buttonPanel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800667 int BIT_BUTTON_POSITIVE = 1;
668 int BIT_BUTTON_NEGATIVE = 2;
669 int BIT_BUTTON_NEUTRAL = 4;
670 int whichButtons = 0;
Alan Viverette62c79e92015-02-26 09:47:10 -0800671 mButtonPositive = (Button) buttonPanel.findViewById(R.id.button1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800672 mButtonPositive.setOnClickListener(mButtonHandler);
673
674 if (TextUtils.isEmpty(mButtonPositiveText)) {
675 mButtonPositive.setVisibility(View.GONE);
676 } else {
677 mButtonPositive.setText(mButtonPositiveText);
678 mButtonPositive.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800679 whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
680 }
681
Alan Viverette62c79e92015-02-26 09:47:10 -0800682 mButtonNegative = (Button) buttonPanel.findViewById(R.id.button2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800683 mButtonNegative.setOnClickListener(mButtonHandler);
684
685 if (TextUtils.isEmpty(mButtonNegativeText)) {
686 mButtonNegative.setVisibility(View.GONE);
687 } else {
688 mButtonNegative.setText(mButtonNegativeText);
689 mButtonNegative.setVisibility(View.VISIBLE);
690
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800691 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
692 }
693
Alan Viverette62c79e92015-02-26 09:47:10 -0800694 mButtonNeutral = (Button) buttonPanel.findViewById(R.id.button3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800695 mButtonNeutral.setOnClickListener(mButtonHandler);
696
697 if (TextUtils.isEmpty(mButtonNeutralText)) {
698 mButtonNeutral.setVisibility(View.GONE);
699 } else {
700 mButtonNeutral.setText(mButtonNeutralText);
701 mButtonNeutral.setVisibility(View.VISIBLE);
702
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800703 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
704 }
705
Adam Powell3320dcd2010-11-05 20:06:02 -0700706 if (shouldCenterSingleButton(mContext)) {
707 /*
708 * If we only have 1 button it should be centered on the layout and
709 * expand to fill 50% of the available space.
710 */
711 if (whichButtons == BIT_BUTTON_POSITIVE) {
712 centerButton(mButtonPositive);
713 } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
Sangkyu Lee3079e582013-02-15 21:11:18 +0900714 centerButton(mButtonNegative);
Adam Powell3320dcd2010-11-05 20:06:02 -0700715 } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
716 centerButton(mButtonNeutral);
717 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800718 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700719
Alan Viverette62c79e92015-02-26 09:47:10 -0800720 final boolean hasButtons = whichButtons != 0;
721 if (!hasButtons) {
722 buttonPanel.setVisibility(View.GONE);
723 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800724 }
725
726 private void centerButton(Button button) {
727 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
728 params.gravity = Gravity.CENTER_HORIZONTAL;
729 params.weight = 0.5f;
730 button.setLayoutParams(params);
731 View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700732 if (leftSpacer != null) {
733 leftSpacer.setVisibility(View.VISIBLE);
734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700736 if (rightSpacer != null) {
737 rightSpacer.setVisibility(View.VISIBLE);
738 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800739 }
740
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800741 private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
742 View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700743 int fullDark = 0;
744 int topDark = 0;
745 int centerDark = 0;
746 int bottomDark = 0;
747 int fullBright = 0;
748 int topBright = 0;
749 int centerBright = 0;
750 int bottomBright = 0;
751 int bottomMedium = 0;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700752
753 // If the needsDefaultBackgrounds attribute is set, we know we're
754 // inheriting from a framework style.
755 final boolean needsDefaultBackgrounds = a.getBoolean(
756 R.styleable.AlertDialog_needsDefaultBackgrounds, true);
757 if (needsDefaultBackgrounds) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700758 fullDark = R.drawable.popup_full_dark;
759 topDark = R.drawable.popup_top_dark;
760 centerDark = R.drawable.popup_center_dark;
761 bottomDark = R.drawable.popup_bottom_dark;
762 fullBright = R.drawable.popup_full_bright;
763 topBright = R.drawable.popup_top_bright;
764 centerBright = R.drawable.popup_center_bright;
765 bottomBright = R.drawable.popup_bottom_bright;
766 bottomMedium = R.drawable.popup_bottom_medium;
767 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700768
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700769 topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700770 topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700771 centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700772 centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700773
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800774 /* We now set the background of all of the sections of the alert.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800775 * First collect together each section that is being displayed along
776 * with whether it is on a light or dark background, then run through
777 * them setting their backgrounds. This is complicated because we need
778 * to correctly use the full, top, middle, and bottom graphics depending
779 * on how many views they are and where they appear.
780 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800781
782 final View[] views = new View[4];
783 final boolean[] light = new boolean[4];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800784 View lastView = null;
785 boolean lastLight = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800786
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800787 int pos = 0;
788 if (hasTitle) {
789 views[pos] = topPanel;
790 light[pos] = false;
791 pos++;
792 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800793
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800794 /* The contentPanel displays either a custom text message or
795 * a ListView. If it's text we should use the dark background
796 * for ListView we should use the light background. If neither
797 * are there the contentPanel will be hidden so set it as null.
798 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800799 views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel;
Romain Guy6fe2b222010-02-22 14:11:40 -0800800 light[pos] = mListView != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 pos++;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800802
803 if (hasCustomView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 views[pos] = customPanel;
805 light[pos] = mForceInverseBackground;
806 pos++;
807 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800808
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800809 if (hasButtons) {
810 views[pos] = buttonPanel;
811 light[pos] = true;
812 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800813
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800814 boolean setView = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800815 for (pos = 0; pos < views.length; pos++) {
816 final View v = views[pos];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800817 if (v == null) {
818 continue;
819 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 if (lastView != null) {
822 if (!setView) {
823 lastView.setBackgroundResource(lastLight ? topBright : topDark);
824 } else {
825 lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
826 }
827 setView = true;
828 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800829
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800830 lastView = v;
831 lastLight = light[pos];
832 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800833
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 if (lastView != null) {
835 if (setView) {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700836 bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
837 bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
838 bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800839
840 // ListViews will use the Bright background, but buttons use the
841 // Medium background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800842 lastView.setBackgroundResource(
843 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
844 } else {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700845 fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
846 fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800847
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800848 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
849 }
850 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800851
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800852 final ListView listView = mListView;
853 if (listView != null && mAdapter != null) {
854 listView.setAdapter(mAdapter);
855 final int checkedItem = mCheckedItem;
856 if (checkedItem > -1) {
857 listView.setItemChecked(checkedItem, true);
858 listView.setSelection(checkedItem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 }
860 }
861 }
Romain Guy9c802c12009-03-25 15:07:31 -0700862
863 public static class RecycleListView extends ListView {
864 boolean mRecycleOnMeasure = true;
865
866 public RecycleListView(Context context) {
867 super(context);
868 }
869
870 public RecycleListView(Context context, AttributeSet attrs) {
871 super(context, attrs);
872 }
873
Alan Viverette617feb92013-09-09 18:09:13 -0700874 public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) {
875 super(context, attrs, defStyleAttr);
876 }
877
878 public RecycleListView(
879 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
880 super(context, attrs, defStyleAttr, defStyleRes);
Romain Guy9c802c12009-03-25 15:07:31 -0700881 }
882
883 @Override
884 protected boolean recycleOnMeasure() {
885 return mRecycleOnMeasure;
886 }
887 }
888
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 public static class AlertParams {
890 public final Context mContext;
891 public final LayoutInflater mInflater;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700892
Gilles Debunne076c7fb2010-10-04 12:00:02 -0700893 public int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800894 public Drawable mIcon;
blunden576e1df2012-09-10 23:29:04 +0200895 public int mIconAttrId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800896 public CharSequence mTitle;
897 public View mCustomTitleView;
898 public CharSequence mMessage;
899 public CharSequence mPositiveButtonText;
900 public DialogInterface.OnClickListener mPositiveButtonListener;
901 public CharSequence mNegativeButtonText;
902 public DialogInterface.OnClickListener mNegativeButtonListener;
903 public CharSequence mNeutralButtonText;
904 public DialogInterface.OnClickListener mNeutralButtonListener;
905 public boolean mCancelable;
906 public DialogInterface.OnCancelListener mOnCancelListener;
Adam Powell7f02dc52012-08-27 13:35:16 -0700907 public DialogInterface.OnDismissListener mOnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800908 public DialogInterface.OnKeyListener mOnKeyListener;
909 public CharSequence[] mItems;
910 public ListAdapter mAdapter;
911 public DialogInterface.OnClickListener mOnClickListener;
Alan Viveretteec186702013-12-05 11:10:31 -0800912 public int mViewLayoutResId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800913 public View mView;
914 public int mViewSpacingLeft;
915 public int mViewSpacingTop;
916 public int mViewSpacingRight;
917 public int mViewSpacingBottom;
918 public boolean mViewSpacingSpecified = false;
919 public boolean[] mCheckedItems;
920 public boolean mIsMultiChoice;
921 public boolean mIsSingleChoice;
922 public int mCheckedItem = -1;
923 public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
924 public Cursor mCursor;
925 public String mLabelColumn;
926 public String mIsCheckedColumn;
927 public boolean mForceInverseBackground;
928 public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
929 public OnPrepareListViewListener mOnPrepareListViewListener;
Romain Guy9c802c12009-03-25 15:07:31 -0700930 public boolean mRecycleOnMeasure = true;
931
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800932 /**
933 * Interface definition for a callback to be invoked before the ListView
934 * will be bound to an adapter.
935 */
936 public interface OnPrepareListViewListener {
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700937
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800938 /**
939 * Called before the ListView is bound to an adapter.
940 * @param listView The ListView that will be shown in the dialog.
941 */
942 void onPrepareListView(ListView listView);
943 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700944
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800945 public AlertParams(Context context) {
946 mContext = context;
947 mCancelable = true;
948 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
949 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700950
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 public void apply(AlertController dialog) {
952 if (mCustomTitleView != null) {
953 dialog.setCustomTitle(mCustomTitleView);
954 } else {
955 if (mTitle != null) {
956 dialog.setTitle(mTitle);
957 }
958 if (mIcon != null) {
959 dialog.setIcon(mIcon);
960 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -0800961 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800962 dialog.setIcon(mIconId);
963 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -0800964 if (mIconAttrId != 0) {
blunden576e1df2012-09-10 23:29:04 +0200965 dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
966 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800967 }
968 if (mMessage != null) {
969 dialog.setMessage(mMessage);
970 }
971 if (mPositiveButtonText != null) {
972 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
973 mPositiveButtonListener, null);
974 }
975 if (mNegativeButtonText != null) {
976 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
977 mNegativeButtonListener, null);
978 }
979 if (mNeutralButtonText != null) {
980 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
981 mNeutralButtonListener, null);
982 }
983 if (mForceInverseBackground) {
984 dialog.setInverseBackgroundForced(true);
985 }
986 // For a list, the client can either supply an array of items or an
987 // adapter or a cursor
988 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
989 createListView(dialog);
990 }
991 if (mView != null) {
992 if (mViewSpacingSpecified) {
993 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
994 mViewSpacingBottom);
995 } else {
996 dialog.setView(mView);
997 }
Alan Viveretteec186702013-12-05 11:10:31 -0800998 } else if (mViewLayoutResId != 0) {
999 dialog.setView(mViewLayoutResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001000 }
Alan Viveretteec186702013-12-05 11:10:31 -08001001
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 /*
1003 dialog.setCancelable(mCancelable);
1004 dialog.setOnCancelListener(mOnCancelListener);
1005 if (mOnKeyListener != null) {
1006 dialog.setOnKeyListener(mOnKeyListener);
1007 }
1008 */
1009 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001010
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001011 private void createListView(final AlertController dialog) {
Alan Viverette89900832015-04-08 12:45:54 -07001012 final RecycleListView listView =
1013 (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
1014 final ListAdapter adapter;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001015
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001016 if (mIsMultiChoice) {
1017 if (mCursor == null) {
1018 adapter = new ArrayAdapter<CharSequence>(
Adam Powellfcca00a2010-11-30 21:26:29 -08001019 mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001020 @Override
1021 public View getView(int position, View convertView, ViewGroup parent) {
1022 View view = super.getView(position, convertView, parent);
1023 if (mCheckedItems != null) {
1024 boolean isItemChecked = mCheckedItems[position];
1025 if (isItemChecked) {
1026 listView.setItemChecked(position, true);
1027 }
1028 }
1029 return view;
1030 }
1031 };
1032 } else {
1033 adapter = new CursorAdapter(mContext, mCursor, false) {
1034 private final int mLabelIndex;
1035 private final int mIsCheckedIndex;
1036
1037 {
1038 final Cursor cursor = getCursor();
1039 mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
1040 mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
1041 }
1042
1043 @Override
1044 public void bindView(View view, Context context, Cursor cursor) {
1045 CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
1046 text.setText(cursor.getString(mLabelIndex));
1047 listView.setItemChecked(cursor.getPosition(),
1048 cursor.getInt(mIsCheckedIndex) == 1);
1049 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001050
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001051 @Override
1052 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001053 return mInflater.inflate(dialog.mMultiChoiceItemLayout,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001054 parent, false);
1055 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001056
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001057 };
1058 }
1059 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001060 final int layout;
1061 if (mIsSingleChoice) {
1062 layout = dialog.mSingleChoiceItemLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001063 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001064 layout = dialog.mListItemLayout;
1065 }
1066
1067 if (mCursor != null) {
1068 adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
1069 new String[] { mLabelColumn }, new int[] { R.id.text1 });
1070 } else if (mAdapter != null) {
1071 adapter = mAdapter;
1072 } else {
1073 adapter = new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001074 }
1075 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001076
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001077 if (mOnPrepareListViewListener != null) {
1078 mOnPrepareListViewListener.onPrepareListView(listView);
1079 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001080
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001081 /* Don't directly set the adapter on the ListView as we might
1082 * want to add a footer to the ListView later.
1083 */
1084 dialog.mAdapter = adapter;
1085 dialog.mCheckedItem = mCheckedItem;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001086
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001087 if (mOnClickListener != null) {
1088 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001089 @Override
1090 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 mOnClickListener.onClick(dialog.mDialogInterface, position);
1092 if (!mIsSingleChoice) {
1093 dialog.mDialogInterface.dismiss();
1094 }
1095 }
1096 });
1097 } else if (mOnCheckboxClickListener != null) {
1098 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001099 @Override
1100 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001101 if (mCheckedItems != null) {
1102 mCheckedItems[position] = listView.isItemChecked(position);
1103 }
1104 mOnCheckboxClickListener.onClick(
1105 dialog.mDialogInterface, position, listView.isItemChecked(position));
1106 }
1107 });
1108 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001109
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001110 // Attach a given OnItemSelectedListener to the ListView
1111 if (mOnItemSelectedListener != null) {
1112 listView.setOnItemSelectedListener(mOnItemSelectedListener);
1113 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 if (mIsSingleChoice) {
1116 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
1117 } else if (mIsMultiChoice) {
1118 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
1119 }
Romain Guy9c802c12009-03-25 15:07:31 -07001120 listView.mRecycleOnMeasure = mRecycleOnMeasure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001121 dialog.mListView = listView;
1122 }
1123 }
1124
Alan Viverette2356c5e2014-05-22 22:43:59 -07001125 private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
1126 public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
1127 CharSequence[] objects) {
1128 super(context, resource, textViewResourceId, objects);
1129 }
1130
1131 @Override
1132 public boolean hasStableIds() {
1133 return true;
1134 }
1135
1136 @Override
1137 public long getItemId(int position) {
1138 return position;
1139 }
1140 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001141}