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