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