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