• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ButtonHandler(DialogInterface dialog)148         public ButtonHandler(DialogInterface dialog) {
149             mDialog = new WeakReference<DialogInterface>(dialog);
150         }
151 
152         @Override
handleMessage(Message msg)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 
AlertController(Context context, DialogInterface di, Window window)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 
canTextInput(View v)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 
installContent()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 
setTitle(CharSequence title)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      */
setCustomTitle(View customTitleView)219     public void setCustomTitle(View customTitleView) {
220         mCustomTitleView = customTitleView;
221     }
222 
setMessage(CharSequence message)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      */
setView(View view)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      */
setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)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      */
setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener, Message msg)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      */
setIcon(int resId)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 
setIcon(Drawable icon)308     public void setIcon(Drawable icon) {
309         mIcon = icon;
310         if ((mIconView != null) && (mIcon != null)) {
311             mIconView.setImageDrawable(icon);
312         }
313     }
314 
setInverseBackgroundForced(boolean forceInverseBackground)315     public void setInverseBackgroundForced(boolean forceInverseBackground) {
316         mForceInverseBackground = forceInverseBackground;
317     }
318 
getListView()319     public ListView getListView() {
320         return mListView;
321     }
322 
getButton(int whichButton)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"})
onKeyDown(int keyCode, KeyEvent event)337     public boolean onKeyDown(int keyCode, KeyEvent event) {
338         return mScrollView != null && mScrollView.executeKeyEvent(event);
339     }
340 
341     @SuppressWarnings({"UnusedDeclaration"})
onKeyUp(int keyCode, KeyEvent event)342     public boolean onKeyUp(int keyCode, KeyEvent event) {
343         return mScrollView != null && mScrollView.executeKeyEvent(event);
344     }
345 
setupView()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 
setupTitle(LinearLayout topPanel)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 
setupContent(LinearLayout contentPanel)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 
setupButtons()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 
centerButton(Button button)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 
setBackground(LinearLayout topPanel, LinearLayout contentPanel, View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle, View buttonPanel)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 
RecycleListView(Context context)668         public RecycleListView(Context context) {
669             super(context);
670         }
671 
RecycleListView(Context context, AttributeSet attrs)672         public RecycleListView(Context context, AttributeSet attrs) {
673             super(context, attrs);
674         }
675 
RecycleListView(Context context, AttributeSet attrs, int defStyle)676         public RecycleListView(Context context, AttributeSet attrs, int defStyle) {
677             super(context, attrs, defStyle);
678         }
679 
680         @Override
recycleOnMeasure()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              */
onPrepareListView(ListView listView)736             void onPrepareListView(ListView listView);
737         }
738 
AlertParams(Context context)739         public AlertParams(Context context) {
740             mContext = context;
741             mCancelable = true;
742             mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
743         }
744 
apply(AlertController dialog)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 
createListView(final AlertController dialog)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