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