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.view.Gravity;
     34 import android.view.KeyEvent;
     35 import android.view.LayoutInflater;
     36 import android.view.View;
     37 import android.view.ViewGroup;
     38 import android.view.ViewGroup.LayoutParams;
     39 import android.view.Window;
     40 import android.view.WindowManager;
     41 import android.widget.AdapterView;
     42 import android.widget.AdapterView.OnItemClickListener;
     43 import android.widget.ArrayAdapter;
     44 import android.widget.Button;
     45 import android.widget.CheckedTextView;
     46 import android.widget.CursorAdapter;
     47 import android.widget.FrameLayout;
     48 import android.widget.ImageView;
     49 import android.widget.LinearLayout;
     50 import android.widget.ListAdapter;
     51 import android.widget.ListView;
     52 import android.widget.ScrollView;
     53 import android.widget.SimpleCursorAdapter;
     54 import android.widget.TextView;
     55 
     56 import java.lang.ref.WeakReference;
     57 
     58 public 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                 hasTitle = false;
    438             }
    439         }
    440         return hasTitle;
    441     }
    442 
    443     private void setupContent(LinearLayout contentPanel) {
    444         mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView);
    445         mScrollView.setFocusable(false);
    446 
    447         // Special case for users that only want to display a String
    448         mMessageView = (TextView) mWindow.findViewById(R.id.message);
    449         if (mMessageView == null) {
    450             return;
    451         }
    452 
    453         if (mMessage != null) {
    454             mMessageView.setText(mMessage);
    455         } else {
    456             mMessageView.setVisibility(View.GONE);
    457             mScrollView.removeView(mMessageView);
    458 
    459             if (mListView != null) {
    460                 contentPanel.removeView(mWindow.findViewById(R.id.scrollView));
    461                 contentPanel.addView(mListView,
    462                         new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT));
    463                 contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f));
    464             } else {
    465                 contentPanel.setVisibility(View.GONE);
    466             }
    467         }
    468     }
    469 
    470     private boolean setupButtons() {
    471         int BIT_BUTTON_POSITIVE = 1;
    472         int BIT_BUTTON_NEGATIVE = 2;
    473         int BIT_BUTTON_NEUTRAL = 4;
    474         int whichButtons = 0;
    475         mButtonPositive = (Button) mWindow.findViewById(R.id.button1);
    476         mButtonPositive.setOnClickListener(mButtonHandler);
    477 
    478         if (TextUtils.isEmpty(mButtonPositiveText)) {
    479             mButtonPositive.setVisibility(View.GONE);
    480         } else {
    481             mButtonPositive.setText(mButtonPositiveText);
    482             mButtonPositive.setVisibility(View.VISIBLE);
    483             whichButtons = whichButtons | BIT_BUTTON_POSITIVE;
    484         }
    485 
    486         mButtonNegative = (Button) mWindow.findViewById(R.id.button2);
    487         mButtonNegative.setOnClickListener(mButtonHandler);
    488 
    489         if (TextUtils.isEmpty(mButtonNegativeText)) {
    490             mButtonNegative.setVisibility(View.GONE);
    491         } else {
    492             mButtonNegative.setText(mButtonNegativeText);
    493             mButtonNegative.setVisibility(View.VISIBLE);
    494 
    495             whichButtons = whichButtons | BIT_BUTTON_NEGATIVE;
    496         }
    497 
    498         mButtonNeutral = (Button) mWindow.findViewById(R.id.button3);
    499         mButtonNeutral.setOnClickListener(mButtonHandler);
    500 
    501         if (TextUtils.isEmpty(mButtonNeutralText)) {
    502             mButtonNeutral.setVisibility(View.GONE);
    503         } else {
    504             mButtonNeutral.setText(mButtonNeutralText);
    505             mButtonNeutral.setVisibility(View.VISIBLE);
    506 
    507             whichButtons = whichButtons | BIT_BUTTON_NEUTRAL;
    508         }
    509 
    510         /*
    511          * If we only have 1 button it should be centered on the layout and
    512          * expand to fill 50% of the available space.
    513          */
    514         if (whichButtons == BIT_BUTTON_POSITIVE) {
    515             centerButton(mButtonPositive);
    516         } else if (whichButtons == BIT_BUTTON_NEGATIVE) {
    517             centerButton(mButtonNeutral);
    518         } else if (whichButtons == BIT_BUTTON_NEUTRAL) {
    519             centerButton(mButtonNeutral);
    520         }
    521 
    522         return whichButtons != 0;
    523     }
    524 
    525     private void centerButton(Button button) {
    526         LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams();
    527         params.gravity = Gravity.CENTER_HORIZONTAL;
    528         params.weight = 0.5f;
    529         button.setLayoutParams(params);
    530         View leftSpacer = mWindow.findViewById(R.id.leftSpacer);
    531         leftSpacer.setVisibility(View.VISIBLE);
    532         View rightSpacer = mWindow.findViewById(R.id.rightSpacer);
    533         rightSpacer.setVisibility(View.VISIBLE);
    534     }
    535 
    536     private void setBackground(LinearLayout topPanel, LinearLayout contentPanel,
    537             View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle,
    538             View buttonPanel) {
    539 
    540         /* Get all the different background required */
    541         int fullDark = a.getResourceId(
    542                 R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark);
    543         int topDark = a.getResourceId(
    544                 R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark);
    545         int centerDark = a.getResourceId(
    546                 R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark);
    547         int bottomDark = a.getResourceId(
    548                 R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark);
    549         int fullBright = a.getResourceId(
    550                 R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright);
    551         int topBright = a.getResourceId(
    552                 R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright);
    553         int centerBright = a.getResourceId(
    554                 R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright);
    555         int bottomBright = a.getResourceId(
    556                 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright);
    557         int bottomMedium = a.getResourceId(
    558                 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium);
    559 
    560         /*
    561          * We now set the background of all of the sections of the alert.
    562          * First collect together each section that is being displayed along
    563          * with whether it is on a light or dark background, then run through
    564          * them setting their backgrounds.  This is complicated because we need
    565          * to correctly use the full, top, middle, and bottom graphics depending
    566          * on how many views they are and where they appear.
    567          */
    568 
    569         View[] views = new View[4];
    570         boolean[] light = new boolean[4];
    571         View lastView = null;
    572         boolean lastLight = false;
    573 
    574         int pos = 0;
    575         if (hasTitle) {
    576             views[pos] = topPanel;
    577             light[pos] = false;
    578             pos++;
    579         }
    580 
    581         /* The contentPanel displays either a custom text message or
    582          * a ListView. If it's text we should use the dark background
    583          * for ListView we should use the light background. If neither
    584          * are there the contentPanel will be hidden so set it as null.
    585          */
    586         views[pos] = (contentPanel.getVisibility() == View.GONE)
    587                 ? null : contentPanel;
    588         light[pos] = mListView != null;
    589         pos++;
    590         if (customPanel != null) {
    591             views[pos] = customPanel;
    592             light[pos] = mForceInverseBackground;
    593             pos++;
    594         }
    595         if (hasButtons) {
    596             views[pos] = buttonPanel;
    597             light[pos] = true;
    598         }
    599 
    600         boolean setView = false;
    601         for (pos=0; pos<views.length; pos++) {
    602             View v = views[pos];
    603             if (v == null) {
    604                 continue;
    605             }
    606             if (lastView != null) {
    607                 if (!setView) {
    608                     lastView.setBackgroundResource(lastLight ? topBright : topDark);
    609                 } else {
    610                     lastView.setBackgroundResource(lastLight ? centerBright : centerDark);
    611                 }
    612                 setView = true;
    613             }
    614             lastView = v;
    615             lastLight = light[pos];
    616         }
    617 
    618         if (lastView != null) {
    619             if (setView) {
    620 
    621                 /* ListViews will use the Bright background but buttons use
    622                  * the Medium background.
    623                  */
    624                 lastView.setBackgroundResource(
    625                         lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark);
    626             } else {
    627                 lastView.setBackgroundResource(lastLight ? fullBright : fullDark);
    628             }
    629         }
    630 
    631         /* TODO: uncomment section below. The logic for this should be if
    632          * it's a Contextual menu being displayed AND only a Cancel button
    633          * is shown then do this.
    634          */
    635 //        if (hasButtons && (mListView != null)) {
    636 
    637             /* Yet another *special* case. If there is a ListView with buttons
    638              * don't put the buttons on the bottom but instead put them in the
    639              * footer of the ListView this will allow more items to be
    640              * displayed.
    641              */
    642 
    643             /*
    644             contentPanel.setBackgroundResource(bottomBright);
    645             buttonPanel.setBackgroundResource(centerMedium);
    646             ViewGroup parent = (ViewGroup) mWindow.findViewById(R.id.parentPanel);
    647             parent.removeView(buttonPanel);
    648             AbsListView.LayoutParams params = new AbsListView.LayoutParams(
    649                     AbsListView.LayoutParams.MATCH_PARENT,
    650                     AbsListView.LayoutParams.MATCH_PARENT);
    651             buttonPanel.setLayoutParams(params);
    652             mListView.addFooterView(buttonPanel);
    653             */
    654 //        }
    655 
    656         if ((mListView != null) && (mAdapter != null)) {
    657             mListView.setAdapter(mAdapter);
    658             if (mCheckedItem > -1) {
    659                 mListView.setItemChecked(mCheckedItem, true);
    660                 mListView.setSelection(mCheckedItem);
    661             }
    662         }
    663     }
    664 
    665     public static class RecycleListView extends ListView {
    666         boolean mRecycleOnMeasure = true;
    667 
    668         public RecycleListView(Context context) {
    669             super(context);
    670         }
    671 
    672         public RecycleListView(Context context, AttributeSet attrs) {
    673             super(context, attrs);
    674         }
    675 
    676         public RecycleListView(Context context, AttributeSet attrs, int defStyle) {
    677             super(context, attrs, defStyle);
    678         }
    679 
    680         @Override
    681         protected boolean recycleOnMeasure() {
    682             return mRecycleOnMeasure;
    683         }
    684     }
    685 
    686     public static class AlertParams {
    687         public final Context mContext;
    688         public final LayoutInflater mInflater;
    689 
    690         public int mIconId = 0;
    691         public Drawable mIcon;
    692         public CharSequence mTitle;
    693         public View mCustomTitleView;
    694         public CharSequence mMessage;
    695         public CharSequence mPositiveButtonText;
    696         public DialogInterface.OnClickListener mPositiveButtonListener;
    697         public CharSequence mNegativeButtonText;
    698         public DialogInterface.OnClickListener mNegativeButtonListener;
    699         public CharSequence mNeutralButtonText;
    700         public DialogInterface.OnClickListener mNeutralButtonListener;
    701         public boolean mCancelable;
    702         public DialogInterface.OnCancelListener mOnCancelListener;
    703         public DialogInterface.OnKeyListener mOnKeyListener;
    704         public CharSequence[] mItems;
    705         public ListAdapter mAdapter;
    706         public DialogInterface.OnClickListener mOnClickListener;
    707         public View mView;
    708         public int mViewSpacingLeft;
    709         public int mViewSpacingTop;
    710         public int mViewSpacingRight;
    711         public int mViewSpacingBottom;
    712         public boolean mViewSpacingSpecified = false;
    713         public boolean[] mCheckedItems;
    714         public boolean mIsMultiChoice;
    715         public boolean mIsSingleChoice;
    716         public int mCheckedItem = -1;
    717         public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener;
    718         public Cursor mCursor;
    719         public String mLabelColumn;
    720         public String mIsCheckedColumn;
    721         public boolean mForceInverseBackground;
    722         public AdapterView.OnItemSelectedListener mOnItemSelectedListener;
    723         public OnPrepareListViewListener mOnPrepareListViewListener;
    724         public boolean mRecycleOnMeasure = true;
    725 
    726         /**
    727          * Interface definition for a callback to be invoked before the ListView
    728          * will be bound to an adapter.
    729          */
    730         public interface OnPrepareListViewListener {
    731 
    732             /**
    733              * Called before the ListView is bound to an adapter.
    734              * @param listView The ListView that will be shown in the dialog.
    735              */
    736             void onPrepareListView(ListView listView);
    737         }
    738 
    739         public AlertParams(Context context) {
    740             mContext = context;
    741             mCancelable = true;
    742             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
    743         }
    744 
    745         public void apply(AlertController dialog) {
    746             if (mCustomTitleView != null) {
    747                 dialog.setCustomTitle(mCustomTitleView);
    748             } else {
    749                 if (mTitle != null) {
    750                     dialog.setTitle(mTitle);
    751                 }
    752                 if (mIcon != null) {
    753                     dialog.setIcon(mIcon);
    754                 }
    755                 if (mIconId >= 0) {
    756                     dialog.setIcon(mIconId);
    757                 }
    758             }
    759             if (mMessage != null) {
    760                 dialog.setMessage(mMessage);
    761             }
    762             if (mPositiveButtonText != null) {
    763                 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText,
    764                         mPositiveButtonListener, null);
    765             }
    766             if (mNegativeButtonText != null) {
    767                 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText,
    768                         mNegativeButtonListener, null);
    769             }
    770             if (mNeutralButtonText != null) {
    771                 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText,
    772                         mNeutralButtonListener, null);
    773             }
    774             if (mForceInverseBackground) {
    775                 dialog.setInverseBackgroundForced(true);
    776             }
    777             // For a list, the client can either supply an array of items or an
    778             // adapter or a cursor
    779             if ((mItems != null) || (mCursor != null) || (mAdapter != null)) {
    780                 createListView(dialog);
    781             }
    782             if (mView != null) {
    783                 if (mViewSpacingSpecified) {
    784                     dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight,
    785                             mViewSpacingBottom);
    786                 } else {
    787                     dialog.setView(mView);
    788                 }
    789             }
    790 
    791             /*
    792             dialog.setCancelable(mCancelable);
    793             dialog.setOnCancelListener(mOnCancelListener);
    794             if (mOnKeyListener != null) {
    795                 dialog.setOnKeyListener(mOnKeyListener);
    796             }
    797             */
    798         }
    799 
    800         private void createListView(final AlertController dialog) {
    801             final RecycleListView listView = (RecycleListView)
    802                     mInflater.inflate(R.layout.select_dialog, null);
    803             ListAdapter adapter;
    804 
    805             if (mIsMultiChoice) {
    806                 if (mCursor == null) {
    807                     adapter = new ArrayAdapter<CharSequence>(
    808                             mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) {
    809                         @Override
    810                         public View getView(int position, View convertView, ViewGroup parent) {
    811                             View view = super.getView(position, convertView, parent);
    812                             if (mCheckedItems != null) {
    813                                 boolean isItemChecked = mCheckedItems[position];
    814                                 if (isItemChecked) {
    815                                     listView.setItemChecked(position, true);
    816                                 }
    817                             }
    818                             return view;
    819                         }
    820                     };
    821                 } else {
    822                     adapter = new CursorAdapter(mContext, mCursor, false) {
    823                         private final int mLabelIndex;
    824                         private final int mIsCheckedIndex;
    825 
    826                         {
    827                             final Cursor cursor = getCursor();
    828                             mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn);
    829                             mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn);
    830                         }
    831 
    832                         @Override
    833                         public void bindView(View view, Context context, Cursor cursor) {
    834                             CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1);
    835                             text.setText(cursor.getString(mLabelIndex));
    836                             listView.setItemChecked(cursor.getPosition(),
    837                                     cursor.getInt(mIsCheckedIndex) == 1);
    838                         }
    839 
    840                         @Override
    841                         public View newView(Context context, Cursor cursor, ViewGroup parent) {
    842                             return mInflater.inflate(R.layout.select_dialog_multichoice,
    843                                     parent, false);
    844                         }
    845 
    846                     };
    847                 }
    848             } else {
    849                 int layout = mIsSingleChoice
    850                         ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item;
    851                 if (mCursor == null) {
    852                     adapter = (mAdapter != null) ? mAdapter
    853                             : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems);
    854                 } else {
    855                     adapter = new SimpleCursorAdapter(mContext, layout,
    856                             mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1});
    857                 }
    858             }
    859 
    860             if (mOnPrepareListViewListener != null) {
    861                 mOnPrepareListViewListener.onPrepareListView(listView);
    862             }
    863 
    864             /* Don't directly set the adapter on the ListView as we might
    865              * want to add a footer to the ListView later.
    866              */
    867             dialog.mAdapter = adapter;
    868             dialog.mCheckedItem = mCheckedItem;
    869 
    870             if (mOnClickListener != null) {
    871                 listView.setOnItemClickListener(new OnItemClickListener() {
    872                     public void onItemClick(AdapterView parent, View v, int position, long id) {
    873                         mOnClickListener.onClick(dialog.mDialogInterface, position);
    874                         if (!mIsSingleChoice) {
    875                             dialog.mDialogInterface.dismiss();
    876                         }
    877                     }
    878                 });
    879             } else if (mOnCheckboxClickListener != null) {
    880                 listView.setOnItemClickListener(new OnItemClickListener() {
    881                     public void onItemClick(AdapterView parent, View v, int position, long id) {
    882                         if (mCheckedItems != null) {
    883                             mCheckedItems[position] = listView.isItemChecked(position);
    884                         }
    885                         mOnCheckboxClickListener.onClick(
    886                                 dialog.mDialogInterface, position, listView.isItemChecked(position));
    887                     }
    888                 });
    889             }
    890 
    891             // Attach a given OnItemSelectedListener to the ListView
    892             if (mOnItemSelectedListener != null) {
    893                 listView.setOnItemSelectedListener(mOnItemSelectedListener);
    894             }
    895 
    896             if (mIsSingleChoice) {
    897                 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE);
    898             } else if (mIsMultiChoice) {
    899                 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE);
    900             }
    901             listView.mRecycleOnMeasure = mRecycleOnMeasure;
    902             dialog.mListView = listView;
    903         }
    904     }
    905 
    906 }
    907