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.packageinstaller; 18 19 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 20 21 import android.content.Context; 22 import android.content.DialogInterface; 23 import android.content.res.TypedArray; 24 import android.graphics.drawable.Drawable; 25 import android.os.Handler; 26 import android.os.Message; 27 import android.text.TextUtils; 28 import android.view.KeyEvent; 29 import android.view.LayoutInflater; 30 import android.view.View; 31 import android.view.ViewGroup; 32 import android.view.ViewGroup.LayoutParams; 33 import android.view.ViewParent; 34 import android.view.ViewStub; 35 import android.view.Window; 36 import android.view.WindowManager; 37 import android.widget.Button; 38 import android.widget.FrameLayout; 39 import android.widget.ImageView; 40 import android.widget.ListAdapter; 41 import android.widget.ListView; 42 import android.widget.ScrollView; 43 import android.widget.TextView; 44 45 import androidx.annotation.Nullable; 46 47 import com.android.packageinstaller.R; 48 49 import java.lang.ref.WeakReference; 50 51 public class AlertController { 52 private final Context mContext; 53 private final DialogInterface mDialogInterface; 54 protected final Window mWindow; 55 56 private CharSequence mTitle; 57 protected CharSequence mMessage; 58 protected ListView mListView; 59 private View mView; 60 61 private int mViewLayoutResId; 62 63 private int mViewSpacingLeft; 64 private int mViewSpacingTop; 65 private int mViewSpacingRight; 66 private int mViewSpacingBottom; 67 private boolean mViewSpacingSpecified = false; 68 69 private Button mButtonPositive; 70 private CharSequence mButtonPositiveText; 71 private Message mButtonPositiveMessage; 72 73 private Button mButtonNegative; 74 private CharSequence mButtonNegativeText; 75 private Message mButtonNegativeMessage; 76 77 private Button mButtonNeutral; 78 private CharSequence mButtonNeutralText; 79 private Message mButtonNeutralMessage; 80 81 protected ScrollView mScrollView; 82 83 private int mIconId = 0; 84 private Drawable mIcon; 85 86 private ImageView mIconView; 87 private TextView mTitleView; 88 protected TextView mMessageView; 89 90 private ListAdapter mAdapter; 91 92 private int mAlertDialogLayout; 93 private int mButtonPanelSideLayout; 94 private int mListLayout; 95 private int mMultiChoiceItemLayout; 96 private int mSingleChoiceItemLayout; 97 private int mListItemLayout; 98 99 private boolean mShowTitle; 100 101 private Handler mHandler; 102 103 private final View.OnClickListener mButtonHandler = new View.OnClickListener() { 104 @Override 105 public void onClick(View v) { 106 final Message m; 107 if (v == mButtonPositive && mButtonPositiveMessage != null) { 108 m = Message.obtain(mButtonPositiveMessage); 109 } else if (v == mButtonNegative && mButtonNegativeMessage != null) { 110 m = Message.obtain(mButtonNegativeMessage); 111 } else if (v == mButtonNeutral && mButtonNeutralMessage != null) { 112 m = Message.obtain(mButtonNeutralMessage); 113 } else { 114 m = null; 115 } 116 117 if (m != null) { 118 m.sendToTarget(); 119 } 120 121 // Post a message so we dismiss after the above handlers are executed 122 mHandler.obtainMessage(ButtonHandler.MSG_DISMISS_DIALOG, mDialogInterface) 123 .sendToTarget(); 124 } 125 }; 126 127 private static final class ButtonHandler extends Handler { 128 // Button clicks have Message.what as the BUTTON{1,2,3} constant 129 private static final int MSG_DISMISS_DIALOG = 1; 130 131 private WeakReference<DialogInterface> mDialog; 132 ButtonHandler(DialogInterface dialog)133 public ButtonHandler(DialogInterface dialog) { 134 mDialog = new WeakReference<>(dialog); 135 } 136 137 @Override handleMessage(Message msg)138 public void handleMessage(Message msg) { 139 switch (msg.what) { 140 141 case DialogInterface.BUTTON_POSITIVE: 142 case DialogInterface.BUTTON_NEGATIVE: 143 case DialogInterface.BUTTON_NEUTRAL: 144 ((DialogInterface.OnClickListener) msg.obj).onClick(mDialog.get(), msg.what); 145 break; 146 147 case MSG_DISMISS_DIALOG: 148 ((DialogInterface) msg.obj).dismiss(); 149 } 150 } 151 } 152 AlertController(Context context, DialogInterface di, Window window)153 public AlertController(Context context, DialogInterface di, Window window) { 154 mContext = context; 155 mDialogInterface = di; 156 mWindow = window; 157 mHandler = new ButtonHandler(di); 158 159 final TypedArray a = context.obtainStyledAttributes(null, 160 R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); 161 162 mAlertDialogLayout = a.getResourceId( 163 R.styleable.AlertDialog_layout, R.layout.alert_dialog_material); 164 mButtonPanelSideLayout = a.getResourceId( 165 R.styleable.AlertDialog_buttonPanelSideLayout, 0); 166 mListLayout = a.getResourceId( 167 R.styleable.AlertDialog_listLayout, R.layout.select_dialog_material); 168 169 mMultiChoiceItemLayout = a.getResourceId( 170 R.styleable.AlertDialog_multiChoiceItemLayout, 171 R.layout.select_dialog_multichoice_material); 172 mSingleChoiceItemLayout = a.getResourceId( 173 R.styleable.AlertDialog_singleChoiceItemLayout, 174 R.layout.select_dialog_singlechoice_material); 175 mListItemLayout = a.getResourceId( 176 R.styleable.AlertDialog_listItemLayout, 177 R.layout.select_dialog_item_material); 178 mShowTitle = a.getBoolean(R.styleable.AlertDialog_showTitle, true); 179 180 a.recycle(); 181 182 /* We use a custom title so never request a window title */ 183 window.requestFeature(Window.FEATURE_NO_TITLE); 184 } 185 canTextInput(View v)186 static boolean canTextInput(View v) { 187 if (v.onCheckIsTextEditor()) { 188 return true; 189 } 190 191 if (!(v instanceof ViewGroup)) { 192 return false; 193 } 194 195 ViewGroup vg = (ViewGroup)v; 196 int i = vg.getChildCount(); 197 while (i > 0) { 198 i--; 199 v = vg.getChildAt(i); 200 if (canTextInput(v)) { 201 return true; 202 } 203 } 204 205 return false; 206 } 207 installContent()208 public void installContent() { 209 mWindow.setContentView(mAlertDialogLayout); 210 setupView(); 211 } 212 setTitle(CharSequence title)213 public void setTitle(CharSequence title) { 214 mTitle = title; 215 if (mTitleView != null) { 216 mTitleView.setText(title); 217 } 218 mWindow.setTitle(title); 219 } 220 221 /** 222 * Set the view resource to display in the dialog. 223 */ setView(int layoutResId)224 public void setView(int layoutResId) { 225 mView = null; 226 mViewLayoutResId = layoutResId; 227 mViewSpacingSpecified = false; 228 } 229 230 /** 231 * Sets a click listener or a message to be sent when the button is clicked. 232 * You only need to pass one of {@code listener} or {@code msg}. 233 * 234 * @param whichButton Which button, can be one of 235 * {@link DialogInterface#BUTTON_POSITIVE}, 236 * {@link DialogInterface#BUTTON_NEGATIVE}, or 237 * {@link DialogInterface#BUTTON_NEUTRAL} 238 * @param text The text to display in positive button. 239 * @param listener The {@link DialogInterface.OnClickListener} to use. 240 * @param msg The {@link Message} to be sent when clicked. 241 */ setButton(int whichButton, CharSequence text, DialogInterface.OnClickListener listener, Message msg)242 public void setButton(int whichButton, CharSequence text, 243 DialogInterface.OnClickListener listener, Message msg) { 244 245 if (msg == null && listener != null) { 246 msg = mHandler.obtainMessage(whichButton, listener); 247 } 248 249 switch (whichButton) { 250 251 case DialogInterface.BUTTON_POSITIVE: 252 mButtonPositiveText = text; 253 mButtonPositiveMessage = msg; 254 break; 255 256 case DialogInterface.BUTTON_NEGATIVE: 257 mButtonNegativeText = text; 258 mButtonNegativeMessage = msg; 259 break; 260 261 case DialogInterface.BUTTON_NEUTRAL: 262 mButtonNeutralText = text; 263 mButtonNeutralMessage = msg; 264 break; 265 266 default: 267 throw new IllegalArgumentException("Button does not exist"); 268 } 269 } 270 271 /** 272 * Specifies the icon to display next to the alert title. 273 * 274 * @param resId the resource identifier of the drawable to use as the icon, 275 * or 0 for no icon 276 */ setIcon(int resId)277 public void setIcon(int resId) { 278 mIcon = null; 279 mIconId = resId; 280 281 if (mIconView != null) { 282 if (resId != 0) { 283 mIconView.setVisibility(View.VISIBLE); 284 mIconView.setImageResource(mIconId); 285 } else { 286 mIconView.setVisibility(View.GONE); 287 } 288 } 289 } 290 291 /** 292 * Specifies the icon to display next to the alert title. 293 * 294 * @param icon the drawable to use as the icon or null for no icon 295 */ setIcon(Drawable icon)296 public void setIcon(Drawable icon) { 297 mIcon = icon; 298 mIconId = 0; 299 300 if (mIconView != null) { 301 if (icon != null) { 302 mIconView.setVisibility(View.VISIBLE); 303 mIconView.setImageDrawable(icon); 304 } else { 305 mIconView.setVisibility(View.GONE); 306 } 307 } 308 } 309 getButton(int whichButton)310 public Button getButton(int whichButton) { 311 switch (whichButton) { 312 case DialogInterface.BUTTON_POSITIVE: 313 return mButtonPositive; 314 case DialogInterface.BUTTON_NEGATIVE: 315 return mButtonNegative; 316 case DialogInterface.BUTTON_NEUTRAL: 317 return mButtonNeutral; 318 default: 319 return null; 320 } 321 } 322 323 @SuppressWarnings({"UnusedDeclaration"}) onKeyDown(int keyCode, KeyEvent event)324 public boolean onKeyDown(int keyCode, KeyEvent event) { 325 return mScrollView != null && mScrollView.executeKeyEvent(event); 326 } 327 328 @SuppressWarnings({"UnusedDeclaration"}) onKeyUp(int keyCode, KeyEvent event)329 public boolean onKeyUp(int keyCode, KeyEvent event) { 330 return mScrollView != null && mScrollView.executeKeyEvent(event); 331 } 332 333 /** 334 * Resolves whether a custom or default panel should be used. Removes the 335 * default panel if a custom panel should be used. If the resolved panel is 336 * a view stub, inflates before returning. 337 * 338 * @param customPanel the custom panel 339 * @param defaultPanel the default panel 340 * @return the panel to use 341 */ 342 @Nullable resolvePanel(@ullable View customPanel, @Nullable View defaultPanel)343 private ViewGroup resolvePanel(@Nullable View customPanel, @Nullable View defaultPanel) { 344 if (customPanel == null) { 345 // Inflate the default panel, if needed. 346 if (defaultPanel instanceof ViewStub) { 347 defaultPanel = ((ViewStub) defaultPanel).inflate(); 348 } 349 350 return (ViewGroup) defaultPanel; 351 } 352 353 // Remove the default panel entirely. 354 if (defaultPanel != null) { 355 final ViewParent parent = defaultPanel.getParent(); 356 if (parent instanceof ViewGroup) { 357 ((ViewGroup) parent).removeView(defaultPanel); 358 } 359 } 360 361 // Inflate the custom panel, if needed. 362 if (customPanel instanceof ViewStub) { 363 customPanel = ((ViewStub) customPanel).inflate(); 364 } 365 366 return (ViewGroup) customPanel; 367 } 368 setupView()369 private void setupView() { 370 final View parentPanel = mWindow.findViewById(R.id.parentPanel); 371 final View defaultTopPanel = parentPanel.findViewById(R.id.topPanel); 372 final View defaultContentPanel = parentPanel.findViewById(R.id.contentPanel); 373 final View defaultButtonPanel = parentPanel.findViewById(R.id.buttonPanel); 374 375 // Install custom content before setting up the title or buttons so 376 // that we can handle panel overrides. 377 final ViewGroup customPanel = parentPanel.findViewById(R.id.customPanel); 378 setupCustomContent(customPanel); 379 380 final View customTopPanel = customPanel.findViewById(R.id.topPanel); 381 final View customContentPanel = customPanel.findViewById(R.id.contentPanel); 382 final View customButtonPanel = customPanel.findViewById(R.id.buttonPanel); 383 384 // Resolve the correct panels and remove the defaults, if needed. 385 final ViewGroup topPanel = resolvePanel(customTopPanel, defaultTopPanel); 386 final ViewGroup contentPanel = resolvePanel(customContentPanel, defaultContentPanel); 387 final ViewGroup buttonPanel = resolvePanel(customButtonPanel, defaultButtonPanel); 388 389 setupContent(contentPanel); 390 setupButtons(buttonPanel); 391 setupTitle(topPanel); 392 393 final boolean hasCustomPanel = customPanel != null 394 && customPanel.getVisibility() != View.GONE; 395 final boolean hasTopPanel = topPanel != null 396 && topPanel.getVisibility() != View.GONE; 397 final boolean hasButtonPanel = buttonPanel != null 398 && buttonPanel.getVisibility() != View.GONE; 399 400 if (!parentPanel.isInTouchMode()) { 401 final View content = hasCustomPanel ? customPanel : contentPanel; 402 if (!requestFocusForContent(content)) { 403 requestFocusForDefaultButton(); 404 } 405 } 406 407 if (hasTopPanel) { 408 // Only clip scrolling content to padding if we have a title. 409 if (mScrollView != null) { 410 mScrollView.setClipToPadding(true); 411 } 412 413 // Only show the divider if we have a title. 414 View divider = null; 415 if (mMessage != null || hasCustomPanel) { 416 divider = topPanel.findViewById(R.id.titleDividerNoCustom); 417 } 418 419 if (divider != null) { 420 divider.setVisibility(View.VISIBLE); 421 } 422 } else { 423 if (contentPanel != null) { 424 final View spacer = contentPanel.findViewById(R.id.textSpacerNoTitle); 425 if (spacer != null) { 426 spacer.setVisibility(View.VISIBLE); 427 } 428 } 429 } 430 431 // Update scroll indicators as needed. 432 if (!hasCustomPanel) { 433 final View content = mScrollView; 434 if (content != null) { 435 final int indicators = (hasTopPanel ? View.SCROLL_INDICATOR_TOP : 0) 436 | (hasButtonPanel ? View.SCROLL_INDICATOR_BOTTOM : 0); 437 content.setScrollIndicators(indicators, 438 View.SCROLL_INDICATOR_TOP | View.SCROLL_INDICATOR_BOTTOM); 439 } 440 } 441 442 final TypedArray a = mContext.obtainStyledAttributes( 443 null, R.styleable.AlertDialog, R.attr.alertDialogStyle, 0); 444 setBackground(a, topPanel, contentPanel, customPanel, buttonPanel, 445 hasTopPanel, hasCustomPanel, hasButtonPanel); 446 a.recycle(); 447 } 448 requestFocusForContent(View content)449 private boolean requestFocusForContent(View content) { 450 return content != null && content.requestFocus(); 451 } 452 requestFocusForDefaultButton()453 private void requestFocusForDefaultButton() { 454 if (mButtonPositive.getVisibility() == View.VISIBLE) { 455 mButtonPositive.requestFocus(); 456 } else if (mButtonNegative.getVisibility() == View.VISIBLE) { 457 mButtonNegative.requestFocus(); 458 } else if (mButtonNeutral.getVisibility() == View.VISIBLE) { 459 mButtonNeutral.requestFocus(); 460 } 461 } 462 setupCustomContent(ViewGroup customPanel)463 private void setupCustomContent(ViewGroup customPanel) { 464 final View customView; 465 if (mView != null) { 466 customView = mView; 467 } else if (mViewLayoutResId != 0) { 468 final LayoutInflater inflater = LayoutInflater.from(mContext); 469 customView = inflater.inflate(mViewLayoutResId, customPanel, false); 470 } else { 471 customView = null; 472 } 473 474 final boolean hasCustomView = customView != null; 475 if (!hasCustomView || !canTextInput(customView)) { 476 mWindow.setFlags(WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM, 477 WindowManager.LayoutParams.FLAG_ALT_FOCUSABLE_IM); 478 } 479 480 if (hasCustomView) { 481 final FrameLayout custom = mWindow.findViewById(R.id.custom); 482 custom.addView(customView, new LayoutParams(MATCH_PARENT, MATCH_PARENT)); 483 484 if (mViewSpacingSpecified) { 485 custom.setPadding( 486 mViewSpacingLeft, mViewSpacingTop, mViewSpacingRight, mViewSpacingBottom); 487 } 488 } else { 489 customPanel.setVisibility(View.GONE); 490 } 491 } 492 setupTitle(ViewGroup topPanel)493 private void setupTitle(ViewGroup topPanel) { 494 mIconView = mWindow.findViewById(R.id.icon); 495 496 final boolean hasTextTitle = !TextUtils.isEmpty(mTitle); 497 if (hasTextTitle && mShowTitle) { 498 // Display the title if a title is supplied, else hide it. 499 mTitleView = mWindow.findViewById(R.id.alertTitle); 500 mTitleView.setText(mTitle); 501 502 // Do this last so that if the user has supplied any icons we 503 // use them instead of the default ones. If the user has 504 // specified 0 then make it disappear. 505 if (mIconId != 0) { 506 mIconView.setImageResource(mIconId); 507 } else if (mIcon != null) { 508 mIconView.setImageDrawable(mIcon); 509 } else { 510 // Apply the padding from the icon to ensure the title is 511 // aligned correctly. 512 mTitleView.setPadding(mIconView.getPaddingLeft(), 513 mIconView.getPaddingTop(), 514 mIconView.getPaddingRight(), 515 mIconView.getPaddingBottom()); 516 mIconView.setVisibility(View.GONE); 517 } 518 } else { 519 // Hide the title template 520 final View titleTemplate = mWindow.findViewById(R.id.title_template); 521 titleTemplate.setVisibility(View.GONE); 522 mIconView.setVisibility(View.GONE); 523 topPanel.setVisibility(View.GONE); 524 } 525 } 526 setupContent(ViewGroup contentPanel)527 private void setupContent(ViewGroup contentPanel) { 528 mScrollView = contentPanel.findViewById(R.id.scrollView); 529 mScrollView.setFocusable(false); 530 531 // Special case for users that only want to display a String 532 mMessageView = contentPanel.findViewById(R.id.message); 533 if (mMessageView == null) { 534 return; 535 } 536 537 mMessageView.setVisibility(View.GONE); 538 mScrollView.removeView(mMessageView); 539 540 contentPanel.setVisibility(View.GONE); 541 } 542 setupButtons(ViewGroup buttonPanel)543 private void setupButtons(ViewGroup buttonPanel) { 544 int BIT_BUTTON_POSITIVE = 1; 545 int BIT_BUTTON_NEGATIVE = 2; 546 int BIT_BUTTON_NEUTRAL = 4; 547 int whichButtons = 0; 548 mButtonPositive = buttonPanel.findViewById(R.id.button1); 549 mButtonPositive.setOnClickListener(mButtonHandler); 550 551 if (TextUtils.isEmpty(mButtonPositiveText)) { 552 mButtonPositive.setVisibility(View.GONE); 553 } else { 554 mButtonPositive.setText(mButtonPositiveText); 555 mButtonPositive.setVisibility(View.VISIBLE); 556 whichButtons = whichButtons | BIT_BUTTON_POSITIVE; 557 } 558 559 mButtonNegative = buttonPanel.findViewById(R.id.button2); 560 mButtonNegative.setOnClickListener(mButtonHandler); 561 562 if (TextUtils.isEmpty(mButtonNegativeText)) { 563 mButtonNegative.setVisibility(View.GONE); 564 } else { 565 mButtonNegative.setText(mButtonNegativeText); 566 mButtonNegative.setVisibility(View.VISIBLE); 567 568 whichButtons = whichButtons | BIT_BUTTON_NEGATIVE; 569 } 570 571 mButtonNeutral = buttonPanel.findViewById(R.id.button3); 572 mButtonNeutral.setOnClickListener(mButtonHandler); 573 574 if (TextUtils.isEmpty(mButtonNeutralText)) { 575 mButtonNeutral.setVisibility(View.GONE); 576 } else { 577 mButtonNeutral.setText(mButtonNeutralText); 578 mButtonNeutral.setVisibility(View.VISIBLE); 579 580 whichButtons = whichButtons | BIT_BUTTON_NEUTRAL; 581 } 582 583 final boolean hasButtons = whichButtons != 0; 584 if (!hasButtons) { 585 buttonPanel.setVisibility(View.GONE); 586 } 587 } 588 setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel, View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons)589 private void setBackground(TypedArray a, View topPanel, View contentPanel, View customPanel, 590 View buttonPanel, boolean hasTitle, boolean hasCustomView, boolean hasButtons) { 591 int fullDark = 0; 592 int topDark = 0; 593 int centerDark = 0; 594 int bottomDark = 0; 595 int fullBright = 0; 596 int topBright = 0; 597 int centerBright = 0; 598 int bottomBright = 0; 599 int bottomMedium = 0; 600 601 topBright = a.getResourceId(R.styleable.AlertDialog_topBright, topBright); 602 topDark = a.getResourceId(R.styleable.AlertDialog_topDark, topDark); 603 centerBright = a.getResourceId(R.styleable.AlertDialog_centerBright, centerBright); 604 centerDark = a.getResourceId(R.styleable.AlertDialog_centerDark, centerDark); 605 606 /* We now set the background of all of the sections of the alert. 607 * First collect together each section that is being displayed along 608 * with whether it is on a light or dark background, then run through 609 * them setting their backgrounds. This is complicated because we need 610 * to correctly use the full, top, middle, and bottom graphics depending 611 * on how many views they are and where they appear. 612 */ 613 614 final View[] views = new View[4]; 615 final boolean[] light = new boolean[4]; 616 View lastView = null; 617 boolean lastLight = false; 618 619 int pos = 0; 620 if (hasTitle) { 621 views[pos] = topPanel; 622 light[pos] = false; 623 pos++; 624 } 625 626 /* The contentPanel displays either a custom text message or 627 * a ListView. If it's text we should use the dark background 628 * for ListView we should use the light background. PIA does not use 629 * a list view. Hence, we set it to use dark background. If neither 630 * are there the contentPanel will be hidden so set it as null. 631 */ 632 views[pos] = contentPanel.getVisibility() == View.GONE ? null : contentPanel; 633 light[pos] = false; 634 pos++; 635 636 if (hasCustomView) { 637 views[pos] = customPanel; 638 light[pos] = false; 639 pos++; 640 } 641 642 if (hasButtons) { 643 views[pos] = buttonPanel; 644 light[pos] = true; 645 } 646 647 boolean setView = false; 648 for (pos = 0; pos < views.length; pos++) { 649 final View v = views[pos]; 650 if (v == null) { 651 continue; 652 } 653 654 if (lastView != null) { 655 if (!setView) { 656 lastView.setBackgroundResource(lastLight ? topBright : topDark); 657 } else { 658 lastView.setBackgroundResource(lastLight ? centerBright : centerDark); 659 } 660 setView = true; 661 } 662 663 lastView = v; 664 lastLight = light[pos]; 665 } 666 667 if (lastView != null) { 668 if (setView) { 669 bottomBright = a.getResourceId(R.styleable.AlertDialog_bottomBright, bottomBright); 670 bottomMedium = a.getResourceId(R.styleable.AlertDialog_bottomMedium, bottomMedium); 671 bottomDark = a.getResourceId(R.styleable.AlertDialog_bottomDark, bottomDark); 672 673 // ListViews will use the Bright background, but buttons use the 674 // Medium background. 675 lastView.setBackgroundResource( 676 lastLight ? (hasButtons ? bottomMedium : bottomBright) : bottomDark); 677 } else { 678 fullBright = a.getResourceId(R.styleable.AlertDialog_fullBright, fullBright); 679 fullDark = a.getResourceId(R.styleable.AlertDialog_fullDark, fullDark); 680 681 lastView.setBackgroundResource(lastLight ? fullBright : fullDark); 682 } 683 } 684 } 685 } 686