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