AlertController.java revision 9066cfe9886ac131c34d59ed0e2d287b0e3c0087
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
17package com.android.internal.app;
18
19import static android.view.ViewGroup.LayoutParams.FILL_PARENT;
20import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
21import android.app.AlertDialog;
22import android.content.Context;
23import android.content.DialogInterface;
24import android.content.res.TypedArray;
25import android.database.Cursor;
26import android.graphics.drawable.Drawable;
27import android.os.Handler;
28import android.os.Message;
29import android.text.TextUtils;
30import android.view.Gravity;
31import android.view.KeyEvent;
32import android.view.LayoutInflater;
33import android.view.View;
34import android.view.ViewGroup;
35import android.view.Window;
36import android.view.WindowManager;
37import android.view.ViewGroup.LayoutParams;
38import android.widget.AdapterView;
39import android.widget.ArrayAdapter;
40import android.widget.Button;
41import android.widget.CheckedTextView;
42import android.widget.CursorAdapter;
43import android.widget.FrameLayout;
44import android.widget.ImageView;
45import android.widget.LinearLayout;
46import android.widget.ListAdapter;
47import android.widget.ListView;
48import android.widget.ScrollView;
49import android.widget.SimpleCursorAdapter;
50import android.widget.TextView;
51import android.widget.AdapterView.OnItemClickListener;
52
53import com.android.internal.R;
54
55import java.lang.ref.WeakReference;
56
57public 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
147        public ButtonHandler(DialogInterface dialog) {
148            mDialog = new WeakReference<DialogInterface>(dialog);
149        }
150
151        @Override
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
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
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
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
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     */
218    public void setCustomTitle(View customTitleView) {
219        mCustomTitleView = customTitleView;
220    }
221
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     */
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     */
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     */
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     */
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
307    public void setIcon(Drawable icon) {
308        mIcon = icon;
309        if ((mIconView != null) && (mIcon != null)) {
310            mIconView.setImageDrawable(icon);
311        }
312    }
313
314    public void setInverseBackgroundForced(boolean forceInverseBackground) {
315        mForceInverseBackground = forceInverseBackground;
316    }
317
318    public ListView getListView() {
319        return mListView;
320    }
321
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
335    public boolean onKeyDown(int keyCode, KeyEvent event) {
336        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
337        return false;
338    }
339
340    public boolean onKeyUp(int keyCode, KeyEvent event) {
341        if (mScrollView != null && mScrollView.executeKeyEvent(event)) return true;
342        return false;
343    }
344
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, WRAP_CONTENT));
365            if (mViewSpacingSpecified) {
366                custom.setPadding(mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
367                        mViewSpacingBottom);
368            }
369        } else {
370            mWindow.findViewById(R.id.customPanel).setVisibility(View.GONE);
371        }
372
373        /* Only display the divider if we have a title and a
374         * custom view or a message.
375         */
376        if (hasTitle && ((mMessage != null) || (mView != null))) {
377            View divider = mWindow.findViewById(R.id.titleDivider);
378            divider.setVisibility(View.VISIBLE);
379        }
380
381        setBackground(topPanel, contentPanel, customPanel, hasButtons, a, hasTitle, buttonPanel);
382        a.recycle();
383    }
384
385    private boolean setupTitle(LinearLayout topPanel) {
386        boolean hasTitle = true;
387
388        if (mCustomTitleView != null) {
389            // Add the custom title view directly to the topPanel layout
390            LinearLayout.LayoutParams lp = new LinearLayout.LayoutParams(
391                    LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.WRAP_CONTENT);
392
393            topPanel.addView(mCustomTitleView, lp);
394
395            // Hide the title template
396            View titleTemplate = mWindow.findViewById(R.id.title_template);
397            titleTemplate.setVisibility(View.GONE);
398        } else {
399            final boolean hasTextTitle = !TextUtils.isEmpty(mTitle);
400
401            mIconView = (ImageView) mWindow.findViewById(R.id.icon);
402            if (hasTextTitle) {
403
404                /* Display the title if a title is supplied, else hide it */
405                mTitleView = (TextView) mWindow.findViewById(R.id.alertTitle);
406
407                mTitleView.setText(mTitle);
408                mIconView.setImageResource(R.drawable.ic_dialog_menu_generic);
409
410                /* Do this last so that if the user has supplied any
411                 * icons we use them instead of the default ones. If the
412                 * user has specified 0 then make it disappear.
413                 */
414                if (mIconId > 0) {
415                    mIconView.setImageResource(mIconId);
416                } else if (mIcon != null) {
417                    mIconView.setImageDrawable(mIcon);
418                } else if (mIconId == 0) {
419
420                    /* Apply the padding from the icon to ensure the
421                     * title is aligned correctly.
422                     */
423                    mTitleView.setPadding(mIconView.getPaddingLeft(),
424                            mIconView.getPaddingTop(),
425                            mIconView.getPaddingRight(),
426                            mIconView.getPaddingBottom());
427                    mIconView.setVisibility(View.GONE);
428                }
429            } else {
430
431                // Hide the title template
432                View titleTemplate = mWindow.findViewById(R.id.title_template);
433                titleTemplate.setVisibility(View.GONE);
434                mIconView.setVisibility(View.GONE);
435                hasTitle = false;
436            }
437        }
438        return hasTitle;
439    }
440
441    private void setupContent(LinearLayout contentPanel) {
442        mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
443        mScrollView.setFocusable(false);
444
445        // Special case for users that only want to display a String
446        mMessageView = (TextView) mWindow.findViewById(R.id.message);
447        if (mMessageView == null) {
448            return;
449        }
450
451        if (mMessage != null) {
452            mMessageView.setText(mMessage);
453        } else {
454            mMessageView.setVisibility(View.GONE);
455            mScrollView.removeView(mMessageView);
456
457            if (mListView != null) {
458                contentPanel.removeView(mWindow.findViewById(R.id.scrollView));
459                contentPanel.addView(mListView, new LinearLayout.LayoutParams(FILL_PARENT, WRAP_CONTENT));
460            } else {
461                contentPanel.setVisibility(View.GONE);
462            }
463        }
464    }
465
466    private boolean setupButtons() {
467        View defaultButton = null;
468        int BIT_BUTTON_POSITIVE = 1;
469        int BIT_BUTTON_NEGATIVE = 2;
470        int BIT_BUTTON_NEUTRAL = 4;
471        int whichButtons = 0;
472        mButtonPositive = (Button) mWindow.findViewById(R.id.button1);
473        mButtonPositive.setOnClickListener(mButtonHandler);
474
475        if (TextUtils.isEmpty(mButtonPositiveText)) {
476            mButtonPositive.setVisibility(View.GONE);
477        } else {
478            mButtonPositive.setText(mButtonPositiveText);
479            mButtonPositive.setVisibility(View.VISIBLE);
480            defaultButton = mButtonPositive;
481            whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
482        }
483
484        mButtonNegative = (Button) mWindow.findViewById(R.id.button2);
485        mButtonNegative.setOnClickListener(mButtonHandler);
486
487        if (TextUtils.isEmpty(mButtonNegativeText)) {
488            mButtonNegative.setVisibility(View.GONE);
489        } else {
490            mButtonNegative.setText(mButtonNegativeText);
491            mButtonNegative.setVisibility(View.VISIBLE);
492
493            if (defaultButton == null) {
494                defaultButton = mButtonNegative;
495            }
496            whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
497        }
498
499        mButtonNeutral = (Button) mWindow.findViewById(R.id.button3);
500        mButtonNeutral.setOnClickListener(mButtonHandler);
501
502        if (TextUtils.isEmpty(mButtonNeutralText)) {
503            mButtonNeutral.setVisibility(View.GONE);
504        } else {
505            mButtonNeutral.setText(mButtonNeutralText);
506            mButtonNeutral.setVisibility(View.VISIBLE);
507
508            if (defaultButton == null) {
509                defaultButton = mButtonNeutral;
510            }
511            whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
512        }
513
514        /*
515         * If we only have 1 button it should be centered on the layout and
516         * expand to fill 50% of the available space.
517         */
518        if (whichButtons == BIT_BUTTON_POSITIVE) {
519            centerButton(mButtonPositive);
520        } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
521            centerButton(mButtonNeutral);
522        } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
523            centerButton(mButtonNeutral);
524        }
525
526        return whichButtons != 0;
527    }
528
529    private void centerButton(Button button) {
530        LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
531        params.gravity = Gravity.CENTER_HORIZONTAL;
532        params.weight = 0.5f;
533        button.setLayoutParams(params);
534        View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
535        leftSpacer.setVisibility(View.VISIBLE);
536        View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
537        rightSpacer.setVisibility(View.VISIBLE);
538    }
539
540    private void setBackground(LinearLayout topPanel, LinearLayout contentPanel,
541            View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle,
542            View buttonPanel) {
543
544        /* Get all the different background required */
545        int fullDark = a.getResourceId(
546                R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
547        int topDark = a.getResourceId(
548                R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
549        int centerDark = a.getResourceId(
550                R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
551        int bottomDark = a.getResourceId(
552                R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
553        int fullBright = a.getResourceId(
554                R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
555        int topBright = a.getResourceId(
556                R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
557        int centerBright = a.getResourceId(
558                R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
559        int bottomBright = a.getResourceId(
560                R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
561        int bottomMedium = a.getResourceId(
562                R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
563        int centerMedium = a.getResourceId(
564                R.styleable.AlertDialog_centerMedium, R.drawable.popup_center_medium);
565
566        /*
567         * We now set the background of all of the sections of the alert.
568         * First collect together each section that is being displayed along
569         * with whether it is on a light or dark background, then run through
570         * them setting their backgrounds.  This is complicated because we need
571         * to correctly use the full, top, middle, and bottom graphics depending
572         * on how many views they are and where they appear.
573         */
574
575        View[] views = new View[4];
576        boolean[] light = new boolean[4];
577        View lastView = null;
578        boolean lastLight = false;
579
580        int pos = 0;
581        if (hasTitle) {
582            views[pos] = topPanel;
583            light[pos] = false;
584            pos++;
585        }
586
587        /* The contentPanel displays either a custom text message or
588         * a ListView. If it's text we should use the dark background
589         * for ListView we should use the light background. If neither
590         * are there the contentPanel will be hidden so set it as null.
591         */
592        views[pos] = (contentPanel.getVisibility() == View.GONE)
593                ? null : contentPanel;
594        light[pos] = mListView == null ? false : true;
595        pos++;
596        if (customPanel != null) {
597            views[pos] = customPanel;
598            light[pos] = mForceInverseBackground;
599            pos++;
600        }
601        if (hasButtons) {
602            views[pos] = buttonPanel;
603            light[pos] = true;
604        }
605
606        boolean setView = false;
607        for (pos=0; pos<views.length; pos++) {
608            View v = views[pos];
609            if (v == null) {
610                continue;
611            }
612            if (lastView != null) {
613                if (!setView) {
614                    lastView.setBackgroundResource(lastLight ? topBright : topDark);
615                } else {
616                    lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
617                }
618                setView = true;
619            }
620            lastView = v;
621            lastLight = light[pos];
622        }
623
624        if (lastView != null) {
625            if (setView) {
626
627                /* ListViews will use the Bright background but buttons use
628                 * the Medium background.
629                 */
630                lastView.setBackgroundResource(
631                        lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
632            } else {
633                lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
634            }
635        }
636
637        /* TODO: uncomment section below. The logic for this should be if
638         * it's a Contextual menu being displayed AND only a Cancel button
639         * is shown then do this.
640         */
641//        if (hasButtons && (mListView != null)) {
642
643            /* Yet another *special* case. If there is a ListView with buttons
644             * don't put the buttons on the bottom but instead put them in the
645             * footer of the ListView this will allow more items to be
646             * displayed.
647             */
648
649            /*
650            contentPanel.setBackgroundResource(bottomBright);
651            buttonPanel.setBackgroundResource(centerMedium);
652            ViewGroup parent = (ViewGroup) mWindow.findViewById(R.id.parentPanel);
653            parent.removeView(buttonPanel);
654            AbsListView.LayoutParams params = new AbsListView.LayoutParams(
655                    AbsListView.LayoutParams.FILL_PARENT,
656                    AbsListView.LayoutParams.FILL_PARENT);
657            buttonPanel.setLayoutParams(params);
658            mListView.addFooterView(buttonPanel);
659            */
660//        }
661
662        if ((mListView != null) && (mAdapter != null)) {
663            mListView.setAdapter(mAdapter);
664            if (mCheckedItem > -1) {
665                mListView.setItemChecked(mCheckedItem, true);
666                mListView.setSelection(mCheckedItem);
667            }
668        }
669    }
670
671    public static class AlertParams {
672        public final Context mContext;
673        public final LayoutInflater mInflater;
674
675        public int mIconId = -1;
676        public Drawable mIcon;
677        public CharSequence mTitle;
678        public View mCustomTitleView;
679        public CharSequence mMessage;
680        public CharSequence mPositiveButtonText;
681        public DialogInterface.OnClickListener mPositiveButtonListener;
682        public CharSequence mNegativeButtonText;
683        public DialogInterface.OnClickListener mNegativeButtonListener;
684        public CharSequence mNeutralButtonText;
685        public DialogInterface.OnClickListener mNeutralButtonListener;
686        public boolean mCancelable;
687        public DialogInterface.OnCancelListener mOnCancelListener;
688        public DialogInterface.OnKeyListener mOnKeyListener;
689        public CharSequence[] mItems;
690        public ListAdapter mAdapter;
691        public DialogInterface.OnClickListener mOnClickListener;
692        public View mView;
693        public int mViewSpacingLeft;
694        public int mViewSpacingTop;
695        public int mViewSpacingRight;
696        public int mViewSpacingBottom;
697        public boolean mViewSpacingSpecified = false;
698        public boolean[] mCheckedItems;
699        public boolean mIsMultiChoice;
700        public boolean mIsSingleChoice;
701        public int mCheckedItem = -1;
702        public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
703        public Cursor mCursor;
704        public String mLabelColumn;
705        public String mIsCheckedColumn;
706        public boolean mForceInverseBackground;
707        public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
708        public OnPrepareListViewListener mOnPrepareListViewListener;
709
710        /**
711         * Interface definition for a callback to be invoked before the ListView
712         * will be bound to an adapter.
713         */
714        public interface OnPrepareListViewListener {
715
716            /**
717             * Called before the ListView is bound to an adapter.
718             * @param listView The ListView that will be shown in the dialog.
719             */
720            void onPrepareListView(ListView listView);
721        }
722
723        public AlertParams(Context context) {
724            mContext = context;
725            mCancelable = true;
726            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
727        }
728
729        public void apply(AlertController dialog) {
730            if (mCustomTitleView != null) {
731                dialog.setCustomTitle(mCustomTitleView);
732            } else {
733                if (mTitle != null) {
734                    dialog.setTitle(mTitle);
735                }
736                if (mIcon != null) {
737                    dialog.setIcon(mIcon);
738                }
739                if (mIconId >= 0) {
740                    dialog.setIcon(mIconId);
741                }
742            }
743            if (mMessage != null) {
744                dialog.setMessage(mMessage);
745            }
746            if (mPositiveButtonText != null) {
747                dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
748                        mPositiveButtonListener, null);
749            }
750            if (mNegativeButtonText != null) {
751                dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
752                        mNegativeButtonListener, null);
753            }
754            if (mNeutralButtonText != null) {
755                dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
756                        mNeutralButtonListener, null);
757            }
758            if (mForceInverseBackground) {
759                dialog.setInverseBackgroundForced(true);
760            }
761            // For a list, the client can either supply an array of items or an
762            // adapter or a cursor
763            if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
764                createListView(dialog);
765            }
766            if (mView != null) {
767                if (mViewSpacingSpecified) {
768                    dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
769                            mViewSpacingBottom);
770                } else {
771                    dialog.setView(mView);
772                }
773            }
774
775            /*
776            dialog.setCancelable(mCancelable);
777            dialog.setOnCancelListener(mOnCancelListener);
778            if (mOnKeyListener != null) {
779                dialog.setOnKeyListener(mOnKeyListener);
780            }
781            */
782        }
783
784        private void createListView(final AlertController dialog) {
785            final ListView listView = (ListView) mInflater.inflate(R.layout.select_dialog, null);
786            ListAdapter adapter;
787
788            if (mIsMultiChoice) {
789                if (mCursor == null) {
790                    adapter = new ArrayAdapter<CharSequence>(
791                            mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) {
792                        @Override
793                        public View getView(int position, View convertView, ViewGroup parent) {
794                            View view = super.getView(position, convertView, parent);
795                            if (mCheckedItems != null) {
796                                boolean isItemChecked = mCheckedItems[position];
797                                if (isItemChecked) {
798                                    listView.setItemChecked(position, true);
799                                }
800                            }
801                            return view;
802                        }
803                    };
804                } else {
805                    adapter = new CursorAdapter(mContext, mCursor, false) {
806                        private final int mLabelIndex;
807                        private final int mIsCheckedIndex;
808
809                        {
810                            final Cursor cursor = getCursor();
811                            mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
812                            mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
813                        }
814
815                        @Override
816                        public void bindView(View view, Context context, Cursor cursor) {
817                            CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
818                            text.setText(cursor.getString(mLabelIndex));
819                            listView.setItemChecked(cursor.getPosition(),
820                                    cursor.getInt(mIsCheckedIndex) == 1);
821                        }
822
823                        @Override
824                        public View newView(Context context, Cursor cursor, ViewGroup parent) {
825                            return mInflater.inflate(R.layout.select_dialog_multichoice,
826                                    parent, false);
827                        }
828
829                    };
830                }
831            } else {
832                int layout = mIsSingleChoice
833                        ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item;
834                if (mCursor == null) {
835                    adapter = (mAdapter != null) ? mAdapter
836                            : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
837                } else {
838                    adapter = new SimpleCursorAdapter(mContext, layout,
839                            mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
840                }
841            }
842
843            if (mOnPrepareListViewListener != null) {
844                mOnPrepareListViewListener.onPrepareListView(listView);
845            }
846
847            /* Don't directly set the adapter on the ListView as we might
848             * want to add a footer to the ListView later.
849             */
850            dialog.mAdapter = adapter;
851            dialog.mCheckedItem = mCheckedItem;
852
853            if (mOnClickListener != null) {
854                listView.setOnItemClickListener(new OnItemClickListener() {
855                    public void onItemClick(AdapterView parent, View v, int position, long id) {
856                        mOnClickListener.onClick(dialog.mDialogInterface, position);
857                        if (!mIsSingleChoice) {
858                            dialog.mDialogInterface.dismiss();
859                        }
860                    }
861                });
862            } else if (mOnCheckboxClickListener != null) {
863                listView.setOnItemClickListener(new OnItemClickListener() {
864                    public void onItemClick(AdapterView parent, View v, int position, long id) {
865                        if (mCheckedItems != null) {
866                            mCheckedItems[position] = listView.isItemChecked(position);
867                        }
868                        mOnCheckboxClickListener.onClick(
869                                dialog.mDialogInterface, position, listView.isItemChecked(position));
870                    }
871                });
872            }
873
874            // Attach a given OnItemSelectedListener to the ListView
875            if (mOnItemSelectedListener != null) {
876                listView.setOnItemSelectedListener(mOnItemSelectedListener);
877            }
878
879            if (mIsSingleChoice) {
880                listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
881            } else if (mIsMultiChoice) {
882                listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
883            }
884            dialog.mListView = listView;
885        }
886    }
887
888}
889