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 ButtonHandler(DialogInterface dialog)148 public ButtonHandler(DialogInterface dialog) { 149 mDialog = new WeakReference<DialogInterface>(dialog); 150 } 151 152 @Override handleMessage(Message msg)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 AlertController(Context context, DialogInterface di, Window window)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 canTextInput(View v)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 installContent()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 setTitle(CharSequence title)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 */ setCustomTitle(View customTitleView)219 public void setCustomTitle(View customTitleView) { 220 mCustomTitleView = customTitleView; 221 } 222 setMessage(CharSequence message)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 */ setView(View view)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 */ setView(View view, int viewSpacingLeft, int viewSpacingTop, int viewSpacingRight, int viewSpacingBottom)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 */ setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener, Message msg)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 */ setIcon(int resId)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 setIcon(Drawable icon)308 public void setIcon(Drawable icon) { 309 mIcon = icon; 310 if ((mIconView != null) && (mIcon != null)) { 311 mIconView.setImageDrawable(icon); 312 } 313 } 314 setInverseBackgroundForced(boolean forceInverseBackground)315 public void setInverseBackgroundForced(boolean forceInverseBackground) { 316 mForceInverseBackground = forceInverseBackground; 317 } 318 getListView()319 public ListView getListView() { 320 return mListView; 321 } 322 getButton(int whichButton)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"}) onKeyDown(int keyCode, KeyEvent event)337 public boolean onKeyDown(int keyCode, KeyEvent event) { 338 return mScrollView != null && mScrollView.executeKeyEvent(event); 339 } 340 341 @SuppressWarnings({"UnusedDeclaration"}) onKeyUp(int keyCode, KeyEvent event)342 public boolean onKeyUp(int keyCode, KeyEvent event) { 343 return mScrollView != null && mScrollView.executeKeyEvent(event); 344 } 345 setupView()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 setupTitle(LinearLayout topPanel)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 setupContent(LinearLayout contentPanel)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 setupButtons()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 centerButton(Button button)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 setBackground(LinearLayout topPanel, LinearLayout contentPanel, View customPanel, boolean hasButtons, TypedArray a, boolean hasTitle, View buttonPanel)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 RecycleListView(Context context)668 public RecycleListView(Context context) { 669 super(context); 670 } 671 RecycleListView(Context context, AttributeSet attrs)672 public RecycleListView(Context context, AttributeSet attrs) { 673 super(context, attrs); 674 } 675 RecycleListView(Context context, AttributeSet attrs, int defStyle)676 public RecycleListView(Context context, AttributeSet attrs, int defStyle) { 677 super(context, attrs, defStyle); 678 } 679 680 @Override recycleOnMeasure()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 */ onPrepareListView(ListView listView)736 void onPrepareListView(ListView listView); 737 } 738 AlertParams(Context context)739 public AlertParams(Context context) { 740 mContext = context; 741 mCancelable = true; 742 mInflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE); 743 } 744 apply(AlertController dialog)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 createListView(final AlertController dialog)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