blob: a7a7e7ed2bf87b67784c6b1b42d1061e3b3dabc0 [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
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080023import android.app.AlertDialog;
24import android.content.Context;
25import android.content.DialogInterface;
26import android.content.res.TypedArray;
27import android.database.Cursor;
28import android.graphics.drawable.Drawable;
29import android.os.Handler;
30import android.os.Message;
31import android.text.TextUtils;
Romain Guy6fe2b222010-02-22 14:11:40 -080032import android.util.AttributeSet;
Adam Powell3320dcd2010-11-05 20:06:02 -070033import android.util.TypedValue;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080034import android.view.Gravity;
35import android.view.KeyEvent;
36import android.view.LayoutInflater;
37import android.view.View;
38import android.view.ViewGroup;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070039import android.view.ViewGroup.LayoutParams;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080040import android.view.Window;
41import android.view.WindowManager;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080042import android.widget.AdapterView;
Gilles Debunne076c7fb2010-10-04 12:00:02 -070043import android.widget.AdapterView.OnItemClickListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080044import android.widget.ArrayAdapter;
45import android.widget.Button;
46import android.widget.CheckedTextView;
47import android.widget.CursorAdapter;
48import android.widget.FrameLayout;
49import android.widget.ImageView;
50import android.widget.LinearLayout;
51import android.widget.ListAdapter;
52import android.widget.ListView;
53import android.widget.ScrollView;
54import android.widget.SimpleCursorAdapter;
55import android.widget.TextView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080056
57import java.lang.ref.WeakReference;
58
59public class AlertController {
60
61 private final Context mContext;
62 private final DialogInterface mDialogInterface;
63 private final Window mWindow;
64
65 private CharSequence mTitle;
66
67 private CharSequence mMessage;
68
69 private ListView mListView;
70
71 private View mView;
72
Alan Viveretteec186702013-12-05 11:10:31 -080073 private int mViewLayoutResId;
74
The Android Open Source Project9066cfe2009-03-03 19:31:44 -080075 private int mViewSpacingLeft;
76
77 private int mViewSpacingTop;
78
79 private int mViewSpacingRight;
80
81 private int mViewSpacingBottom;
82
83 private boolean mViewSpacingSpecified = false;
84
85 private Button mButtonPositive;
86
87 private CharSequence mButtonPositiveText;
88
89 private Message mButtonPositiveMessage;
90
91 private Button mButtonNegative;
92
93 private CharSequence mButtonNegativeText;
94
95 private Message mButtonNegativeMessage;
96
97 private Button mButtonNeutral;
98
99 private CharSequence mButtonNeutralText;
100
101 private Message mButtonNeutralMessage;
102
103 private ScrollView mScrollView;
104
105 private int mIconId = -1;
106
107 private Drawable mIcon;
108
109 private ImageView mIconView;
110
111 private TextView mTitleView;
112
113 private TextView mMessageView;
114
115 private View mCustomTitleView;
116
117 private boolean mForceInverseBackground;
118
119 private ListAdapter mAdapter;
120
121 private int mCheckedItem = -1;
122
Adam Powellfcca00a2010-11-30 21:26:29 -0800123 private int mAlertDialogLayout;
124 private int mListLayout;
125 private int mMultiChoiceItemLayout;
126 private int mSingleChoiceItemLayout;
127 private int mListItemLayout;
128
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800129 private Handler mHandler;
130
131 View.OnClickListener mButtonHandler = new View.OnClickListener() {
132 public void onClick(View v) {
133 Message m = null;
134 if (v == mButtonPositive && mButtonPositiveMessage != null) {
135 m = Message.obtain(mButtonPositiveMessage);
136 } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
137 m = Message.obtain(mButtonNegativeMessage);
138 } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
139 m = Message.obtain(mButtonNeutralMessage);
140 }
141 if (m != null) {
142 m.sendToTarget();
143 }
144
145 // Post a message so we dismiss after the above handlers are executed
146 mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
147 .sendToTarget();
148 }
149 };
150
151 private static final class ButtonHandler extends Handler {
152 // Button clicks have Message.what as the BUTTON{1,2,3} constant
153 private static final int MSG_DISMISS_DIALOG = 1;
154
155 private WeakReference<DialogInterface> mDialog;
156
157 public ButtonHandler(DialogInterface dialog) {
158 mDialog = new WeakReference<DialogInterface>(dialog);
159 }
160
161 @Override
162 public void handleMessage(Message msg) {
163 switch (msg.what) {
164
165 case DialogInterface.BUTTON_POSITIVE:
166 case DialogInterface.BUTTON_NEGATIVE:
167 case DialogInterface.BUTTON_NEUTRAL:
168 ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
169 break;
170
171 case MSG_DISMISS_DIALOG:
172 ((DialogInterface) msg.obj).dismiss();
173 }
174 }
175 }
176
Adam Powell3320dcd2010-11-05 20:06:02 -0700177 private static boolean shouldCenterSingleButton(Context context) {
178 TypedValue outValue = new TypedValue();
179 context.getTheme().resolveAttribute(com.android.internal.R.attr.alertDialogCenterButtons,
180 outValue, true);
181 return outValue.data != 0;
182 }
183
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800184 public AlertController(Context context, DialogInterface di, Window window) {
185 mContext = context;
186 mDialogInterface = di;
187 mWindow = window;
188 mHandler = new ButtonHandler(di);
Adam Powellfcca00a2010-11-30 21:26:29 -0800189
190 TypedArray a = context.obtainStyledAttributes(null,
191 com.android.internal.R.styleable.AlertDialog,
192 com.android.internal.R.attr.alertDialogStyle, 0);
193
194 mAlertDialogLayout = a.getResourceId(com.android.internal.R.styleable.AlertDialog_layout,
195 com.android.internal.R.layout.alert_dialog);
196 mListLayout = a.getResourceId(
197 com.android.internal.R.styleable.AlertDialog_listLayout,
198 com.android.internal.R.layout.select_dialog);
199 mMultiChoiceItemLayout = a.getResourceId(
200 com.android.internal.R.styleable.AlertDialog_multiChoiceItemLayout,
201 com.android.internal.R.layout.select_dialog_multichoice);
202 mSingleChoiceItemLayout = a.getResourceId(
203 com.android.internal.R.styleable.AlertDialog_singleChoiceItemLayout,
204 com.android.internal.R.layout.select_dialog_singlechoice);
205 mListItemLayout = a.getResourceId(
206 com.android.internal.R.styleable.AlertDialog_listItemLayout,
207 com.android.internal.R.layout.select_dialog_item);
208
209 a.recycle();
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800210 }
211
212 static boolean canTextInput(View v) {
213 if (v.onCheckIsTextEditor()) {
214 return true;
215 }
216
217 if (!(v instanceof ViewGroup)) {
218 return false;
219 }
220
221 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 }
230
231 return false;
232 }
233
234 public void installContent() {
235 /* We use a custom title so never request a window title */
236 mWindow.requestFeature(Window.FEATURE_NO_TITLE);
Adam Powellfcca00a2010-11-30 21:26:29 -0800237 mWindow.setContentView(mAlertDialogLayout);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800238 setupView();
239 }
240
241 public void setTitle(CharSequence title) {
242 mTitle = title;
243 if (mTitleView != null) {
244 mTitleView.setText(title);
245 }
246 }
247
248 /**
249 * @see AlertDialog.Builder#setCustomTitle(View)
250 */
251 public void setCustomTitle(View customTitleView) {
252 mCustomTitleView = customTitleView;
253 }
254
255 public void setMessage(CharSequence message) {
256 mMessage = message;
257 if (mMessageView != null) {
258 mMessageView.setText(message);
259 }
260 }
261
262 /**
Alan Viveretteec186702013-12-05 11:10:31 -0800263 * Set the view resource to display in the dialog.
264 */
265 public void setView(int layoutResId) {
266 mView = null;
267 mViewLayoutResId = layoutResId;
268 mViewSpacingSpecified = false;
269 }
270
271 /**
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800272 * Set the view to display in the dialog.
273 */
274 public void setView(View view) {
275 mView = view;
Alan Viveretteec186702013-12-05 11:10:31 -0800276 mViewLayoutResId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800277 mViewSpacingSpecified = false;
278 }
279
280 /**
281 * Set the view to display in the dialog along with the spacing around that view
282 */
283 public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
284 int viewSpacingBottom) {
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 = true;
288 mViewSpacingLeft = viewSpacingLeft;
289 mViewSpacingTop = viewSpacingTop;
290 mViewSpacingRight = viewSpacingRight;
291 mViewSpacingBottom = viewSpacingBottom;
292 }
293
294 /**
295 * Sets a click listener or a message to be sent when the button is clicked.
296 * You only need to pass one of {@code listener} or {@code msg}.
297 *
298 * @param whichButton Which button, can be one of
299 * {@link DialogInterface#BUTTON_POSITIVE},
300 * {@link DialogInterface#BUTTON_NEGATIVE}, or
301 * {@link DialogInterface#BUTTON_NEUTRAL}
302 * @param text The text to display in positive button.
303 * @param listener The {@link DialogInterface.OnClickListener} to use.
304 * @param msg The {@link Message} to be sent when clicked.
305 */
306 public void setButton(int whichButton, CharSequence text,
307 DialogInterface.OnClickListener listener, Message msg) {
308
309 if (msg == null && listener != null) {
310 msg = mHandler.obtainMessage(whichButton, listener);
311 }
312
313 switch (whichButton) {
314
315 case DialogInterface.BUTTON_POSITIVE:
316 mButtonPositiveText = text;
317 mButtonPositiveMessage = msg;
318 break;
319
320 case DialogInterface.BUTTON_NEGATIVE:
321 mButtonNegativeText = text;
322 mButtonNegativeMessage = msg;
323 break;
324
325 case DialogInterface.BUTTON_NEUTRAL:
326 mButtonNeutralText = text;
327 mButtonNeutralMessage = msg;
328 break;
329
330 default:
331 throw new IllegalArgumentException("Button does not exist");
332 }
333 }
334
335 /**
336 * Set resId to 0 if you don't want an icon.
337 * @param resId the resourceId of the drawable to use as the icon or 0
338 * if you don't want an icon.
339 */
340 public void setIcon(int resId) {
341 mIconId = resId;
342 if (mIconView != null) {
343 if (resId > 0) {
344 mIconView.setImageResource(mIconId);
345 } else if (resId == 0) {
346 mIconView.setVisibility(View.GONE);
347 }
348 }
349 }
350
351 public void setIcon(Drawable icon) {
352 mIcon = icon;
353 if ((mIconView != null) && (mIcon != null)) {
354 mIconView.setImageDrawable(icon);
355 }
356 }
357
blunden576e1df2012-09-10 23:29:04 +0200358 /**
359 * @param attrId the attributeId of the theme-specific drawable
360 * to resolve the resourceId for.
361 *
362 * @return resId the resourceId of the theme-specific drawable
363 */
364 public int getIconAttributeResId(int attrId) {
365 TypedValue out = new TypedValue();
366 mContext.getTheme().resolveAttribute(attrId, out, true);
367 return out.resourceId;
368 }
369
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800370 public void setInverseBackgroundForced(boolean forceInverseBackground) {
371 mForceInverseBackground = forceInverseBackground;
372 }
373
374 public ListView getListView() {
375 return mListView;
376 }
377
378 public Button getButton(int whichButton) {
379 switch (whichButton) {
380 case DialogInterface.BUTTON_POSITIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800381 return mButtonPositive;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800382 case DialogInterface.BUTTON_NEGATIVE:
Romain Guy6fe2b222010-02-22 14:11:40 -0800383 return mButtonNegative;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800384 case DialogInterface.BUTTON_NEUTRAL:
Romain Guy6fe2b222010-02-22 14:11:40 -0800385 return mButtonNeutral;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800386 default:
387 return null;
388 }
389 }
390
Romain Guy6fe2b222010-02-22 14:11:40 -0800391 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800392 public boolean onKeyDown(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800393 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800394 }
395
Romain Guy6fe2b222010-02-22 14:11:40 -0800396 @SuppressWarnings({"UnusedDeclaration"})
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800397 public boolean onKeyUp(int keyCode, KeyEvent event) {
Romain Guy6fe2b222010-02-22 14:11:40 -0800398 return mScrollView != null && mScrollView.executeKeyEvent(event);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800399 }
400
401 private void setupView() {
402 LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel);
403 setupContent(contentPanel);
404 boolean hasButtons = setupButtons();
405
406 LinearLayout topPanel = (LinearLayout) mWindow.findViewById(R.id.topPanel);
407 TypedArray a = mContext.obtainStyledAttributes(
408 null, com.android.internal.R.styleable.AlertDialog, com.android.internal.R.attr.alertDialogStyle, 0);
409 boolean hasTitle = setupTitle(topPanel);
410
411 View buttonPanel = mWindow.findViewById(R.id.buttonPanel);
412 if (!hasButtons) {
413 buttonPanel.setVisibility(View.GONE);
Dianne Hackbornef575752011-01-18 17:35:17 -0800414 mWindow.setCloseOnTouchOutsideIfNotSet(true);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800415 }
416
Alan Viveretteec186702013-12-05 11:10:31 -0800417 final FrameLayout customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
418 final View customView;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800419 if (mView != null) {
Alan Viveretteec186702013-12-05 11:10:31 -0800420 customView = mView;
421 } else if (mViewLayoutResId != 0) {
422 final LayoutInflater inflater = LayoutInflater.from(mContext);
423 customView = inflater.inflate(mViewLayoutResId, customPanel, false);
424 } else {
425 customView = null;
426 }
427
428 if (customView == null || !canTextInput(customView)) {
429 mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
430 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
431 }
432
433 if (customView != null) {
434 final FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
435 custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT));
436
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800437 if (mViewSpacingSpecified) {
Alan Viveretteec186702013-12-05 11:10:31 -0800438 custom.setPadding(
439 mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800440 }
Alan Viveretteec186702013-12-05 11:10:31 -0800441
The Android Open Source Project10592532009-03-18 17:39:46 -0700442 if (mListView != null) {
443 ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
444 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800445 } else {
Alan Viveretteec186702013-12-05 11:10:31 -0800446 customPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800447 }
448
449 /* Only display the divider if we have a title and a
450 * custom view or a message.
451 */
Adam Powell32aa2c92011-01-11 15:37:04 -0800452 if (hasTitle) {
453 View divider = null;
Alan Viveretteec186702013-12-05 11:10:31 -0800454 if (mMessage != null || customView != null || mListView != null) {
Adam Powell32aa2c92011-01-11 15:37:04 -0800455 divider = mWindow.findViewById(R.id.titleDivider);
456 } else {
457 divider = mWindow.findViewById(R.id.titleDividerTop);
458 }
459
460 if (divider != null) {
461 divider.setVisibility(View.VISIBLE);
462 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800463 }
464
465 setBackground(topPanel, contentPanel, customPanel, hasButtons, a, hasTitle, buttonPanel);
466 a.recycle();
467 }
468
469 private boolean setupTitle(LinearLayout topPanel) {
470 boolean hasTitle = true;
471
472 if (mCustomTitleView != null) {
473 // Add the custom title view directly to the topPanel layout
474 LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -0800475 LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800476
Adam Powell5ea0bc12011-07-21 13:07:05 -0700477 topPanel.addView(mCustomTitleView, 0, lp);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800478
479 // Hide the title template
480 View titleTemplate = mWindow.findViewById(R.id.title_template);
481 titleTemplate.setVisibility(View.GONE);
482 } else {
483 final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
484
485 mIconView = (ImageView) mWindow.findViewById(R.id.icon);
486 if (hasTextTitle) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800487 /* Display the title if a title is supplied, else hide it */
488 mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
489
490 mTitleView.setText(mTitle);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800491
492 /* Do this last so that if the user has supplied any
493 * icons we use them instead of the default ones. If the
494 * user has specified 0 then make it disappear.
495 */
496 if (mIconId > 0) {
497 mIconView.setImageResource(mIconId);
498 } else if (mIcon != null) {
499 mIconView.setImageDrawable(mIcon);
500 } else if (mIconId == 0) {
501
502 /* Apply the padding from the icon to ensure the
503 * title is aligned correctly.
504 */
505 mTitleView.setPadding(mIconView.getPaddingLeft(),
506 mIconView.getPaddingTop(),
507 mIconView.getPaddingRight(),
508 mIconView.getPaddingBottom());
509 mIconView.setVisibility(View.GONE);
510 }
511 } else {
512
513 // Hide the title template
514 View titleTemplate = mWindow.findViewById(R.id.title_template);
515 titleTemplate.setVisibility(View.GONE);
516 mIconView.setVisibility(View.GONE);
Martin Sjoline74e7e22010-06-22 21:42:11 +0200517 topPanel.setVisibility(View.GONE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800518 hasTitle = false;
519 }
520 }
521 return hasTitle;
522 }
523
524 private void setupContent(LinearLayout contentPanel) {
525 mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
526 mScrollView.setFocusable(false);
527
528 // Special case for users that only want to display a String
529 mMessageView = (TextView) mWindow.findViewById(R.id.message);
530 if (mMessageView == null) {
531 return;
532 }
533
534 if (mMessage != null) {
535 mMessageView.setText(mMessage);
536 } else {
537 mMessageView.setVisibility(View.GONE);
538 mScrollView.removeView(mMessageView);
539
540 if (mListView != null) {
541 contentPanel.removeView(mWindow.findViewById(R.id.scrollView));
The Android Open Source Project10592532009-03-18 17:39:46 -0700542 contentPanel.addView(mListView,
Romain Guy980a9382010-01-08 15:06:28 -0800543 new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
544 contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800545 } else {
546 contentPanel.setVisibility(View.GONE);
547 }
548 }
549 }
550
551 private boolean setupButtons() {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800552 int BIT_BUTTON_POSITIVE = 1;
553 int BIT_BUTTON_NEGATIVE = 2;
554 int BIT_BUTTON_NEUTRAL = 4;
555 int whichButtons = 0;
556 mButtonPositive = (Button) mWindow.findViewById(R.id.button1);
557 mButtonPositive.setOnClickListener(mButtonHandler);
558
559 if (TextUtils.isEmpty(mButtonPositiveText)) {
560 mButtonPositive.setVisibility(View.GONE);
561 } else {
562 mButtonPositive.setText(mButtonPositiveText);
563 mButtonPositive.setVisibility(View.VISIBLE);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800564 whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
565 }
566
567 mButtonNegative = (Button) mWindow.findViewById(R.id.button2);
568 mButtonNegative.setOnClickListener(mButtonHandler);
569
570 if (TextUtils.isEmpty(mButtonNegativeText)) {
571 mButtonNegative.setVisibility(View.GONE);
572 } else {
573 mButtonNegative.setText(mButtonNegativeText);
574 mButtonNegative.setVisibility(View.VISIBLE);
575
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800576 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
577 }
578
579 mButtonNeutral = (Button) mWindow.findViewById(R.id.button3);
580 mButtonNeutral.setOnClickListener(mButtonHandler);
581
582 if (TextUtils.isEmpty(mButtonNeutralText)) {
583 mButtonNeutral.setVisibility(View.GONE);
584 } else {
585 mButtonNeutral.setText(mButtonNeutralText);
586 mButtonNeutral.setVisibility(View.VISIBLE);
587
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800588 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
589 }
590
Adam Powell3320dcd2010-11-05 20:06:02 -0700591 if (shouldCenterSingleButton(mContext)) {
592 /*
593 * If we only have 1 button it should be centered on the layout and
594 * expand to fill 50% of the available space.
595 */
596 if (whichButtons == BIT_BUTTON_POSITIVE) {
597 centerButton(mButtonPositive);
598 } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
Sangkyu Lee3079e582013-02-15 21:11:18 +0900599 centerButton(mButtonNegative);
Adam Powell3320dcd2010-11-05 20:06:02 -0700600 } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
601 centerButton(mButtonNeutral);
602 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800603 }
604
605 return whichButtons != 0;
606 }
607
608 private void centerButton(Button button) {
609 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
610 params.gravity = Gravity.CENTER_HORIZONTAL;
611 params.weight = 0.5f;
612 button.setLayoutParams(params);
613 View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700614 if (leftSpacer != null) {
615 leftSpacer.setVisibility(View.VISIBLE);
616 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800617 View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
Adam Powella93347a2011-06-14 13:37:14 -0700618 if (rightSpacer != null) {
619 rightSpacer.setVisibility(View.VISIBLE);
620 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800621 }
622
623 private void setBackground(LinearLayout topPanel, LinearLayout contentPanel,
624 View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle,
625 View buttonPanel) {
626
627 /* Get all the different background required */
628 int fullDark = a.getResourceId(
629 R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
630 int topDark = a.getResourceId(
631 R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
632 int centerDark = a.getResourceId(
633 R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
634 int bottomDark = a.getResourceId(
635 R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
636 int fullBright = a.getResourceId(
637 R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
638 int topBright = a.getResourceId(
639 R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
640 int centerBright = a.getResourceId(
641 R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
642 int bottomBright = a.getResourceId(
643 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
644 int bottomMedium = a.getResourceId(
645 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800646
647 /*
648 * We now set the background of all of the sections of the alert.
649 * First collect together each section that is being displayed along
650 * with whether it is on a light or dark background, then run through
651 * them setting their backgrounds. This is complicated because we need
652 * to correctly use the full, top, middle, and bottom graphics depending
653 * on how many views they are and where they appear.
654 */
655
656 View[] views = new View[4];
657 boolean[] light = new boolean[4];
658 View lastView = null;
659 boolean lastLight = false;
660
661 int pos = 0;
662 if (hasTitle) {
663 views[pos] = topPanel;
664 light[pos] = false;
665 pos++;
666 }
667
668 /* The contentPanel displays either a custom text message or
669 * a ListView. If it's text we should use the dark background
670 * for ListView we should use the light background. If neither
671 * are there the contentPanel will be hidden so set it as null.
672 */
673 views[pos] = (contentPanel.getVisibility() == View.GONE)
674 ? null : contentPanel;
Romain Guy6fe2b222010-02-22 14:11:40 -0800675 light[pos] = mListView != null;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800676 pos++;
677 if (customPanel != null) {
678 views[pos] = customPanel;
679 light[pos] = mForceInverseBackground;
680 pos++;
681 }
682 if (hasButtons) {
683 views[pos] = buttonPanel;
684 light[pos] = true;
685 }
686
687 boolean setView = false;
688 for (pos=0; pos<views.length; pos++) {
689 View v = views[pos];
690 if (v == null) {
691 continue;
692 }
693 if (lastView != null) {
694 if (!setView) {
695 lastView.setBackgroundResource(lastLight ? topBright : topDark);
696 } else {
697 lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
698 }
699 setView = true;
700 }
701 lastView = v;
702 lastLight = light[pos];
703 }
704
705 if (lastView != null) {
706 if (setView) {
707
708 /* ListViews will use the Bright background but buttons use
709 * the Medium background.
710 */
711 lastView.setBackgroundResource(
712 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
713 } else {
714 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
715 }
716 }
717
718 /* TODO: uncomment section below. The logic for this should be if
719 * it's a Contextual menu being displayed AND only a Cancel button
720 * is shown then do this.
721 */
722// if (hasButtons && (mListView != null)) {
723
724 /* Yet another *special* case. If there is a ListView with buttons
725 * don't put the buttons on the bottom but instead put them in the
726 * footer of the ListView this will allow more items to be
727 * displayed.
728 */
729
730 /*
731 contentPanel.setBackgroundResource(bottomBright);
732 buttonPanel.setBackgroundResource(centerMedium);
733 ViewGroup parent = (ViewGroup) mWindow.findViewById(R.id.parentPanel);
734 parent.removeView(buttonPanel);
735 AbsListView.LayoutParams params = new AbsListView.LayoutParams(
Romain Guy980a9382010-01-08 15:06:28 -0800736 AbsListView.LayoutParams.MATCH_PARENT,
737 AbsListView.LayoutParams.MATCH_PARENT);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800738 buttonPanel.setLayoutParams(params);
739 mListView.addFooterView(buttonPanel);
740 */
741// }
742
743 if ((mListView != null) && (mAdapter != null)) {
744 mListView.setAdapter(mAdapter);
745 if (mCheckedItem > -1) {
746 mListView.setItemChecked(mCheckedItem, true);
747 mListView.setSelection(mCheckedItem);
748 }
749 }
750 }
Romain Guy9c802c12009-03-25 15:07:31 -0700751
752 public static class RecycleListView extends ListView {
753 boolean mRecycleOnMeasure = true;
754
755 public RecycleListView(Context context) {
756 super(context);
757 }
758
759 public RecycleListView(Context context, AttributeSet attrs) {
760 super(context, attrs);
761 }
762
Alan Viverette617feb92013-09-09 18:09:13 -0700763 public RecycleListView(Context context, AttributeSet attrs, int defStyleAttr) {
764 super(context, attrs, defStyleAttr);
765 }
766
767 public RecycleListView(
768 Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
769 super(context, attrs, defStyleAttr, defStyleRes);
Romain Guy9c802c12009-03-25 15:07:31 -0700770 }
771
772 @Override
773 protected boolean recycleOnMeasure() {
774 return mRecycleOnMeasure;
775 }
776 }
777
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800778 public static class AlertParams {
779 public final Context mContext;
780 public final LayoutInflater mInflater;
781
Gilles Debunne076c7fb2010-10-04 12:00:02 -0700782 public int mIconId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800783 public Drawable mIcon;
blunden576e1df2012-09-10 23:29:04 +0200784 public int mIconAttrId = 0;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800785 public CharSequence mTitle;
786 public View mCustomTitleView;
787 public CharSequence mMessage;
788 public CharSequence mPositiveButtonText;
789 public DialogInterface.OnClickListener mPositiveButtonListener;
790 public CharSequence mNegativeButtonText;
791 public DialogInterface.OnClickListener mNegativeButtonListener;
792 public CharSequence mNeutralButtonText;
793 public DialogInterface.OnClickListener mNeutralButtonListener;
794 public boolean mCancelable;
795 public DialogInterface.OnCancelListener mOnCancelListener;
Adam Powell7f02dc52012-08-27 13:35:16 -0700796 public DialogInterface.OnDismissListener mOnDismissListener;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800797 public DialogInterface.OnKeyListener mOnKeyListener;
798 public CharSequence[] mItems;
799 public ListAdapter mAdapter;
800 public DialogInterface.OnClickListener mOnClickListener;
Alan Viveretteec186702013-12-05 11:10:31 -0800801 public int mViewLayoutResId;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800802 public View mView;
803 public int mViewSpacingLeft;
804 public int mViewSpacingTop;
805 public int mViewSpacingRight;
806 public int mViewSpacingBottom;
807 public boolean mViewSpacingSpecified = false;
808 public boolean[] mCheckedItems;
809 public boolean mIsMultiChoice;
810 public boolean mIsSingleChoice;
811 public int mCheckedItem = -1;
812 public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
813 public Cursor mCursor;
814 public String mLabelColumn;
815 public String mIsCheckedColumn;
816 public boolean mForceInverseBackground;
817 public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
818 public OnPrepareListViewListener mOnPrepareListViewListener;
Romain Guy9c802c12009-03-25 15:07:31 -0700819 public boolean mRecycleOnMeasure = true;
820
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800821 /**
822 * Interface definition for a callback to be invoked before the ListView
823 * will be bound to an adapter.
824 */
825 public interface OnPrepareListViewListener {
826
827 /**
828 * Called before the ListView is bound to an adapter.
829 * @param listView The ListView that will be shown in the dialog.
830 */
831 void onPrepareListView(ListView listView);
832 }
833
834 public AlertParams(Context context) {
835 mContext = context;
836 mCancelable = true;
837 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
838 }
839
840 public void apply(AlertController dialog) {
841 if (mCustomTitleView != null) {
842 dialog.setCustomTitle(mCustomTitleView);
843 } else {
844 if (mTitle != null) {
845 dialog.setTitle(mTitle);
846 }
847 if (mIcon != null) {
848 dialog.setIcon(mIcon);
849 }
850 if (mIconId >= 0) {
851 dialog.setIcon(mIconId);
852 }
blunden576e1df2012-09-10 23:29:04 +0200853 if (mIconAttrId > 0) {
854 dialog.setIcon(dialog.getIconAttributeResId(mIconAttrId));
855 }
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800856 }
857 if (mMessage != null) {
858 dialog.setMessage(mMessage);
859 }
860 if (mPositiveButtonText != null) {
861 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
862 mPositiveButtonListener, null);
863 }
864 if (mNegativeButtonText != null) {
865 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
866 mNegativeButtonListener, null);
867 }
868 if (mNeutralButtonText != null) {
869 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
870 mNeutralButtonListener, null);
871 }
872 if (mForceInverseBackground) {
873 dialog.setInverseBackgroundForced(true);
874 }
875 // For a list, the client can either supply an array of items or an
876 // adapter or a cursor
877 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
878 createListView(dialog);
879 }
880 if (mView != null) {
881 if (mViewSpacingSpecified) {
882 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
883 mViewSpacingBottom);
884 } else {
885 dialog.setView(mView);
886 }
Alan Viveretteec186702013-12-05 11:10:31 -0800887 } else if (mViewLayoutResId != 0) {
888 dialog.setView(mViewLayoutResId);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800889 }
Alan Viveretteec186702013-12-05 11:10:31 -0800890
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800891 /*
892 dialog.setCancelable(mCancelable);
893 dialog.setOnCancelListener(mOnCancelListener);
894 if (mOnKeyListener != null) {
895 dialog.setOnKeyListener(mOnKeyListener);
896 }
897 */
898 }
899
900 private void createListView(final AlertController dialog) {
Romain Guy9c802c12009-03-25 15:07:31 -0700901 final RecycleListView listView = (RecycleListView)
Adam Powellfcca00a2010-11-30 21:26:29 -0800902 mInflater.inflate(dialog.mListLayout, null);
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800903 ListAdapter adapter;
904
905 if (mIsMultiChoice) {
906 if (mCursor == null) {
907 adapter = new ArrayAdapter<CharSequence>(
Adam Powellfcca00a2010-11-30 21:26:29 -0800908 mContext, dialog.mMultiChoiceItemLayout, R.id.text1, mItems) {
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800909 @Override
910 public View getView(int position, View convertView, ViewGroup parent) {
911 View view = super.getView(position, convertView, parent);
912 if (mCheckedItems != null) {
913 boolean isItemChecked = mCheckedItems[position];
914 if (isItemChecked) {
915 listView.setItemChecked(position, true);
916 }
917 }
918 return view;
919 }
920 };
921 } else {
922 adapter = new CursorAdapter(mContext, mCursor, false) {
923 private final int mLabelIndex;
924 private final int mIsCheckedIndex;
925
926 {
927 final Cursor cursor = getCursor();
928 mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
929 mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
930 }
931
932 @Override
933 public void bindView(View view, Context context, Cursor cursor) {
934 CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
935 text.setText(cursor.getString(mLabelIndex));
936 listView.setItemChecked(cursor.getPosition(),
937 cursor.getInt(mIsCheckedIndex) == 1);
938 }
939
940 @Override
941 public View newView(Context context, Cursor cursor, ViewGroup parent) {
Adam Powellfcca00a2010-11-30 21:26:29 -0800942 return mInflater.inflate(dialog.mMultiChoiceItemLayout,
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800943 parent, false);
944 }
945
946 };
947 }
948 } else {
949 int layout = mIsSingleChoice
Adam Powellfcca00a2010-11-30 21:26:29 -0800950 ? dialog.mSingleChoiceItemLayout : dialog.mListItemLayout;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -0800951 if (mCursor == null) {
952 adapter = (mAdapter != null) ? mAdapter
953 : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
954 } else {
955 adapter = new SimpleCursorAdapter(mContext, layout,
956 mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
957 }
958 }
959
960 if (mOnPrepareListViewListener != null) {
961 mOnPrepareListViewListener.onPrepareListView(listView);
962 }
963
964 /* Don't directly set the adapter on the ListView as we might
965 * want to add a footer to the ListView later.
966 */
967 dialog.mAdapter = adapter;
968 dialog.mCheckedItem = mCheckedItem;
969
970 if (mOnClickListener != null) {
971 listView.setOnItemClickListener(new OnItemClickListener() {
972 public void onItemClick(AdapterView parent, View v, int position, long id) {
973 mOnClickListener.onClick(dialog.mDialogInterface, position);
974 if (!mIsSingleChoice) {
975 dialog.mDialogInterface.dismiss();
976 }
977 }
978 });
979 } else if (mOnCheckboxClickListener != null) {
980 listView.setOnItemClickListener(new OnItemClickListener() {
981 public void onItemClick(AdapterView parent, View v, int position, long id) {
982 if (mCheckedItems != null) {
983 mCheckedItems[position] = listView.isItemChecked(position);
984 }
985 mOnCheckboxClickListener.onClick(
986 dialog.mDialogInterface, position, listView.isItemChecked(position));
987 }
988 });
989 }
990
991 // Attach a given OnItemSelectedListener to the ListView
992 if (mOnItemSelectedListener != null) {
993 listView.setOnItemSelectedListener(mOnItemSelectedListener);
994 }
995
996 if (mIsSingleChoice) {
997 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
998 } else if (mIsMultiChoice) {
999 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
1000 }
Romain Guy9c802c12009-03-25 15:07:31 -07001001 listView.mRecycleOnMeasure = mRecycleOnMeasure;
The Android Open Source Project9066cfe2009-03-03 19:31:44 -08001002 dialog.mListView = listView;
1003 }
1004 }
1005
1006}