1 /* 2 * Copyright (C) 2008 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package com.android.internal.app; 18 19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 21 import com.android.internal.R; 22 23 import android.app.AlertDialog; 24 import android.content.Context; 25 import android.content.DialogInterface; 26 import android.content.res.TypedArray; 27 import android.database.Cursor; 28 import android.graphics.drawable.Drawable; 29 import android.os.Handler; 30 import android.os.Message; 31 import android.text.TextUtils; 32 import android.util.AttributeSet; 33 import android.view.Gravity; 34 import android.view.KeyEvent; 35 import android.view.LayoutInflater; 36 import android.view.View; 37 import android.view.ViewGroup; 38 import android.view.ViewGroup.LayoutParams; 39 import android.view.Window; 40 import android.view.WindowManager; 41 import android.widget.AdapterView; 42 import android.widget.AdapterView.OnItemClickListener; 43 import android.widget.ArrayAdapter; 44 import android.widget.Button; 45 import android.widget.CheckedTextView; 46 import android.widget.CursorAdapter; 47 import android.widget.FrameLayout; 48 import android.widget.ImageView; 49 import android.widget.LinearLayout; 50 import android.widget.ListAdapter; 51 import android.widget.ListView; 52 import android.widget.ScrollView; 53 import android.widget.SimpleCursorAdapter; 54 import android.widget.TextView; 55 56 import java.lang.ref.WeakReference; 57 58 public 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 hasTitle = false; 438 } 439 } 440 return hasTitle; 441 } 442 443 private void setupContent(LinearLayout contentPanel) { 444 mScrollView = (ScrollView) mWindow.findViewById(R.id.scrollView); 445 mScrollView.setFocusable(false); 446 447 // Special case for users that only want to display a String 448 mMessageView = (TextView) mWindow.findViewById(R.id.message); 449 if (mMessageView == null) { 450 return; 451 } 452 453 if (mMessage != null) { 454 mMessageView.setText(mMessage); 455 } else { 456 mMessageView.setVisibility(View.GONE); 457 mScrollView.removeView(mMessageView); 458 459 if (mListView != null) { 460 contentPanel.removeView(mWindow.findViewById(R.id.scrollView)); 461 contentPanel.addView(mListView, 462 new LinearLayout.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 463 contentPanel.setLayoutParams(new LinearLayout.LayoutParams(MATCH_PARENT, 0, 1.0f)); 464 } else { 465 contentPanel.setVisibility(View.GONE); 466 } 467 } 468 } 469 470 private boolean setupButtons() { 471 int BIT_BUTTON_POSITIVE = 1; 472 int BIT_BUTTON_NEGATIVE = 2; 473 int BIT_BUTTON_NEUTRAL = 4; 474 int whichButtons = 0; 475 mButtonPositive = (Button) mWindow.findViewById(R.id.button1); 476 mButtonPositive.setOnClickListener(mButtonHandler); 477 478 if (TextUtils.isEmpty(mButtonPositiveText)) { 479 mButtonPositive.setVisibility(View.GONE); 480 } else { 481 mButtonPositive.setText(mButtonPositiveText); 482 mButtonPositive.setVisibility(View.VISIBLE); 483 whichButtons = whichButtons | BIT_BUTTON_POSITIVE; 484 } 485 486 mButtonNegative = (Button) mWindow.findViewById(R.id.button2); 487 mButtonNegative.setOnClickListener(mButtonHandler); 488 489 if (TextUtils.isEmpty(mButtonNegativeText)) { 490 mButtonNegative.setVisibility(View.GONE); 491 } else { 492 mButtonNegative.setText(mButtonNegativeText); 493 mButtonNegative.setVisibility(View.VISIBLE); 494 495 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE; 496 } 497 498 mButtonNeutral = (Button) mWindow.findViewById(R.id.button3); 499 mButtonNeutral.setOnClickListener(mButtonHandler); 500 501 if (TextUtils.isEmpty(mButtonNeutralText)) { 502 mButtonNeutral.setVisibility(View.GONE); 503 } else { 504 mButtonNeutral.setText(mButtonNeutralText); 505 mButtonNeutral.setVisibility(View.VISIBLE); 506 507 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL; 508 } 509 510 /* 511 * If we only have 1 button it should be centered on the layout and 512 * expand to fill 50% of the available space. 513 */ 514 if (whichButtons == BIT_BUTTON_POSITIVE) { 515 centerButton(mButtonPositive); 516 } else if (whichButtons == BIT_BUTTON_NEGATIVE) { 517 centerButton(mButtonNeutral); 518 } else if (whichButtons == BIT_BUTTON_NEUTRAL) { 519 centerButton(mButtonNeutral); 520 } 521 522 return whichButtons != 0; 523 } 524 525 private void centerButton(Button button) { 526 LinearLayout.LayoutParams params = (LinearLayout.LayoutParams) button.getLayoutParams(); 527 params.gravity = Gravity.CENTER_HORIZONTAL; 528 params.weight = 0.5f; 529 button.setLayoutParams(params); 530 View leftSpacer = mWindow.findViewById(R.id.leftSpacer); 531 leftSpacer.setVisibility(View.VISIBLE); 532 View rightSpacer = mWindow.findViewById(R.id.rightSpacer); 533 rightSpacer.setVisibility(View.VISIBLE); 534 } 535 536 private void setBackground(LinearLayout topPanel, LinearLayout contentPanel, 537 View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle, 538 View buttonPanel) { 539 540 /* Get all the different background required */ 541 int fullDark = a.getResourceId( 542 R.styleable.AlertDialog_fullDark, R.drawable.popup_full_dark); 543 int topDark = a.getResourceId( 544 R.styleable.AlertDialog_topDark, R.drawable.popup_top_dark); 545 int centerDark = a.getResourceId( 546 R.styleable.AlertDialog_centerDark, R.drawable.popup_center_dark); 547 int bottomDark = a.getResourceId( 548 R.styleable.AlertDialog_bottomDark, R.drawable.popup_bottom_dark); 549 int fullBright = a.getResourceId( 550 R.styleable.AlertDialog_fullBright, R.drawable.popup_full_bright); 551 int topBright = a.getResourceId( 552 R.styleable.AlertDialog_topBright, R.drawable.popup_top_bright); 553 int centerBright = a.getResourceId( 554 R.styleable.AlertDialog_centerBright, R.drawable.popup_center_bright); 555 int bottomBright = a.getResourceId( 556 R.styleable.AlertDialog_bottomBright, R.drawable.popup_bottom_bright); 557 int bottomMedium = a.getResourceId( 558 R.styleable.AlertDialog_bottomMedium, R.drawable.popup_bottom_medium); 559 560 /* 561 * We now set the background of all of the sections of the alert. 562 * First collect together each section that is being displayed along 563 * with whether it is on a light or dark background, then run through 564 * them setting their backgrounds. This is complicated because we need 565 * to correctly use the full, top, middle, and bottom graphics depending 566 * on how many views they are and where they appear. 567 */ 568 569 View[] views = new View[4]; 570 boolean[] light = new boolean[4]; 571 View lastView = null; 572 boolean lastLight = false; 573 574 int pos = 0; 575 if (hasTitle) { 576 views[pos] = topPanel; 577 light[pos] = false; 578 pos++; 579 } 580 581 /* The contentPanel displays either a custom text message or 582 * a ListView. If it's text we should use the dark background 583 * for ListView we should use the light background. If neither 584 * are there the contentPanel will be hidden so set it as null. 585 */ 586 views[pos] = (contentPanel.getVisibility() == View.GONE) 587 ? null : contentPanel; 588 light[pos] = mListView != null; 589 pos++; 590 if (customPanel != null) { 591 views[pos] = customPanel; 592 light[pos] = mForceInverseBackground; 593 pos++; 594 } 595 if (hasButtons) { 596 views[pos] = buttonPanel; 597 light[pos] = true; 598 } 599 600 boolean setView = false; 601 for (pos=0; pos<views.length; pos++) { 602 View v = views[pos]; 603 if (v == null) { 604 continue; 605 } 606 if (lastView != null) { 607 if (!setView) { 608 lastView.setBackgroundResource(lastLight ? topBright : topDark); 609 } else { 610 lastView.setBackgroundResource(lastLight ? centerBright : centerDark); 611 } 612 setView = true; 613 } 614 lastView = v; 615 lastLight = light[pos]; 616 } 617 618 if (lastView != null) { 619 if (setView) { 620 621 /* ListViews will use the Bright background but buttons use 622 * the Medium background. 623 */ 624 lastView.setBackgroundResource( 625 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark); 626 } else { 627 lastView.setBackgroundResource(lastLight ? fullBright : fullDark); 628 } 629 } 630 631 /* TODO: uncomment section below. The logic for this should be if 632 * it's a Contextual menu being displayed AND only a Cancel button 633 * is shown then do this. 634 */ 635 // if (hasButtons && (mListView != null)) { 636 637 /* Yet another *special* case. If there is a ListView with buttons 638 * don't put the buttons on the bottom but instead put them in the 639 * footer of the ListView this will allow more items to be 640 * displayed. 641 */ 642 643 /* 644 contentPanel.setBackgroundResource(bottomBright); 645 buttonPanel.setBackgroundResource(centerMedium); 646 ViewGroup parent = (ViewGroup) mWindow.findViewById(R.id.parentPanel); 647 parent.removeView(buttonPanel); 648 AbsListView.LayoutParams params = new AbsListView.LayoutParams( 649 AbsListView.LayoutParams.MATCH_PARENT, 650 AbsListView.LayoutParams.MATCH_PARENT); 651 buttonPanel.setLayoutParams(params); 652 mListView.addFooterView(buttonPanel); 653 */ 654 // } 655 656 if ((mListView != null) && (mAdapter != null)) { 657 mListView.setAdapter(mAdapter); 658 if (mCheckedItem > -1) { 659 mListView.setItemChecked(mCheckedItem, true); 660 mListView.setSelection(mCheckedItem); 661 } 662 } 663 } 664 665 public static class RecycleListView extends ListView { 666 boolean mRecycleOnMeasure = true; 667 668 public RecycleListView(Context context) { 669 super(context); 670 } 671 672 public RecycleListView(Context context, AttributeSet attrs) { 673 super(context, attrs); 674 } 675 676 public RecycleListView(Context context, AttributeSet attrs, int defStyle) { 677 super(context, attrs, defStyle); 678 } 679 680 @Override 681 protected boolean recycleOnMeasure() { 682 return mRecycleOnMeasure; 683 } 684 } 685 686 public static class AlertParams { 687 public final Context mContext; 688 public final LayoutInflater mInflater; 689 690 public int mIconId = 0; 691 public Drawable mIcon; 692 public CharSequence mTitle; 693 public View mCustomTitleView; 694 public CharSequence mMessage; 695 public CharSequence mPositiveButtonText; 696 public DialogInterface.OnClickListener mPositiveButtonListener; 697 public CharSequence mNegativeButtonText; 698 public DialogInterface.OnClickListener mNegativeButtonListener; 699 public CharSequence mNeutralButtonText; 700 public DialogInterface.OnClickListener mNeutralButtonListener; 701 public boolean mCancelable; 702 public DialogInterface.OnCancelListener mOnCancelListener; 703 public DialogInterface.OnKeyListener mOnKeyListener; 704 public CharSequence[] mItems; 705 public ListAdapter mAdapter; 706 public DialogInterface.OnClickListener mOnClickListener; 707 public View mView; 708 public int mViewSpacingLeft; 709 public int mViewSpacingTop; 710 public int mViewSpacingRight; 711 public int mViewSpacingBottom; 712 public boolean mViewSpacingSpecified = false; 713 public boolean[] mCheckedItems; 714 public boolean mIsMultiChoice; 715 public boolean mIsSingleChoice; 716 public int mCheckedItem = -1; 717 public DialogInterface.OnMultiChoiceClickListener mOnCheckboxClickListener; 718 public Cursor mCursor; 719 public String mLabelColumn; 720 public String mIsCheckedColumn; 721 public boolean mForceInverseBackground; 722 public AdapterView.OnItemSelectedListener mOnItemSelectedListener; 723 public OnPrepareListViewListener mOnPrepareListViewListener; 724 public boolean mRecycleOnMeasure = true; 725 726 /** 727 * Interface definition for a callback to be invoked before the ListView 728 * will be bound to an adapter. 729 */ 730 public interface OnPrepareListViewListener { 731 732 /** 733 * Called before the ListView is bound to an adapter. 734 * @param listView The ListView that will be shown in the dialog. 735 */ 736 void onPrepareListView(ListView listView); 737 } 738 739 public AlertParams(Context context) { 740 mContext = context; 741 mCancelable = true; 742 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 743 } 744 745 public void apply(AlertController dialog) { 746 if (mCustomTitleView != null) { 747 dialog.setCustomTitle(mCustomTitleView); 748 } else { 749 if (mTitle != null) { 750 dialog.setTitle(mTitle); 751 } 752 if (mIcon != null) { 753 dialog.setIcon(mIcon); 754 } 755 if (mIconId >= 0) { 756 dialog.setIcon(mIconId); 757 } 758 } 759 if (mMessage != null) { 760 dialog.setMessage(mMessage); 761 } 762 if (mPositiveButtonText != null) { 763 dialog.setButton(DialogInterface.BUTTON_POSITIVE, mPositiveButtonText, 764 mPositiveButtonListener, null); 765 } 766 if (mNegativeButtonText != null) { 767 dialog.setButton(DialogInterface.BUTTON_NEGATIVE, mNegativeButtonText, 768 mNegativeButtonListener, null); 769 } 770 if (mNeutralButtonText != null) { 771 dialog.setButton(DialogInterface.BUTTON_NEUTRAL, mNeutralButtonText, 772 mNeutralButtonListener, null); 773 } 774 if (mForceInverseBackground) { 775 dialog.setInverseBackgroundForced(true); 776 } 777 // For a list, the client can either supply an array of items or an 778 // adapter or a cursor 779 if ((mItems != null) || (mCursor != null) || (mAdapter != null)) { 780 createListView(dialog); 781 } 782 if (mView != null) { 783 if (mViewSpacingSpecified) { 784 dialog.setView(mView, mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, 785 mViewSpacingBottom); 786 } else { 787 dialog.setView(mView); 788 } 789 } 790 791 /* 792 dialog.setCancelable(mCancelable); 793 dialog.setOnCancelListener(mOnCancelListener); 794 if (mOnKeyListener != null) { 795 dialog.setOnKeyListener(mOnKeyListener); 796 } 797 */ 798 } 799 800 private void createListView(final AlertController dialog) { 801 final RecycleListView listView = (RecycleListView) 802 mInflater.inflate(R.layout.select_dialog, null); 803 ListAdapter adapter; 804 805 if (mIsMultiChoice) { 806 if (mCursor == null) { 807 adapter = new ArrayAdapter<CharSequence>( 808 mContext, R.layout.select_dialog_multichoice, R.id.text1, mItems) { 809 @Override 810 public View getView(int position, View convertView, ViewGroup parent) { 811 View view = super.getView(position, convertView, parent); 812 if (mCheckedItems != null) { 813 boolean isItemChecked = mCheckedItems[position]; 814 if (isItemChecked) { 815 listView.setItemChecked(position, true); 816 } 817 } 818 return view; 819 } 820 }; 821 } else { 822 adapter = new CursorAdapter(mContext, mCursor, false) { 823 private final int mLabelIndex; 824 private final int mIsCheckedIndex; 825 826 { 827 final Cursor cursor = getCursor(); 828 mLabelIndex = cursor.getColumnIndexOrThrow(mLabelColumn); 829 mIsCheckedIndex = cursor.getColumnIndexOrThrow(mIsCheckedColumn); 830 } 831 832 @Override 833 public void bindView(View view, Context context, Cursor cursor) { 834 CheckedTextView text = (CheckedTextView) view.findViewById(R.id.text1); 835 text.setText(cursor.getString(mLabelIndex)); 836 listView.setItemChecked(cursor.getPosition(), 837 cursor.getInt(mIsCheckedIndex) == 1); 838 } 839 840 @Override 841 public View newView(Context context, Cursor cursor, ViewGroup parent) { 842 return mInflater.inflate(R.layout.select_dialog_multichoice, 843 parent, false); 844 } 845 846 }; 847 } 848 } else { 849 int layout = mIsSingleChoice 850 ? R.layout.select_dialog_singlechoice : R.layout.select_dialog_item; 851 if (mCursor == null) { 852 adapter = (mAdapter != null) ? mAdapter 853 : new ArrayAdapter<CharSequence>(mContext, layout, R.id.text1, mItems); 854 } else { 855 adapter = new SimpleCursorAdapter(mContext, layout, 856 mCursor, new String[]{mLabelColumn}, new int[]{R.id.text1}); 857 } 858 } 859 860 if (mOnPrepareListViewListener != null) { 861 mOnPrepareListViewListener.onPrepareListView(listView); 862 } 863 864 /* Don't directly set the adapter on the ListView as we might 865 * want to add a footer to the ListView later. 866 */ 867 dialog.mAdapter = adapter; 868 dialog.mCheckedItem = mCheckedItem; 869 870 if (mOnClickListener != null) { 871 listView.setOnItemClickListener(new OnItemClickListener() { 872 public void onItemClick(AdapterView parent, View v, int position, long id) { 873 mOnClickListener.onClick(dialog.mDialogInterface, position); 874 if (!mIsSingleChoice) { 875 dialog.mDialogInterface.dismiss(); 876 } 877 } 878 }); 879 } else if (mOnCheckboxClickListener != null) { 880 listView.setOnItemClickListener(new OnItemClickListener() { 881 public void onItemClick(AdapterView parent, View v, int position, long id) { 882 if (mCheckedItems != null) { 883 mCheckedItems[position] = listView.isItemChecked(position); 884 } 885 mOnCheckboxClickListener.onClick( 886 dialog.mDialogInterface, position, listView.isItemChecked(position)); 887 } 888 }); 889 } 890 891 // Attach a given OnItemSelectedListener to the ListView 892 if (mOnItemSelectedListener != null) { 893 listView.setOnItemSelectedListener(mOnItemSelectedListener); 894 } 895 896 if (mIsSingleChoice) { 897 listView.setChoiceMode(ListView.CHOICE_MODE_SINGLE); 898 } else if (mIsMultiChoice) { 899 listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE); 900 } 901 listView.mRecycleOnMeasure = mRecycleOnMeasure; 902 dialog.mListView = listView; 903 } 904 } 905 906 } 907