blob: b7ac6008a1fdc04bf0d32eb2e793869afd28b8ad [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 Viveretted6bfb822016-03-30 20:56:30 -0400519 View divider = null;
Alan Viverette922e1c62015-05-05 17:18:27 -0700520 if (mMessage != null || mListView != null || hasCustomPanel) {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400521 if (!hasCustomPanel) {
522 divider = topPanel.findViewById(R.id.titleDividerNoCustom);
523 }
524 if (divider == null) {
525 divider = topPanel.findViewById(R.id.titleDivider);
526 }
527
Alan Viverette62c79e92015-02-26 09:47:10 -0800528 } else {
529 divider = topPanel.findViewById(R.id.titleDividerTop);
530 }
531
532 if (divider != null) {
533 divider.setVisibility(View.VISIBLE);
534 }
Alan Viveretted6bfb822016-03-30 20:56:30 -0400535 } else {
536 if (contentPanel != null) {
537 final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle);
538 if (spacer != null) {
539 spacer.setVisibility(View.VISIBLE);
540 }
541 }
542 }
543
544 if (mListView instanceof RecycleListView) {
545 ((RecycleListView) mListView).setHasDecor(hasTopPanel, hasButtonPanel);
Alan Viverette62c79e92015-02-26 09:47:10 -0800546 }
547
Alan Viverette922e1c62015-05-05 17:18:27 -0700548 // Update scroll indicators as needed.
549 if (!hasCustomPanel) {
550 final View content = mListView != null ? mListView : mScrollView;
551 if (content != null) {
552 final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0)
553 | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0);
554 content.setScrollIndicators(indicators,
555 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM);
556 }
557 }
558
Alan Viverette62c79e92015-02-26 09:47:10 -0800559 final TypedArray a = mContext.obtainStyledAttributes(
560 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0);
561 setBackground(a, topPanel, contentPanel, customPanel, buttonPanel,
562 hasTopPanel, hasCustomPanel, hasButtonPanel);
563 a.recycle();
564 }
565
566 private void setupCustomContent(ViewGroup customPanel) {
Alan Viveretteec186702013-12-05 11:10:31 -0800567 final View customView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800568 if (mView != null) {
Alan Viveretteec186702013-12-05 11:10:31 -0800569 customView = mView;
570 } else if (mViewLayoutResId != 0) {
571 final LayoutInflater inflater = LayoutInflater.from(mContext);
572 customView = inflater.inflate(mViewLayoutResId, customPanel, false);
573 } else {
574 customView = null;
575 }
576
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800577 final boolean hasCustomView = customView != null;
578 if (!hasCustomView || !canTextInput(customView)) {
Alan Viveretteec186702013-12-05 11:10:31 -0800579 mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
580 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
581 }
582
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800583 if (hasCustomView) {
Alan Viveretteec186702013-12-05 11:10:31 -0800584 final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
585 custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
586
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800587 if (mViewSpacingSpecified) {
Alan Viveretteec186702013-12-05 11:10:31 -0800588 custom.setPadding(
589 mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800590 }
Alan Viveretteec186702013-12-05 11:10:31 -0800591
The Android Open Source Project10592532009-03-18 17:39:46 -0700592 if (mListView != null) {
593 ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
594 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800595 } else {
Alan Viveretteec186702013-12-05 11:10:31 -0800596 customPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800597 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800598 }
599
Alan Viverette62c79e92015-02-26 09:47:10 -0800600 private void setupTitle(ViewGroup topPanel) {
Alan Viverette2e750a12016-01-29 12:53:26 -0500601 if (mCustomTitleView != null && mShowTitle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800602 // Add the custom title view directly to the topPanel layout
Alan Viverette2e750a12016-01-29 12:53:26 -0500603 final LayoutParams lp = new LayoutParams(
Alan Viverette154c2c22014-10-29 15:46:06 -0700604 LayoutParams.MATCH_PARENT, LayoutParams.WRAP_CONTENT);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700605
Adam Powell5ea0bc12011-07-21 13:07:05 -0700606 topPanel.addView(mCustomTitleView, 0, lp);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700607
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800608 // Hide the title template
Alan Viverette2e750a12016-01-29 12:53:26 -0500609 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800610 titleTemplate.setVisibility(View.GONE);
611 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800612 mIconView = (ImageView) mWindow.findViewById(R.id.icon);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800613
Alan Viverettea1a68042014-04-28 13:30:08 -0700614 final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
Alan Viverette2e750a12016-01-29 12:53:26 -0500615 if (hasTextTitle && mShowTitle) {
Alan Viverettea1a68042014-04-28 13:30:08 -0700616 // Display the title if a title is supplied, else hide it.
617 mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800618 mTitleView.setText(mTitle);
Alan Viverettea1a68042014-04-28 13:30:08 -0700619
620 // Do this last so that if the user has supplied any icons we
621 // use them instead of the default ones. If the user has
622 // specified 0 then make it disappear.
623 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800624 mIconView.setImageResource(mIconId);
625 } else if (mIcon != null) {
626 mIconView.setImageDrawable(mIcon);
Alan Viverettea1a68042014-04-28 13:30:08 -0700627 } else {
628 // Apply the padding from the icon to ensure the title is
629 // aligned correctly.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800630 mTitleView.setPadding(mIconView.getPaddingLeft(),
631 mIconView.getPaddingTop(),
632 mIconView.getPaddingRight(),
633 mIconView.getPaddingBottom());
634 mIconView.setVisibility(View.GONE);
635 }
636 } else {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800637 // Hide the title template
Alan Viverettea1a68042014-04-28 13:30:08 -0700638 final View titleTemplate = mWindow.findViewById(R.id.title_template);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800639 titleTemplate.setVisibility(View.GONE);
640 mIconView.setVisibility(View.GONE);
Martin Sjoline74e7e22010-06-22 21:42:11 +0200641 topPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800642 }
643 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800644 }
645
Alan Viverette154c2c22014-10-29 15:46:06 -0700646 private void setupContent(ViewGroup contentPanel) {
Alan Viverette62c79e92015-02-26 09:47:10 -0800647 mScrollView = (ScrollView) contentPanel.findViewById(R.id.scrollView);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800648 mScrollView.setFocusable(false);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700649
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800650 // Special case for users that only want to display a String
Alan Viverette62c79e92015-02-26 09:47:10 -0800651 mMessageView = (TextView) contentPanel.findViewById(R.id.message);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800652 if (mMessageView == null) {
653 return;
654 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700655
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800656 if (mMessage != null) {
657 mMessageView.setText(mMessage);
658 } else {
659 mMessageView.setVisibility(View.GONE);
660 mScrollView.removeView(mMessageView);
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700661
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800662 if (mListView != null) {
Alan Viverette35c3cb62014-10-30 13:51:21 -0700663 final ViewGroup scrollParent = (ViewGroup) mScrollView.getParent();
664 final int childIndex = scrollParent.indexOfChild(mScrollView);
665 scrollParent.removeViewAt(childIndex);
666 scrollParent.addView(mListView, childIndex,
Alan Viverette154c2c22014-10-29 15:46:06 -0700667 new LayoutParams(MATCH_PARENT, MATCH_PARENT));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800668 } else {
669 contentPanel.setVisibility(View.GONE);
670 }
671 }
Alan Viverette154c2c22014-10-29 15:46:06 -0700672 }
673
674 private static void manageScrollIndicators(View v, View upIndicator, View downIndicator) {
675 if (upIndicator != null) {
676 upIndicator.setVisibility(v.canScrollVertically(-1) ? View.VISIBLE : View.INVISIBLE);
677 }
678 if (downIndicator != null) {
679 downIndicator.setVisibility(v.canScrollVertically(1) ? View.VISIBLE : View.INVISIBLE);
680 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800681 }
682
Alan Viverette62c79e92015-02-26 09:47:10 -0800683 private void setupButtons(ViewGroup buttonPanel) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800684 int BIT_BUTTON_POSITIVE = 1;
685 int BIT_BUTTON_NEGATIVE = 2;
686 int BIT_BUTTON_NEUTRAL = 4;
687 int whichButtons = 0;
Alan Viverette62c79e92015-02-26 09:47:10 -0800688 mButtonPositive = (Button) buttonPanel.findViewById(R.id.button1);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800689 mButtonPositive.setOnClickListener(mButtonHandler);
690
691 if (TextUtils.isEmpty(mButtonPositiveText)) {
692 mButtonPositive.setVisibility(View.GONE);
693 } else {
694 mButtonPositive.setText(mButtonPositiveText);
695 mButtonPositive.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800696 whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
697 }
698
Alan Viverette62c79e92015-02-26 09:47:10 -0800699 mButtonNegative = (Button) buttonPanel.findViewById(R.id.button2);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800700 mButtonNegative.setOnClickListener(mButtonHandler);
701
702 if (TextUtils.isEmpty(mButtonNegativeText)) {
703 mButtonNegative.setVisibility(View.GONE);
704 } else {
705 mButtonNegative.setText(mButtonNegativeText);
706 mButtonNegative.setVisibility(View.VISIBLE);
707
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800708 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
709 }
710
Alan Viverette62c79e92015-02-26 09:47:10 -0800711 mButtonNeutral = (Button) buttonPanel.findViewById(R.id.button3);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800712 mButtonNeutral.setOnClickListener(mButtonHandler);
713
714 if (TextUtils.isEmpty(mButtonNeutralText)) {
715 mButtonNeutral.setVisibility(View.GONE);
716 } else {
717 mButtonNeutral.setText(mButtonNeutralText);
718 mButtonNeutral.setVisibility(View.VISIBLE);
719
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800720 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
721 }
722
Adam Powell3320dcd2010-11-05 20:06:02 -0700723 if (shouldCenterSingleButton(mContext)) {
724 /*
725 * If we only have 1 button it should be centered on the layout and
726 * expand to fill 50% of the available space.
727 */
728 if (whichButtons == BIT_BUTTON_POSITIVE) {
729 centerButton(mButtonPositive);
730 } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
Sangkyu Lee3079e582013-02-15 21:11:18 +0900731 centerButton(mButtonNegative);
Adam Powell3320dcd2010-11-05 20:06:02 -0700732 } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
733 centerButton(mButtonNeutral);
734 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800735 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700736
Alan Viverette62c79e92015-02-26 09:47:10 -0800737 final boolean hasButtons = whichButtons != 0;
738 if (!hasButtons) {
739 buttonPanel.setVisibility(View.GONE);
740 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800741 }
742
743 private void centerButton(Button button) {
744 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
745 params.gravity = Gravity.CENTER_HORIZONTAL;
746 params.weight = 0.5f;
747 button.setLayoutParams(params);
748 View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700749 if (leftSpacer != null) {
750 leftSpacer.setVisibility(View.VISIBLE);
751 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800752 View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700753 if (rightSpacer != null) {
754 rightSpacer.setVisibility(View.VISIBLE);
755 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800756 }
757
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800758 private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel,
759 View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700760 int fullDark = 0;
761 int topDark = 0;
762 int centerDark = 0;
763 int bottomDark = 0;
764 int fullBright = 0;
765 int topBright = 0;
766 int centerBright = 0;
767 int bottomBright = 0;
768 int bottomMedium = 0;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700769
770 // If the needsDefaultBackgrounds attribute is set, we know we're
771 // inheriting from a framework style.
772 final boolean needsDefaultBackgrounds = a.getBoolean(
773 R.styleable.AlertDialog_needsDefaultBackgrounds, true);
774 if (needsDefaultBackgrounds) {
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700775 fullDark = R.drawable.popup_full_dark;
776 topDark = R.drawable.popup_top_dark;
777 centerDark = R.drawable.popup_center_dark;
778 bottomDark = R.drawable.popup_bottom_dark;
779 fullBright = R.drawable.popup_full_bright;
780 topBright = R.drawable.popup_top_bright;
781 centerBright = R.drawable.popup_center_bright;
782 bottomBright = R.drawable.popup_bottom_bright;
783 bottomMedium = R.drawable.popup_bottom_medium;
784 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700785
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700786 topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700787 topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700788 centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright);
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700789 centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark);
Vinod Krishnan119ba2c2014-05-05 14:58:15 -0700790
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800791 /* We now set the background of all of the sections of the alert.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800792 * First collect together each section that is being displayed along
793 * with whether it is on a light or dark background, then run through
794 * them setting their backgrounds. This is complicated because we need
795 * to correctly use the full, top, middle, and bottom graphics depending
796 * on how many views they are and where they appear.
797 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800798
799 final View[] views = new View[4];
800 final boolean[] light = new boolean[4];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800801 View lastView = null;
802 boolean lastLight = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800803
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800804 int pos = 0;
805 if (hasTitle) {
806 views[pos] = topPanel;
807 light[pos] = false;
808 pos++;
809 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800810
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800811 /* The contentPanel displays either a custom text message or
812 * a ListView. If it's text we should use the dark background
813 * for ListView we should use the light background. If neither
814 * are there the contentPanel will be hidden so set it as null.
815 */
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800816 views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel;
Romain Guy6fe2b222010-02-22 14:11:40 -0800817 light[pos] = mListView != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800818 pos++;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800819
820 if (hasCustomView) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 views[pos] = customPanel;
822 light[pos] = mForceInverseBackground;
823 pos++;
824 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800825
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800826 if (hasButtons) {
827 views[pos] = buttonPanel;
828 light[pos] = true;
829 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800830
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800831 boolean setView = false;
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800832 for (pos = 0; pos < views.length; pos++) {
833 final View v = views[pos];
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800834 if (v == null) {
835 continue;
836 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800837
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800838 if (lastView != null) {
839 if (!setView) {
840 lastView.setBackgroundResource(lastLight ? topBright : topDark);
841 } else {
842 lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
843 }
844 setView = true;
845 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800846
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800847 lastView = v;
848 lastLight = light[pos];
849 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800850
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800851 if (lastView != null) {
852 if (setView) {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700853 bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright);
854 bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium);
855 bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800856
857 // ListViews will use the Bright background, but buttons use the
858 // Medium background.
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800859 lastView.setBackgroundResource(
860 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
861 } else {
Vinod Krishnan4a3735d2014-05-09 12:04:35 -0700862 fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright);
863 fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark);
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800864
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800865 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
866 }
867 }
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800868
Alan Viverettec3cf19a2014-02-18 18:29:11 -0800869 final ListView listView = mListView;
870 if (listView != null && mAdapter != null) {
871 listView.setAdapter(mAdapter);
872 final int checkedItem = mCheckedItem;
873 if (checkedItem > -1) {
874 listView.setItemChecked(checkedItem, true);
875 listView.setSelection(checkedItem);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800876 }
877 }
878 }
Romain Guy9c802c12009-03-25 15:07:31 -0700879
880 public static class RecycleListView extends ListView {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400881 private final int mPaddingTopNoTitle;
882 private final int mPaddingBottomNoButtons;
883
Romain Guy9c802c12009-03-25 15:07:31 -0700884 boolean mRecycleOnMeasure = true;
885
886 public RecycleListView(Context context) {
Alan Viveretted6bfb822016-03-30 20:56:30 -0400887 this(context, null);
Romain Guy9c802c12009-03-25 15:07:31 -0700888 }
889
890 public RecycleListView(Context context, AttributeSet attrs) {
891 super(context, attrs);
Alan Viveretted6bfb822016-03-30 20:56:30 -0400892
893 final TypedArray ta = context.obtainStyledAttributes(
894 attrs, R.styleable.RecycleListView);
895 mPaddingBottomNoButtons = ta.getDimensionPixelOffset(
896 R.styleable.RecycleListView_paddingBottomNoButtons, -1);
897 mPaddingTopNoTitle = ta.getDimensionPixelOffset(
898 R.styleable.RecycleListView_paddingTopNoTitle, -1);
Romain Guy9c802c12009-03-25 15:07:31 -0700899 }
900
Alan Viveretted6bfb822016-03-30 20:56:30 -0400901 public void setHasDecor(boolean hasTitle, boolean hasButtons) {
902 if (!hasButtons || !hasTitle) {
903 final int paddingLeft = getPaddingLeft();
904 final int paddingTop = hasTitle ? getPaddingTop() : mPaddingTopNoTitle;
905 final int paddingRight = getPaddingRight();
906 final int paddingBottom = hasButtons ? getPaddingBottom() : mPaddingBottomNoButtons;
907 setPadding(paddingLeft, paddingTop, paddingRight, paddingBottom);
908 }
Romain Guy9c802c12009-03-25 15:07:31 -0700909 }
910
911 @Override
912 protected boolean recycleOnMeasure() {
913 return mRecycleOnMeasure;
914 }
915 }
916
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800917 public static class AlertParams {
918 public final Context mContext;
919 public final LayoutInflater mInflater;
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700920
Gilles Debunne076c7fb2010-10-04 12:00:02 -0700921 public int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800922 public Drawable mIcon;
blunden576e1df2012-09-10 23:29:04 +0200923 public int mIconAttrId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800924 public CharSequence mTitle;
925 public View mCustomTitleView;
926 public CharSequence mMessage;
927 public CharSequence mPositiveButtonText;
928 public DialogInterface.OnClickListener mPositiveButtonListener;
929 public CharSequence mNegativeButtonText;
930 public DialogInterface.OnClickListener mNegativeButtonListener;
931 public CharSequence mNeutralButtonText;
932 public DialogInterface.OnClickListener mNeutralButtonListener;
933 public boolean mCancelable;
934 public DialogInterface.OnCancelListener mOnCancelListener;
Adam Powell7f02dc52012-08-27 13:35:16 -0700935 public DialogInterface.OnDismissListener mOnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800936 public DialogInterface.OnKeyListener mOnKeyListener;
937 public CharSequence[] mItems;
938 public ListAdapter mAdapter;
939 public DialogInterface.OnClickListener mOnClickListener;
Alan Viveretteec186702013-12-05 11:10:31 -0800940 public int mViewLayoutResId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800941 public View mView;
942 public int mViewSpacingLeft;
943 public int mViewSpacingTop;
944 public int mViewSpacingRight;
945 public int mViewSpacingBottom;
946 public boolean mViewSpacingSpecified = false;
947 public boolean[] mCheckedItems;
948 public boolean mIsMultiChoice;
949 public boolean mIsSingleChoice;
950 public int mCheckedItem = -1;
951 public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
952 public Cursor mCursor;
953 public String mLabelColumn;
954 public String mIsCheckedColumn;
955 public boolean mForceInverseBackground;
956 public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
957 public OnPrepareListViewListener mOnPrepareListViewListener;
Romain Guy9c802c12009-03-25 15:07:31 -0700958 public boolean mRecycleOnMeasure = true;
959
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800960 /**
961 * Interface definition for a callback to be invoked before the ListView
962 * will be bound to an adapter.
963 */
964 public interface OnPrepareListViewListener {
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700965
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800966 /**
967 * Called before the ListView is bound to an adapter.
968 * @param listView The ListView that will be shown in the dialog.
969 */
970 void onPrepareListView(ListView listView);
971 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700972
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800973 public AlertParams(Context context) {
974 mContext = context;
975 mCancelable = true;
976 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
977 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -0700978
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800979 public void apply(AlertController dialog) {
980 if (mCustomTitleView != null) {
981 dialog.setCustomTitle(mCustomTitleView);
982 } else {
983 if (mTitle != null) {
984 dialog.setTitle(mTitle);
985 }
986 if (mIcon != null) {
987 dialog.setIcon(mIcon);
988 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -0800989 if (mIconId != 0) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800990 dialog.setIcon(mIconId);
991 }
Alan Viverette5c2d8f72015-01-05 12:56:45 -0800992 if (mIconAttrId != 0) {
blunden576e1df2012-09-10 23:29:04 +0200993 dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
994 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800995 }
996 if (mMessage != null) {
997 dialog.setMessage(mMessage);
998 }
999 if (mPositiveButtonText != null) {
1000 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
1001 mPositiveButtonListener, null);
1002 }
1003 if (mNegativeButtonText != null) {
1004 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
1005 mNegativeButtonListener, null);
1006 }
1007 if (mNeutralButtonText != null) {
1008 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
1009 mNeutralButtonListener, null);
1010 }
1011 if (mForceInverseBackground) {
1012 dialog.setInverseBackgroundForced(true);
1013 }
1014 // For a list, the client can either supply an array of items or an
1015 // adapter or a cursor
1016 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
1017 createListView(dialog);
1018 }
1019 if (mView != null) {
1020 if (mViewSpacingSpecified) {
1021 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
1022 mViewSpacingBottom);
1023 } else {
1024 dialog.setView(mView);
1025 }
Alan Viveretteec186702013-12-05 11:10:31 -08001026 } else if (mViewLayoutResId != 0) {
1027 dialog.setView(mViewLayoutResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001028 }
Alan Viveretteec186702013-12-05 11:10:31 -08001029
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001030 /*
1031 dialog.setCancelable(mCancelable);
1032 dialog.setOnCancelListener(mOnCancelListener);
1033 if (mOnKeyListener != null) {
1034 dialog.setOnKeyListener(mOnKeyListener);
1035 }
1036 */
1037 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001038
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001039 private void createListView(final AlertController dialog) {
Alan Viverette89900832015-04-08 12:45:54 -07001040 final RecycleListView listView =
1041 (RecycleListView) mInflater.inflate(dialog.mListLayout, null);
1042 final ListAdapter adapter;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001043
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001044 if (mIsMultiChoice) {
1045 if (mCursor == null) {
1046 adapter = new ArrayAdapter<CharSequence>(
Adam Powellfcca00a2010-11-30 21:26:29 -08001047 mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001048 @Override
1049 public View getView(int position, View convertView, ViewGroup parent) {
1050 View view = super.getView(position, convertView, parent);
1051 if (mCheckedItems != null) {
1052 boolean isItemChecked = mCheckedItems[position];
1053 if (isItemChecked) {
1054 listView.setItemChecked(position, true);
1055 }
1056 }
1057 return view;
1058 }
1059 };
1060 } else {
1061 adapter = new CursorAdapter(mContext, mCursor, false) {
1062 private final int mLabelIndex;
1063 private final int mIsCheckedIndex;
1064
1065 {
1066 final Cursor cursor = getCursor();
1067 mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
1068 mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
1069 }
1070
1071 @Override
1072 public void bindView(View view, Context context, Cursor cursor) {
1073 CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
1074 text.setText(cursor.getString(mLabelIndex));
1075 listView.setItemChecked(cursor.getPosition(),
1076 cursor.getInt(mIsCheckedIndex) == 1);
1077 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001078
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001079 @Override
1080 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Adam Powellfcca00a2010-11-30 21:26:29 -08001081 return mInflater.inflate(dialog.mMultiChoiceItemLayout,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001082 parent, false);
1083 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001084
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001085 };
1086 }
1087 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001088 final int layout;
1089 if (mIsSingleChoice) {
1090 layout = dialog.mSingleChoiceItemLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001091 } else {
Alan Viverette89900832015-04-08 12:45:54 -07001092 layout = dialog.mListItemLayout;
1093 }
1094
1095 if (mCursor != null) {
1096 adapter = new SimpleCursorAdapter(mContext, layout, mCursor,
1097 new String[] { mLabelColumn }, new int[] { R.id.text1 });
1098 } else if (mAdapter != null) {
1099 adapter = mAdapter;
1100 } else {
1101 adapter = new CheckedItemAdapter(mContext, layout, R.id.text1, mItems);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001102 }
1103 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001104
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001105 if (mOnPrepareListViewListener != null) {
1106 mOnPrepareListViewListener.onPrepareListView(listView);
1107 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001108
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001109 /* Don't directly set the adapter on the ListView as we might
1110 * want to add a footer to the ListView later.
1111 */
1112 dialog.mAdapter = adapter;
1113 dialog.mCheckedItem = mCheckedItem;
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001114
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001115 if (mOnClickListener != null) {
1116 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001117 @Override
1118 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001119 mOnClickListener.onClick(dialog.mDialogInterface, position);
1120 if (!mIsSingleChoice) {
1121 dialog.mDialogInterface.dismiss();
1122 }
1123 }
1124 });
1125 } else if (mOnCheckboxClickListener != null) {
1126 listView.setOnItemClickListener(new OnItemClickListener() {
Alan Viverettec3cf19a2014-02-18 18:29:11 -08001127 @Override
1128 public void onItemClick(AdapterView<?> parent, View v, int position, long id) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001129 if (mCheckedItems != null) {
1130 mCheckedItems[position] = listView.isItemChecked(position);
1131 }
1132 mOnCheckboxClickListener.onClick(
1133 dialog.mDialogInterface, position, listView.isItemChecked(position));
1134 }
1135 });
1136 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001137
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001138 // Attach a given OnItemSelectedListener to the ListView
1139 if (mOnItemSelectedListener != null) {
1140 listView.setOnItemSelectedListener(mOnItemSelectedListener);
1141 }
Alan Viveretteb17c6c12014-09-03 16:27:51 -07001142
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001143 if (mIsSingleChoice) {
1144 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
1145 } else if (mIsMultiChoice) {
1146 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
1147 }
Romain Guy9c802c12009-03-25 15:07:31 -07001148 listView.mRecycleOnMeasure = mRecycleOnMeasure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001149 dialog.mListView = listView;
1150 }
1151 }
1152
Alan Viverette2356c5e2014-05-22 22:43:59 -07001153 private static class CheckedItemAdapter extends ArrayAdapter<CharSequence> {
1154 public CheckedItemAdapter(Context context, int resource, int textViewResourceId,
1155 CharSequence[] objects) {
1156 super(context, resource, textViewResourceId, objects);
1157 }
1158
1159 @Override
1160 public boolean hasStableIds() {
1161 return true;
1162 }
1163
1164 @Override
1165 public long getItemId(int position) {
1166 return position;
1167 }
1168 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001169}