• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
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 
17 package com.android.internal.app;
18 
19 import static android.view.ViewGroup.LayoutParams.FILL_PARENT;
20 import android.app.AlertDialog;
21 import android.content.Context;
22 import android.content.DialogInterface;
23 import android.content.res.TypedArray;
24 import android.database.Cursor;
25 import android.graphics.drawable.Drawable;
26 import android.os.Handler;
27 import android.os.Message;
28 import android.text.TextUtils;
29 import android.view.Gravity;
30 import android.view.KeyEvent;
31 import android.view.LayoutInflater;
32 import android.view.View;
33 import android.view.ViewGroup;
34 import android.view.Window;
35 import android.view.WindowManager;
36 import android.view.ViewGroup.LayoutParams;
37 import android.widget.AdapterView;
38 import android.widget.ArrayAdapter;
39 import android.widget.Button;
40 import android.widget.CheckedTextView;
41 import android.widget.CursorAdapter;
42 import android.widget.FrameLayout;
43 import android.widget.ImageView;
44 import android.widget.LinearLayout;
45 import android.widget.ListAdapter;
46 import android.widget.ListView;
47 import android.widget.ScrollView;
48 import android.widget.SimpleCursorAdapter;
49 import android.widget.TextView;
50 import android.widget.AdapterView.OnItemClickListener;
51 import android.util.AttributeSet;
52 
53 import com.android.internal.R;
54 
55 import java.lang.ref.WeakReference;
56 
57 public class AlertController {
58 
59     private final Context mContext;
60     private final DialogInterface mDialogInterface;
61     private final Window mWindow;
62 
63     private CharSequence mTitle;
64 
65     private CharSequence mMessage;
66 
67     private ListView mListView;
68 
69     private View mView;
70 
71     private int mViewSpacingLeft;
72 
73     private int mViewSpacingTop;
74 
75     private int mViewSpacingRight;
76 
77     private int mViewSpacingBottom;
78 
79     private boolean mViewSpacingSpecified = false;
80 
81     private Button mButtonPositive;
82 
83     private CharSequence mButtonPositiveText;
84 
85     private Message mButtonPositiveMessage;
86 
87     private Button mButtonNegative;
88 
89     private CharSequence mButtonNegativeText;
90 
91     private Message mButtonNegativeMessage;
92 
93     private Button mButtonNeutral;
94 
95     private CharSequence mButtonNeutralText;
96 
97     private Message mButtonNeutralMessage;
98 
99     private ScrollView mScrollView;
100 
101     private int mIconId = -1;
102 
103     private Drawable mIcon;
104 
105     private ImageView mIconView;
106 
107     private TextView mTitleView;
108 
109     private TextView mMessageView;
110 
111     private View mCustomTitleView;
112 
113     private boolean mForceInverseBackground;
114 
115     private ListAdapter mAdapter;
116 
117     private int mCheckedItem = -1;
118 
119     private Handler mHandler;
120 
121     View.OnClickListener mButtonHandler = new View.OnClickListener() {
122         public void onClick(View v) {
123             Message m = null;
124             if (v == mButtonPositive && mButtonPositiveMessage != null) {
125                 m = Message.obtain(mButtonPositiveMessage);
126             } else if (v == mButtonNegative && mButtonNegativeMessage != null) {
127                 m = Message.obtain(mButtonNegativeMessage);
128             } else if (v == mButtonNeutral && mButtonNeutralMessage != null) {
129                 m = Message.obtain(mButtonNeutralMessage);
130             }
131             if (m != null) {
132                 m.sendToTarget();
133             }
134 
135             // Post a message so we dismiss after the above handlers are executed
136             mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface)
137                     .sendToTarget();
138         }
139     };
140 
141     private static final class ButtonHandler extends Handler {
142         // Button clicks have Message.what as the BUTTON{1,2,3} constant
143         private static final int MSG_DISMISS_DIALOG = 1;
144 
145         private WeakReference<DialogInterface> mDialog;
146 
ButtonHandler(DialogInterface dialog)147         public ButtonHandler(DialogInterface dialog) {
148             mDialog = new WeakReference<DialogInterface>(dialog);
149         }
150 
151         @Override
handleMessage(Message msg)152         public void handleMessage(Message msg) {
153             switch (msg.what) {
154 
155                 case DialogInterface.BUTTON_POSITIVE:
156                 case DialogInterface.BUTTON_NEGATIVE:
157                 case DialogInterface.BUTTON_NEUTRAL:
158                     ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what);
159                     break;
160 
161                 case MSG_DISMISS_DIALOG:
162                     ((DialogInterface) msg.obj).dismiss();
163             }
164         }
165     }
166 
AlertController(Context context, DialogInterface di, Window window)167     public AlertController(Context context, DialogInterface di, Window window) {
168         mContext = context;
169         mDialogInterface = di;
170         mWindow = window;
171         mHandler = new ButtonHandler(di);
172     }
173 
canTextInput(View v)174     static boolean canTextInput(View v) {
175         if (v.onCheckIsTextEditor()) {
176             return true;
177         }
178 
179         if (!(v instanceof ViewGroup)) {
180             return false;
181         }
182 
183         ViewGroup vg = (ViewGroup)v;
184         int i = vg.getChildCount();
185         while (i > 0) {
186             i--;
187             v = vg.getChildAt(i);
188             if (canTextInput(v)) {
189                 return true;
190             }
191         }
192 
193         return false;
194     }
195 
installContent()196     public void installContent() {
197         /* We use a custom title so never request a window title */
198         mWindow.requestFeature(Window.FEATURE_NO_TITLE);
199 
200         if (mView == null || !canTextInput(mView)) {
201             mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM,
202                     WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM);
203         }
204         mWindow.setContentView(com.android.internal.R.layout.alert_dialog);
205         setupView();
206     }
207 
setTitle(CharSequence title)208     public void setTitle(CharSequence title) {
209         mTitle = title;
210         if (mTitleView != null) {
211             mTitleView.setText(title);
212         }
213     }
214 
215     /**
216      * @see AlertDialog.Builder#setCustomTitle(View)
217      */
setCustomTitle(View customTitleView)218     public void setCustomTitle(View customTitleView) {
219         mCustomTitleView = customTitleView;
220     }
221 
setMessage(CharSequence message)222     public void setMessage(CharSequence message) {
223         mMessage = message;
224         if (mMessageView != null) {
225             mMessageView.setText(message);
226         }
227     }
228 
229     /**
230      * Set the view to display in the dialog.
231      */
setView(View view)232     public void setView(View view) {
233         mView = view;
234         mViewSpacingSpecified = false;
235     }
236 
237     /**
238      * Set the view to display in the dialog along with the spacing around that view
239      */
setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)240     public void setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight,
241             int viewSpacingBottom) {
242         mView = view;
243         mViewSpacingSpecified = true;
244         mViewSpacingLeft = viewSpacingLeft;
245         mViewSpacingTop = viewSpacingTop;
246         mViewSpacingRight = viewSpacingRight;
247         mViewSpacingBottom = viewSpacingBottom;
248     }
249 
250     /**
251      * Sets a click listener or a message to be sent when the button is clicked.
252      * You only need to pass one of {@code listener} or {@code msg}.
253      *
254      * @param whichButton Which button, can be one of
255      *            {@link DialogInterface#BUTTON_POSITIVE},
256      *            {@link DialogInterface#BUTTON_NEGATIVE}, or
257      *            {@link DialogInterface#BUTTON_NEUTRAL}
258      * @param text The text to display in positive button.
259      * @param listener The {@link DialogInterface.OnClickListener} to use.
260      * @param msg The {@link Message} to be sent when clicked.
261      */
setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener, Message msg)262     public void setButton(int whichButton, CharSequence text,
263             DialogInterface.OnClickListener listener, Message msg) {
264 
265         if (msg == null && listener != null) {
266             msg = mHandler.obtainMessage(whichButton, listener);
267         }
268 
269         switch (whichButton) {
270 
271             case DialogInterface.BUTTON_POSITIVE:
272                 mButtonPositiveText = text;
273                 mButtonPositiveMessage = msg;
274                 break;
275 
276             case DialogInterface.BUTTON_NEGATIVE:
277                 mButtonNegativeText = text;
278                 mButtonNegativeMessage = msg;
279                 break;
280 
281             case DialogInterface.BUTTON_NEUTRAL:
282                 mButtonNeutralText = text;
283                 mButtonNeutralMessage = msg;
284                 break;
285 
286             default:
287                 throw new IllegalArgumentException("Button does not exist");
288         }
289     }
290 
291     /**
292      * Set resId to 0 if you don't want an icon.
293      * @param resId the resourceId of the drawable to use as the icon or 0
294      * if you don't want an icon.
295      */
setIcon(int resId)296     public void setIcon(int resId) {
297         mIconId = resId;
298         if (mIconView != null) {
299             if (resId > 0) {
300                 mIconView.setImageResource(mIconId);
301             } else if (resId == 0) {
302                 mIconView.setVisibility(View.GONE);
303             }
304         }
305     }
306 
setIcon(Drawable icon)307     public void setIcon(Drawable icon) {
308         mIcon = icon;
309         if ((mIconView != null) && (mIcon != null)) {
310             mIconView.setImageDrawable(icon);
311         }
312     }
313 
setInverseBackgroundForced(boolean forceInverseBackground)314     public void setInverseBackgroundForced(boolean forceInverseBackground) {
315         mForceInverseBackground = forceInverseBackground;
316     }
317 
getListView()318     public ListView getListView() {
319         return mListView;
320     }
321 
getButton(int whichButton)322     public Button getButton(int whichButton) {
323         switch (whichButton) {
324             case DialogInterface.BUTTON_POSITIVE:
325                 return mButtonPositiveMessage != null ? mButtonPositive : null;
326             case DialogInterface.BUTTON_NEGATIVE:
327                 return mButtonNegativeMessage != null ? mButtonNegative : null;
328             case DialogInterface.BUTTON_NEUTRAL:
329                 return mButtonNeutralMessage != null ? mButtonNeutral : null;
330             default:
331                 return null;
332         }
333     }
334 
onKeyDown(int keyCode, KeyEvent event)335     public boolean onKeyDown(int keyCode, KeyEvent event) {
336         if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
337         return false;
338     }
339 
onKeyUp(int keyCode, KeyEvent event)340     public boolean onKeyUp(int keyCode, KeyEvent event) {
341         if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
342         return false;
343     }
344 
setupView()345     private void setupView() {
346         LinearLayout contentPanel = (LinearLayout) mWindow.findViewById(R.id.contentPanel);
347         setupContent(contentPanel);
348         boolean hasButtons = setupButtons();
349 
350         LinearLayout topPanel = (LinearLayout) mWindow.findViewById(R.id.topPanel);
351         TypedArray a = mContext.obtainStyledAttributes(
352                 null, com.android.internal.R.styleable.AlertDialog, com.android.internal.R.attr.alertDialogStyle, 0);
353         boolean hasTitle = setupTitle(topPanel);
354 
355         View buttonPanel = mWindow.findViewById(R.id.buttonPanel);
356         if (!hasButtons) {
357             buttonPanel.setVisibility(View.GONE);
358         }
359 
360         FrameLayout customPanel = null;
361         if (mView != null) {
362             customPanel = (FrameLayout) mWindow.findViewById(R.id.customPanel);
363             FrameLayout custom = (FrameLayout) mWindow.findViewById(R.id.custom);
364             custom.addView(mView, new LayoutParams(FILL_PARENT, FILL_PARENT));
365             if (mViewSpacingSpecified) {
366                 custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
367                         mViewSpacingBottom);
368             }
369             if (mListView != null) {
370                 ((LinearLayout.LayoutParams) customPanel.getLayoutParams()).weight = 0;
371             }
372         } else {
373             mWindow.findViewById(R.id.customPanel).setVisibility(View.GONE);
374         }
375 
376         /* Only display the divider if we have a title and a
377          * custom view or a message.
378          */
379         if (hasTitle && ((mMessage != null) || (mView != null))) {
380             View divider = mWindow.findViewById(R.id.titleDivider);
381             divider.setVisibility(View.VISIBLE);
382         }
383 
384         setBackground(topPanel, contentPanel, customPanel, hasButtons, a, hasTitle, buttonPanel);
385         a.recycle();
386     }
387 
setupTitle(LinearLayout topPanel)388     private boolean setupTitle(LinearLayout topPanel) {
389         boolean hasTitle = true;
390 
391         if (mCustomTitleView != null) {
392             // Add the custom title view directly to the topPanel layout
393             LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
394                     LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
395 
396             topPanel.addView(mCustomTitleView, lp);
397 
398             // Hide the title template
399             View titleTemplate = mWindow.findViewById(R.id.title_template);
400             titleTemplate.setVisibility(View.GONE);
401         } else {
402             final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
403 
404             mIconView = (ImageView) mWindow.findViewById(R.id.icon);
405             if (hasTextTitle) {
406 
407                 /* Display the title if a title is supplied, else hide it */
408                 mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
409 
410                 mTitleView.setText(mTitle);
411                 mIconView.setImageResource(R.drawable.ic_dialog_menu_generic);
412 
413                 /* Do this last so that if the user has supplied any
414                  * icons we use them instead of the default ones. If the
415                  * user has specified 0 then make it disappear.
416                  */
417                 if (mIconId > 0) {
418                     mIconView.setImageResource(mIconId);
419                 } else if (mIcon != null) {
420                     mIconView.setImageDrawable(mIcon);
421                 } else if (mIconId == 0) {
422 
423                     /* Apply the padding from the icon to ensure the
424                      * title is aligned correctly.
425                      */
426                     mTitleView.setPadding(mIconView.getPaddingLeft(),
427                             mIconView.getPaddingTop(),
428                             mIconView.getPaddingRight(),
429                             mIconView.getPaddingBottom());
430                     mIconView.setVisibility(View.GONE);
431                 }
432             } else {
433 
434                 // Hide the title template
435                 View titleTemplate = mWindow.findViewById(R.id.title_template);
436                 titleTemplate.setVisibility(View.GONE);
437                 mIconView.setVisibility(View.GONE);
438                 hasTitle = false;
439             }
440         }
441         return hasTitle;
442     }
443 
setupContent(LinearLayout contentPanel)444     private void setupContent(LinearLayout contentPanel) {
445         mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
446         mScrollView.setFocusable(false);
447 
448         // Special case for users that only want to display a String
449         mMessageView = (TextView) mWindow.findViewById(R.id.message);
450         if (mMessageView == null) {
451             return;
452         }
453 
454         if (mMessage != null) {
455             mMessageView.setText(mMessage);
456         } else {
457             mMessageView.setVisibility(View.GONE);
458             mScrollView.removeView(mMessageView);
459 
460             if (mListView != null) {
461                 contentPanel.removeView(mWindow.findViewById(R.id.scrollView));
462                 contentPanel.addView(mListView,
463                         new LinearLayout.LayoutParams(FILL_PARENT, FILL_PARENT));
464                 contentPanel.setLayoutParams(new LinearLayout.LayoutParams(FILL_PARENT, 0, 1.0f));
465             } else {
466                 contentPanel.setVisibility(View.GONE);
467             }
468         }
469     }
470 
setupButtons()471     private boolean setupButtons() {
472         View defaultButton = null;
473         int BIT_BUTTON_POSITIVE = 1;
474         int BIT_BUTTON_NEGATIVE = 2;
475         int BIT_BUTTON_NEUTRAL = 4;
476         int whichButtons = 0;
477         mButtonPositive = (Button) mWindow.findViewById(R.id.button1);
478         mButtonPositive.setOnClickListener(mButtonHandler);
479 
480         if (TextUtils.isEmpty(mButtonPositiveText)) {
481             mButtonPositive.setVisibility(View.GONE);
482         } else {
483             mButtonPositive.setText(mButtonPositiveText);
484             mButtonPositive.setVisibility(View.VISIBLE);
485             defaultButton = mButtonPositive;
486             whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
487         }
488 
489         mButtonNegative = (Button) mWindow.findViewById(R.id.button2);
490         mButtonNegative.setOnClickListener(mButtonHandler);
491 
492         if (TextUtils.isEmpty(mButtonNegativeText)) {
493             mButtonNegative.setVisibility(View.GONE);
494         } else {
495             mButtonNegative.setText(mButtonNegativeText);
496             mButtonNegative.setVisibility(View.VISIBLE);
497 
498             if (defaultButton == null) {
499                 defaultButton = mButtonNegative;
500             }
501             whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
502         }
503 
504         mButtonNeutral = (Button) mWindow.findViewById(R.id.button3);
505         mButtonNeutral.setOnClickListener(mButtonHandler);
506 
507         if (TextUtils.isEmpty(mButtonNeutralText)) {
508             mButtonNeutral.setVisibility(View.GONE);
509         } else {
510             mButtonNeutral.setText(mButtonNeutralText);
511             mButtonNeutral.setVisibility(View.VISIBLE);
512 
513             if (defaultButton == null) {
514                 defaultButton = mButtonNeutral;
515             }
516             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
517         }
518 
519         /*
520          * If we only have 1 button it should be centered on the layout and
521          * expand to fill 50% of the available space.
522          */
523         if (whichButtons == BIT_BUTTON_POSITIVE) {
524             centerButton(mButtonPositive);
525         } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
526             centerButton(mButtonNeutral);
527         } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
528             centerButton(mButtonNeutral);
529         }
530 
531         return whichButtons != 0;
532     }
533 
centerButton(Button button)534     private void centerButton(Button button) {
535         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
536         params.gravity = Gravity.CENTER_HORIZONTAL;
537         params.weight = 0.5f;
538         button.setLayoutParams(params);
539         View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
540         leftSpacer.setVisibility(View.VISIBLE);
541         View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
542         rightSpacer.setVisibility(View.VISIBLE);
543     }
544 
setBackground(LinearLayout topPanel, LinearLayout contentPanel, View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle, View buttonPanel)545     private void setBackground(LinearLayout topPanel, LinearLayout contentPanel,
546             View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle,
547             View buttonPanel) {
548 
549         /* Get all the different background required */
550         int fullDark = a.getResourceId(
551                 R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
552         int topDark = a.getResourceId(
553                 R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
554         int centerDark = a.getResourceId(
555                 R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
556         int bottomDark = a.getResourceId(
557                 R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
558         int fullBright = a.getResourceId(
559                 R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
560         int topBright = a.getResourceId(
561                 R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
562         int centerBright = a.getResourceId(
563                 R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
564         int bottomBright = a.getResourceId(
565                 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
566         int bottomMedium = a.getResourceId(
567                 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
568         int centerMedium = a.getResourceId(
569                 R.styleable.AlertDialog_centerMedium, R.drawable.popup_center_medium);
570 
571         /*
572          * We now set the background of all of the sections of the alert.
573          * First collect together each section that is being displayed along
574          * with whether it is on a light or dark background, then run through
575          * them setting their backgrounds.  This is complicated because we need
576          * to correctly use the full, top, middle, and bottom graphics depending
577          * on how many views they are and where they appear.
578          */
579 
580         View[] views = new View[4];
581         boolean[] light = new boolean[4];
582         View lastView = null;
583         boolean lastLight = false;
584 
585         int pos = 0;
586         if (hasTitle) {
587             views[pos] = topPanel;
588             light[pos] = false;
589             pos++;
590         }
591 
592         /* The contentPanel displays either a custom text message or
593          * a ListView. If it's text we should use the dark background
594          * for ListView we should use the light background. If neither
595          * are there the contentPanel will be hidden so set it as null.
596          */
597         views[pos] = (contentPanel.getVisibility() == View.GONE)
598                 ? null : contentPanel;
599         light[pos] = mListView == null ? false : true;
600         pos++;
601         if (customPanel != null) {
602             views[pos] = customPanel;
603             light[pos] = mForceInverseBackground;
604             pos++;
605         }
606         if (hasButtons) {
607             views[pos] = buttonPanel;
608             light[pos] = true;
609         }
610 
611         boolean setView = false;
612         for (pos=0; pos<views.length; pos++) {
613             View v = views[pos];
614             if (v == null) {
615                 continue;
616             }
617             if (lastView != null) {
618                 if (!setView) {
619                     lastView.setBackgroundResource(lastLight ? topBright : topDark);
620                 } else {
621                     lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
622                 }
623                 setView = true;
624             }
625             lastView = v;
626             lastLight = light[pos];
627         }
628 
629         if (lastView != null) {
630             if (setView) {
631 
632                 /* ListViews will use the Bright background but buttons use
633                  * the Medium background.
634                  */
635                 lastView.setBackgroundResource(
636                         lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
637             } else {
638                 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
639             }
640         }
641 
642         /* TODO: uncomment section below. The logic for this should be if
643          * it's a Contextual menu being displayed AND only a Cancel button
644          * is shown then do this.
645          */
646 //        if (hasButtons && (mListView != null)) {
647 
648             /* Yet another *special* case. If there is a ListView with buttons
649              * don't put the buttons on the bottom but instead put them in the
650              * footer of the ListView this will allow more items to be
651              * displayed.
652              */
653 
654             /*
655             contentPanel.setBackgroundResource(bottomBright);
656             buttonPanel.setBackgroundResource(centerMedium);
657             ViewGroup parent = (ViewGroup) mWindow.findViewById(R.id.parentPanel);
658             parent.removeView(buttonPanel);
659             AbsListView.LayoutParams params = new AbsListView.LayoutParams(
660                     AbsListView.LayoutParams.FILL_PARENT,
661                     AbsListView.LayoutParams.FILL_PARENT);
662             buttonPanel.setLayoutParams(params);
663             mListView.addFooterView(buttonPanel);
664             */
665 //        }
666 
667         if ((mListView != null) && (mAdapter != null)) {
668             mListView.setAdapter(mAdapter);
669             if (mCheckedItem > -1) {
670                 mListView.setItemChecked(mCheckedItem, true);
671                 mListView.setSelection(mCheckedItem);
672             }
673         }
674     }
675 
676     public static class RecycleListView extends ListView {
677         boolean mRecycleOnMeasure = true;
678 
RecycleListView(Context context)679         public RecycleListView(Context context) {
680             super(context);
681         }
682 
RecycleListView(Context context, AttributeSet attrs)683         public RecycleListView(Context context, AttributeSet attrs) {
684             super(context, attrs);
685         }
686 
RecycleListView(Context context, AttributeSet attrs, int defStyle)687         public RecycleListView(Context context, AttributeSet attrs, int defStyle) {
688             super(context, attrs, defStyle);
689         }
690 
691         @Override
recycleOnMeasure()692         protected boolean recycleOnMeasure() {
693             return mRecycleOnMeasure;
694         }
695     }
696 
697     public static class AlertParams {
698         public final Context mContext;
699         public final LayoutInflater mInflater;
700 
701         public int mIconId = -1;
702         public Drawable mIcon;
703         public CharSequence mTitle;
704         public View mCustomTitleView;
705         public CharSequence mMessage;
706         public CharSequence mPositiveButtonText;
707         public DialogInterface.OnClickListener mPositiveButtonListener;
708         public CharSequence mNegativeButtonText;
709         public DialogInterface.OnClickListener mNegativeButtonListener;
710         public CharSequence mNeutralButtonText;
711         public DialogInterface.OnClickListener mNeutralButtonListener;
712         public boolean mCancelable;
713         public DialogInterface.OnCancelListener mOnCancelListener;
714         public DialogInterface.OnKeyListener mOnKeyListener;
715         public CharSequence[] mItems;
716         public ListAdapter mAdapter;
717         public DialogInterface.OnClickListener mOnClickListener;
718         public View mView;
719         public int mViewSpacingLeft;
720         public int mViewSpacingTop;
721         public int mViewSpacingRight;
722         public int mViewSpacingBottom;
723         public boolean mViewSpacingSpecified = false;
724         public boolean[] mCheckedItems;
725         public boolean mIsMultiChoice;
726         public boolean mIsSingleChoice;
727         public int mCheckedItem = -1;
728         public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
729         public Cursor mCursor;
730         public String mLabelColumn;
731         public String mIsCheckedColumn;
732         public boolean mForceInverseBackground;
733         public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
734         public OnPrepareListViewListener mOnPrepareListViewListener;
735         public boolean mRecycleOnMeasure = true;
736 
737         /**
738          * Interface definition for a callback to be invoked before the ListView
739          * will be bound to an adapter.
740          */
741         public interface OnPrepareListViewListener {
742 
743             /**
744              * Called before the ListView is bound to an adapter.
745              * @param listView The ListView that will be shown in the dialog.
746              */
onPrepareListView(ListView listView)747             void onPrepareListView(ListView listView);
748         }
749 
AlertParams(Context context)750         public AlertParams(Context context) {
751             mContext = context;
752             mCancelable = true;
753             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
754         }
755 
apply(AlertController dialog)756         public void apply(AlertController dialog) {
757             if (mCustomTitleView != null) {
758                 dialog.setCustomTitle(mCustomTitleView);
759             } else {
760                 if (mTitle != null) {
761                     dialog.setTitle(mTitle);
762                 }
763                 if (mIcon != null) {
764                     dialog.setIcon(mIcon);
765                 }
766                 if (mIconId >= 0) {
767                     dialog.setIcon(mIconId);
768                 }
769             }
770             if (mMessage != null) {
771                 dialog.setMessage(mMessage);
772             }
773             if (mPositiveButtonText != null) {
774                 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
775                         mPositiveButtonListener, null);
776             }
777             if (mNegativeButtonText != null) {
778                 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
779                         mNegativeButtonListener, null);
780             }
781             if (mNeutralButtonText != null) {
782                 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
783                         mNeutralButtonListener, null);
784             }
785             if (mForceInverseBackground) {
786                 dialog.setInverseBackgroundForced(true);
787             }
788             // For a list, the client can either supply an array of items or an
789             // adapter or a cursor
790             if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
791                 createListView(dialog);
792             }
793             if (mView != null) {
794                 if (mViewSpacingSpecified) {
795                     dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
796                             mViewSpacingBottom);
797                 } else {
798                     dialog.setView(mView);
799                 }
800             }
801 
802             /*
803             dialog.setCancelable(mCancelable);
804             dialog.setOnCancelListener(mOnCancelListener);
805             if (mOnKeyListener != null) {
806                 dialog.setOnKeyListener(mOnKeyListener);
807             }
808             */
809         }
810 
createListView(final AlertController dialog)811         private void createListView(final AlertController dialog) {
812             final RecycleListView listView = (RecycleListView)
813                     mInflater.inflate(R.layout.select_dialog, null);
814             ListAdapter adapter;
815 
816             if (mIsMultiChoice) {
817                 if (mCursor == null) {
818                     adapter = new ArrayAdapter<CharSequence>(
819                             mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) {
820                         @Override
821                         public View getView(int position, View convertView, ViewGroup parent) {
822                             View view = super.getView(position, convertView, parent);
823                             if (mCheckedItems != null) {
824                                 boolean isItemChecked = mCheckedItems[position];
825                                 if (isItemChecked) {
826                                     listView.setItemChecked(position, true);
827                                 }
828                             }
829                             return view;
830                         }
831                     };
832                 } else {
833                     adapter = new CursorAdapter(mContext, mCursor, false) {
834                         private final int mLabelIndex;
835                         private final int mIsCheckedIndex;
836 
837                         {
838                             final Cursor cursor = getCursor();
839                             mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
840                             mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
841                         }
842 
843                         @Override
844                         public void bindView(View view, Context context, Cursor cursor) {
845                             CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
846                             text.setText(cursor.getString(mLabelIndex));
847                             listView.setItemChecked(cursor.getPosition(),
848                                     cursor.getInt(mIsCheckedIndex) == 1);
849                         }
850 
851                         @Override
852                         public View newView(Context context, Cursor cursor, ViewGroup parent) {
853                             return mInflater.inflate(R.layout.select_dialog_multichoice,
854                                     parent, false);
855                         }
856 
857                     };
858                 }
859             } else {
860                 int layout = mIsSingleChoice
861                         ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item;
862                 if (mCursor == null) {
863                     adapter = (mAdapter != null) ? mAdapter
864                             : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
865                 } else {
866                     adapter = new SimpleCursorAdapter(mContext, layout,
867                             mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
868                 }
869             }
870 
871             if (mOnPrepareListViewListener != null) {
872                 mOnPrepareListViewListener.onPrepareListView(listView);
873             }
874 
875             /* Don't directly set the adapter on the ListView as we might
876              * want to add a footer to the ListView later.
877              */
878             dialog.mAdapter = adapter;
879             dialog.mCheckedItem = mCheckedItem;
880 
881             if (mOnClickListener != null) {
882                 listView.setOnItemClickListener(new OnItemClickListener() {
883                     public void onItemClick(AdapterView parent, View v, int position, long id) {
884                         mOnClickListener.onClick(dialog.mDialogInterface, position);
885                         if (!mIsSingleChoice) {
886                             dialog.mDialogInterface.dismiss();
887                         }
888                     }
889                 });
890             } else if (mOnCheckboxClickListener != null) {
891                 listView.setOnItemClickListener(new OnItemClickListener() {
892                     public void onItemClick(AdapterView parent, View v, int position, long id) {
893                         if (mCheckedItems != null) {
894                             mCheckedItems[position] = listView.isItemChecked(position);
895                         }
896                         mOnCheckboxClickListener.onClick(
897                                 dialog.mDialogInterface, position, listView.isItemChecked(position));
898                     }
899                 });
900             }
901 
902             // Attach a given OnItemSelectedListener to the ListView
903             if (mOnItemSelectedListener != null) {
904                 listView.setOnItemSelectedListener(mOnItemSelectedListener);
905             }
906 
907             if (mIsSingleChoice) {
908                 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
909             } else if (mIsMultiChoice) {
910                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
911             }
912             listView.mRecycleOnMeasure = mRecycleOnMeasure;
913             dialog.mListView = listView;
914         }
915     }
916 
917 }
918