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