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