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