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