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