AlertController.java revision 980a938c1c9a6a5791a8240e5a1e6638ab28dc77
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.MATCH_PARENT;
20import android.app.AlertDialog;
21import android.content.Context;
22import android.content.DialogInterface;
23import android.content.res.TypedArray;
24import android.database.Cursor;
25import android.graphics.drawable.Drawable;
26import android.os.Handler;
27import android.os.Message;
28import android.text.TextUtils;
29import android.view.Gravity;
30import android.view.KeyEvent;
31import android.view.LayoutInflater;
32import android.view.View;
33import android.view.ViewGroup;
34import android.view.Window;
35import android.view.WindowManager;
36import android.view.ViewGroup.LayoutParams;
37import android.widget.AdapterView;
38import android.widget.ArrayAdapter;
39import android.widget.Button;
40import android.widget.CheckedTextView;
41import android.widget.CursorAdapter;
42import android.widget.FrameLayout;
43import android.widget.ImageView;
44import android.widget.LinearLayout;
45import android.widget.ListAdapter;
46import android.widget.ListView;
47import android.widget.ScrollView;
48import android.widget.SimpleCursorAdapter;
49import android.widget.TextView;
50import android.widget.AdapterView.OnItemClickListener;
51import android.util.AttributeSet;
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(MATCH_PARENT, MATCH_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
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.MATCH_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
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(MATCH_PARENT, MATCH_PARENT));
464                contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
465            } else {
466                contentPanel.setVisibility(View.GONE);
467            }
468        }
469    }
470
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
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
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.MATCH_PARENT,
661                    AbsListView.LayoutParams.MATCH_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
679        public RecycleListView(Context context) {
680            super(context);
681        }
682
683        public RecycleListView(Context context, AttributeSet attrs) {
684            super(context, attrs);
685        }
686
687        public RecycleListView(Context context, AttributeSet attrs, int defStyle) {
688            super(context, attrs, defStyle);
689        }
690
691        @Override
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             */
747            void onPrepareListView(ListView listView);
748        }
749
750        public AlertParams(Context context) {
751            mContext = context;
752            mCancelable = true;
753            mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
754        }
755
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
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