1 /* 2 * Copyright (C) 2015 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.policy; 18 19 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP; 20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM; 21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED; 22 import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 23 import static android.os.Build.VERSION_CODES.M; 24 import static android.os.Build.VERSION_CODES.N; 25 import static android.view.View.MeasureSpec.AT_MOST; 26 import static android.view.View.MeasureSpec.EXACTLY; 27 import static android.view.View.MeasureSpec.getMode; 28 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 29 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 30 import static android.view.Window.DECOR_CAPTION_SHADE_DARK; 31 import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT; 32 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 33 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 34 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 35 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 36 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 37 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 38 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 39 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 40 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; 41 42 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; 43 44 import android.animation.Animator; 45 import android.animation.AnimatorListenerAdapter; 46 import android.animation.ObjectAnimator; 47 import android.annotation.Nullable; 48 import android.annotation.TestApi; 49 import android.app.WindowConfiguration; 50 import android.compat.annotation.UnsupportedAppUsage; 51 import android.content.Context; 52 import android.content.res.Configuration; 53 import android.content.res.Resources; 54 import android.graphics.Canvas; 55 import android.graphics.Color; 56 import android.graphics.Insets; 57 import android.graphics.LinearGradient; 58 import android.graphics.Outline; 59 import android.graphics.Paint; 60 import android.graphics.PixelFormat; 61 import android.graphics.RecordingCanvas; 62 import android.graphics.Rect; 63 import android.graphics.Region; 64 import android.graphics.Shader; 65 import android.graphics.drawable.ColorDrawable; 66 import android.graphics.drawable.Drawable; 67 import android.graphics.drawable.InsetDrawable; 68 import android.graphics.drawable.LayerDrawable; 69 import android.util.DisplayMetrics; 70 import android.util.Log; 71 import android.util.Pair; 72 import android.util.TypedValue; 73 import android.view.ActionMode; 74 import android.view.ContextThemeWrapper; 75 import android.view.Gravity; 76 import android.view.InputQueue; 77 import android.view.KeyEvent; 78 import android.view.KeyboardShortcutGroup; 79 import android.view.LayoutInflater; 80 import android.view.Menu; 81 import android.view.MenuItem; 82 import android.view.MotionEvent; 83 import android.view.ThreadedRenderer; 84 import android.view.View; 85 import android.view.ViewGroup; 86 import android.view.ViewOutlineProvider; 87 import android.view.ViewRootImpl; 88 import android.view.ViewStub; 89 import android.view.ViewTreeObserver; 90 import android.view.Window; 91 import android.view.WindowCallbacks; 92 import android.view.WindowInsets; 93 import android.view.WindowManager; 94 import android.view.accessibility.AccessibilityEvent; 95 import android.view.accessibility.AccessibilityManager; 96 import android.view.accessibility.AccessibilityNodeInfo; 97 import android.view.animation.AnimationUtils; 98 import android.view.animation.Interpolator; 99 import android.widget.FrameLayout; 100 import android.widget.PopupWindow; 101 102 import com.android.internal.R; 103 import com.android.internal.policy.PhoneWindow.PanelFeatureState; 104 import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback; 105 import com.android.internal.view.FloatingActionMode; 106 import com.android.internal.view.RootViewSurfaceTaker; 107 import com.android.internal.view.StandaloneActionMode; 108 import com.android.internal.view.menu.ContextMenuBuilder; 109 import com.android.internal.view.menu.MenuHelper; 110 import com.android.internal.widget.ActionBarContextView; 111 import com.android.internal.widget.BackgroundFallback; 112 import com.android.internal.widget.DecorCaptionView; 113 import com.android.internal.widget.FloatingToolbar; 114 115 import java.util.List; 116 117 /** @hide */ 118 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks { 119 private static final String TAG = "DecorView"; 120 121 private static final boolean DEBUG_MEASURE = false; 122 123 private static final boolean SWEEP_OPEN_MENU = false; 124 125 // The height of a window which has focus in DIP. 126 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20; 127 // The height of a window which has not in DIP. 128 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5; 129 130 private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white 131 132 public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES = 133 new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS, 134 Gravity.TOP, Gravity.LEFT, Gravity.RIGHT, 135 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME, 136 com.android.internal.R.id.statusBarBackground, 137 FLAG_FULLSCREEN); 138 139 public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES = 140 new ColorViewAttributes( 141 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION, 142 Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT, 143 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME, 144 com.android.internal.R.id.navigationBarBackground, 145 0 /* hideWindowFlag */); 146 147 // This is used to workaround an issue where the PiP shadow can be transparent if the window 148 // background is transparent 149 private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() { 150 @Override 151 public void getOutline(View view, Outline outline) { 152 outline.setRect(0, 0, view.getWidth(), view.getHeight()); 153 outline.setAlpha(1f); 154 } 155 }; 156 157 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer 158 // size calculation takes the shadow size into account. We set the elevation currently 159 // to max until the first layout command has been executed. 160 private boolean mAllowUpdateElevation = false; 161 162 private boolean mElevationAdjustedForStack = false; 163 164 // Keeps track of the picture-in-picture mode for the view shadow 165 private boolean mIsInPictureInPictureMode; 166 167 // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER 168 private ViewOutlineProvider mLastOutlineProvider; 169 170 int mDefaultOpacity = PixelFormat.OPAQUE; 171 172 /** The feature ID of the panel, or -1 if this is the application's DecorView */ 173 private final int mFeatureId; 174 175 private final Rect mDrawingBounds = new Rect(); 176 177 private final Rect mBackgroundPadding = new Rect(); 178 179 private final Rect mFramePadding = new Rect(); 180 181 private final Rect mFrameOffsets = new Rect(); 182 183 private boolean mHasCaption = false; 184 185 private boolean mChanging; 186 187 private Drawable mMenuBackground; 188 private boolean mWatchingForMenu; 189 private int mDownY; 190 191 ActionMode mPrimaryActionMode; 192 private ActionMode mFloatingActionMode; 193 private ActionBarContextView mPrimaryActionModeView; 194 private PopupWindow mPrimaryActionModePopup; 195 private Runnable mShowPrimaryActionModePopup; 196 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener; 197 private View mFloatingActionModeOriginatingView; 198 private FloatingToolbar mFloatingToolbar; 199 private ObjectAnimator mFadeAnim; 200 201 // View added at runtime to draw under the status bar area 202 private View mStatusGuard; 203 204 private final ColorViewState mStatusColorViewState = 205 new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES); 206 private final ColorViewState mNavigationColorViewState = 207 new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES); 208 209 private final Interpolator mShowInterpolator; 210 private final Interpolator mHideInterpolator; 211 private final int mBarEnterExitDuration; 212 final boolean mForceWindowDrawsBarBackgrounds; 213 private final int mSemiTransparentBarColor; 214 215 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback(); 216 217 private int mLastTopInset = 0; 218 @UnsupportedAppUsage 219 private int mLastBottomInset = 0; 220 @UnsupportedAppUsage 221 private int mLastRightInset = 0; 222 @UnsupportedAppUsage 223 private int mLastLeftInset = 0; 224 private boolean mLastHasTopStableInset = false; 225 private boolean mLastHasBottomStableInset = false; 226 private boolean mLastHasRightStableInset = false; 227 private boolean mLastHasLeftStableInset = false; 228 private int mLastWindowFlags = 0; 229 private boolean mLastShouldAlwaysConsumeSystemBars = false; 230 231 private int mRootScrollY = 0; 232 233 @UnsupportedAppUsage 234 private PhoneWindow mWindow; 235 236 ViewGroup mContentRoot; 237 238 private Rect mTempRect; 239 private Rect mOutsets = new Rect(); 240 241 // This is the caption view for the window, containing the caption and window control 242 // buttons. The visibility of this decor depends on the workspace and the window type. 243 // If the window type does not require such a view, this member might be null. 244 private DecorCaptionView mDecorCaptionView; 245 246 private boolean mWindowResizeCallbacksAdded = false; 247 private Drawable.Callback mLastBackgroundDrawableCb = null; 248 private BackdropFrameRenderer mBackdropFrameRenderer = null; 249 private Drawable mOriginalBackgroundDrawable; 250 private Drawable mLastOriginalBackgroundDrawable; 251 private Drawable mResizingBackgroundDrawable; 252 private Drawable mCaptionBackgroundDrawable; 253 private Drawable mUserCaptionBackgroundDrawable; 254 255 private float mAvailableWidth; 256 257 String mLogTag = TAG; 258 private final Rect mFloatingInsets = new Rect(); 259 private boolean mApplyFloatingVerticalInsets = false; 260 private boolean mApplyFloatingHorizontalInsets = false; 261 262 private int mResizeMode = RESIZE_MODE_INVALID; 263 private final int mResizeShadowSize; 264 private final Paint mVerticalResizeShadowPaint = new Paint(); 265 private final Paint mHorizontalResizeShadowPaint = new Paint(); 266 private final Paint mLegacyNavigationBarBackgroundPaint = new Paint(); 267 private Insets mBackgroundInsets = Insets.NONE; 268 private Insets mLastBackgroundInsets = Insets.NONE; 269 private boolean mDrawLegacyNavigationBarBackground; 270 DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params)271 DecorView(Context context, int featureId, PhoneWindow window, 272 WindowManager.LayoutParams params) { 273 super(context); 274 mFeatureId = featureId; 275 276 mShowInterpolator = AnimationUtils.loadInterpolator(context, 277 android.R.interpolator.linear_out_slow_in); 278 mHideInterpolator = AnimationUtils.loadInterpolator(context, 279 android.R.interpolator.fast_out_linear_in); 280 281 mBarEnterExitDuration = context.getResources().getInteger( 282 R.integer.dock_enter_exit_duration); 283 mForceWindowDrawsBarBackgrounds = context.getResources().getBoolean( 284 R.bool.config_forceWindowDrawsStatusBarBackground) 285 && context.getApplicationInfo().targetSdkVersion >= N; 286 mSemiTransparentBarColor = context.getResources().getColor( 287 R.color.system_bar_background_semi_transparent, null /* theme */); 288 289 updateAvailableWidth(); 290 291 setWindow(window); 292 293 updateLogTag(params); 294 295 mResizeShadowSize = context.getResources().getDimensionPixelSize( 296 R.dimen.resize_shadow_size); 297 initResizingPaints(); 298 299 mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK); 300 } 301 setBackgroundFallback(@ullable Drawable fallbackDrawable)302 void setBackgroundFallback(@Nullable Drawable fallbackDrawable) { 303 mBackgroundFallback.setDrawable(fallbackDrawable); 304 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback()); 305 } 306 307 @TestApi getBackgroundFallback()308 public @Nullable Drawable getBackgroundFallback() { 309 return mBackgroundFallback.getDrawable(); 310 } 311 312 @Override gatherTransparentRegion(Region region)313 public boolean gatherTransparentRegion(Region region) { 314 boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region); 315 boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region); 316 boolean decorOpaque = super.gatherTransparentRegion(region); 317 318 // combine bools after computation, so each method above always executes 319 return statusOpaque || navOpaque || decorOpaque; 320 } 321 gatherTransparentRegion(ColorViewState colorViewState, Region region)322 boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) { 323 if (colorViewState.view != null && colorViewState.visible && isResizing()) { 324 // If a visible ColorViewState is in a resizing host DecorView, forcibly register its 325 // opaque area, since it's drawn by a different root RenderNode. It would otherwise be 326 // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE. 327 return colorViewState.view.gatherTransparentRegion(region); 328 } 329 return false; // no opaque area added 330 } 331 332 @Override onDraw(Canvas c)333 public void onDraw(Canvas c) { 334 super.onDraw(c); 335 336 mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent, 337 mStatusColorViewState.view, mNavigationColorViewState.view); 338 } 339 340 @Override dispatchKeyEvent(KeyEvent event)341 public boolean dispatchKeyEvent(KeyEvent event) { 342 final int keyCode = event.getKeyCode(); 343 final int action = event.getAction(); 344 final boolean isDown = action == KeyEvent.ACTION_DOWN; 345 346 if (isDown && (event.getRepeatCount() == 0)) { 347 // First handle chording of panel key: if a panel key is held 348 // but not released, try to execute a shortcut in it. 349 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) { 350 boolean handled = dispatchKeyShortcutEvent(event); 351 if (handled) { 352 return true; 353 } 354 } 355 356 // If a panel is open, perform a shortcut on it without the 357 // chorded panel key 358 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) { 359 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) { 360 return true; 361 } 362 } 363 } 364 365 if (!mWindow.isDestroyed()) { 366 final Window.Callback cb = mWindow.getCallback(); 367 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event) 368 : super.dispatchKeyEvent(event); 369 if (handled) { 370 return true; 371 } 372 } 373 374 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event) 375 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event); 376 } 377 378 @Override 379 public boolean dispatchKeyShortcutEvent(KeyEvent ev) { 380 // If the panel is already prepared, then perform the shortcut using it. 381 boolean handled; 382 if (mWindow.mPreparedPanel != null) { 383 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev, 384 Menu.FLAG_PERFORM_NO_CLOSE); 385 if (handled) { 386 if (mWindow.mPreparedPanel != null) { 387 mWindow.mPreparedPanel.isHandled = true; 388 } 389 return true; 390 } 391 } 392 393 // Shortcut not handled by the panel. Dispatch to the view hierarchy. 394 final Window.Callback cb = mWindow.getCallback(); 395 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0 396 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev); 397 if (handled) { 398 return true; 399 } 400 401 // If the panel is not prepared, then we may be trying to handle a shortcut key 402 // combination such as Control+C. Temporarily prepare the panel then mark it 403 // unprepared again when finished to ensure that the panel will again be prepared 404 // the next time it is shown for real. 405 PhoneWindow.PanelFeatureState st = 406 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false); 407 if (st != null && mWindow.mPreparedPanel == null) { 408 mWindow.preparePanel(st, ev); 409 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev, 410 Menu.FLAG_PERFORM_NO_CLOSE); 411 st.isPrepared = false; 412 if (handled) { 413 return true; 414 } 415 } 416 return false; 417 } 418 419 @Override 420 public boolean dispatchTouchEvent(MotionEvent ev) { 421 final Window.Callback cb = mWindow.getCallback(); 422 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 423 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev); 424 } 425 426 @Override 427 public boolean dispatchTrackballEvent(MotionEvent ev) { 428 final Window.Callback cb = mWindow.getCallback(); 429 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 430 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev); 431 } 432 433 @Override 434 public boolean dispatchGenericMotionEvent(MotionEvent ev) { 435 final Window.Callback cb = mWindow.getCallback(); 436 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0 437 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev); 438 } 439 440 public boolean superDispatchKeyEvent(KeyEvent event) { 441 // Give priority to closing action modes if applicable. 442 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) { 443 final int action = event.getAction(); 444 // Back cancels action modes first. 445 if (mPrimaryActionMode != null) { 446 if (action == KeyEvent.ACTION_UP) { 447 mPrimaryActionMode.finish(); 448 } 449 return true; 450 } 451 } 452 453 if (super.dispatchKeyEvent(event)) { 454 return true; 455 } 456 457 return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event); 458 } 459 460 public boolean superDispatchKeyShortcutEvent(KeyEvent event) { 461 return super.dispatchKeyShortcutEvent(event); 462 } 463 464 public boolean superDispatchTouchEvent(MotionEvent event) { 465 return super.dispatchTouchEvent(event); 466 } 467 468 public boolean superDispatchTrackballEvent(MotionEvent event) { 469 return super.dispatchTrackballEvent(event); 470 } 471 472 public boolean superDispatchGenericMotionEvent(MotionEvent event) { 473 return super.dispatchGenericMotionEvent(event); 474 } 475 476 @Override 477 public boolean onTouchEvent(MotionEvent event) { 478 return onInterceptTouchEvent(event); 479 } 480 481 private boolean isOutOfInnerBounds(int x, int y) { 482 return x < 0 || y < 0 || x > getWidth() || y > getHeight(); 483 } 484 485 private boolean isOutOfBounds(int x, int y) { 486 return x < -5 || y < -5 || x > (getWidth() + 5) 487 || y > (getHeight() + 5); 488 } 489 490 @Override 491 public boolean onInterceptTouchEvent(MotionEvent event) { 492 int action = event.getAction(); 493 if (mHasCaption && isShowingCaption()) { 494 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event 495 // was (starting) outside the window. Window resizing events should be handled by 496 // WindowManager. 497 // TODO: Investigate how to handle the outside touch in window manager 498 // without generating these events. 499 // Currently we receive these because we need to enlarge the window's 500 // touch region so that the monitor channel receives the events 501 // in the outside touch area. 502 if (action == MotionEvent.ACTION_DOWN) { 503 final int x = (int) event.getX(); 504 final int y = (int) event.getY(); 505 if (isOutOfInnerBounds(x, y)) { 506 return true; 507 } 508 } 509 } 510 511 if (mFeatureId >= 0) { 512 if (action == MotionEvent.ACTION_DOWN) { 513 int x = (int)event.getX(); 514 int y = (int)event.getY(); 515 if (isOutOfBounds(x, y)) { 516 mWindow.closePanel(mFeatureId); 517 return true; 518 } 519 } 520 } 521 522 if (!SWEEP_OPEN_MENU) { 523 return false; 524 } 525 526 if (mFeatureId >= 0) { 527 if (action == MotionEvent.ACTION_DOWN) { 528 Log.i(mLogTag, "Watchiing!"); 529 mWatchingForMenu = true; 530 mDownY = (int) event.getY(); 531 return false; 532 } 533 534 if (!mWatchingForMenu) { 535 return false; 536 } 537 538 int y = (int)event.getY(); 539 if (action == MotionEvent.ACTION_MOVE) { 540 if (y > (mDownY+30)) { 541 Log.i(mLogTag, "Closing!"); 542 mWindow.closePanel(mFeatureId); 543 mWatchingForMenu = false; 544 return true; 545 } 546 } else if (action == MotionEvent.ACTION_UP) { 547 mWatchingForMenu = false; 548 } 549 550 return false; 551 } 552 553 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY() 554 // + " (in " + getHeight() + ")"); 555 556 if (action == MotionEvent.ACTION_DOWN) { 557 int y = (int)event.getY(); 558 if (y >= (getHeight()-5) && !mWindow.hasChildren()) { 559 Log.i(mLogTag, "Watching!"); 560 mWatchingForMenu = true; 561 } 562 return false; 563 } 564 565 if (!mWatchingForMenu) { 566 return false; 567 } 568 569 int y = (int)event.getY(); 570 if (action == MotionEvent.ACTION_MOVE) { 571 if (y < (getHeight()-30)) { 572 Log.i(mLogTag, "Opening!"); 573 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent( 574 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU)); 575 mWatchingForMenu = false; 576 return true; 577 } 578 } else if (action == MotionEvent.ACTION_UP) { 579 mWatchingForMenu = false; 580 } 581 582 return false; 583 } 584 585 @Override 586 public void sendAccessibilityEvent(int eventType) { 587 if (!AccessibilityManager.getInstance(mContext).isEnabled()) { 588 return; 589 } 590 591 // if we are showing a feature that should be announced and one child 592 // make this child the event source since this is the feature itself 593 // otherwise the callback will take over and announce its client 594 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL || 595 mFeatureId == Window.FEATURE_CONTEXT_MENU || 596 mFeatureId == Window.FEATURE_PROGRESS || 597 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS) 598 && getChildCount() == 1) { 599 getChildAt(0).sendAccessibilityEvent(eventType); 600 } else { 601 super.sendAccessibilityEvent(eventType); 602 } 603 } 604 605 @Override 606 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) { 607 final Window.Callback cb = mWindow.getCallback(); 608 if (cb != null && !mWindow.isDestroyed()) { 609 if (cb.dispatchPopulateAccessibilityEvent(event)) { 610 return true; 611 } 612 } 613 return super.dispatchPopulateAccessibilityEventInternal(event); 614 } 615 616 @Override 617 protected boolean setFrame(int l, int t, int r, int b) { 618 boolean changed = super.setFrame(l, t, r, b); 619 if (changed) { 620 final Rect drawingBounds = mDrawingBounds; 621 getDrawingRect(drawingBounds); 622 623 Drawable fg = getForeground(); 624 if (fg != null) { 625 final Rect frameOffsets = mFrameOffsets; 626 drawingBounds.left += frameOffsets.left; 627 drawingBounds.top += frameOffsets.top; 628 drawingBounds.right -= frameOffsets.right; 629 drawingBounds.bottom -= frameOffsets.bottom; 630 fg.setBounds(drawingBounds); 631 final Rect framePadding = mFramePadding; 632 drawingBounds.left += framePadding.left - frameOffsets.left; 633 drawingBounds.top += framePadding.top - frameOffsets.top; 634 drawingBounds.right -= framePadding.right - frameOffsets.right; 635 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom; 636 } 637 638 // Need to call super here as we pretend to be having the original background. 639 Drawable bg = super.getBackground(); 640 if (bg != null) { 641 bg.setBounds(drawingBounds); 642 } 643 644 if (SWEEP_OPEN_MENU) { 645 if (mMenuBackground == null && mFeatureId < 0 646 && mWindow.getAttributes().height 647 == WindowManager.LayoutParams.MATCH_PARENT) { 648 mMenuBackground = getContext().getDrawable( 649 R.drawable.menu_background); 650 } 651 if (mMenuBackground != null) { 652 mMenuBackground.setBounds(drawingBounds.left, 653 drawingBounds.bottom-6, drawingBounds.right, 654 drawingBounds.bottom+20); 655 } 656 } 657 } 658 return changed; 659 } 660 661 @Override 662 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 663 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics(); 664 final boolean isPortrait = 665 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT; 666 667 final int widthMode = getMode(widthMeasureSpec); 668 final int heightMode = getMode(heightMeasureSpec); 669 670 boolean fixedWidth = false; 671 mApplyFloatingHorizontalInsets = false; 672 if (widthMode == AT_MOST) { 673 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor; 674 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) { 675 final int w; 676 if (tvw.type == TypedValue.TYPE_DIMENSION) { 677 w = (int) tvw.getDimension(metrics); 678 } else if (tvw.type == TypedValue.TYPE_FRACTION) { 679 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels); 680 } else { 681 w = 0; 682 } 683 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w); 684 final int widthSize = MeasureSpec.getSize(widthMeasureSpec); 685 if (w > 0) { 686 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 687 Math.min(w, widthSize), EXACTLY); 688 fixedWidth = true; 689 } else { 690 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 691 widthSize - mFloatingInsets.left - mFloatingInsets.right, 692 AT_MOST); 693 mApplyFloatingHorizontalInsets = true; 694 } 695 } 696 } 697 698 mApplyFloatingVerticalInsets = false; 699 if (heightMode == AT_MOST) { 700 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor 701 : mWindow.mFixedHeightMinor; 702 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) { 703 final int h; 704 if (tvh.type == TypedValue.TYPE_DIMENSION) { 705 h = (int) tvh.getDimension(metrics); 706 } else if (tvh.type == TypedValue.TYPE_FRACTION) { 707 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels); 708 } else { 709 h = 0; 710 } 711 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h); 712 final int heightSize = MeasureSpec.getSize(heightMeasureSpec); 713 if (h > 0) { 714 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 715 Math.min(h, heightSize), EXACTLY); 716 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) { 717 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 718 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST); 719 mApplyFloatingVerticalInsets = true; 720 } 721 } 722 } 723 724 getOutsets(mOutsets); 725 if (mOutsets.top > 0 || mOutsets.bottom > 0) { 726 int mode = MeasureSpec.getMode(heightMeasureSpec); 727 if (mode != MeasureSpec.UNSPECIFIED) { 728 int height = MeasureSpec.getSize(heightMeasureSpec); 729 heightMeasureSpec = MeasureSpec.makeMeasureSpec( 730 height + mOutsets.top + mOutsets.bottom, mode); 731 } 732 } 733 if (mOutsets.left > 0 || mOutsets.right > 0) { 734 int mode = MeasureSpec.getMode(widthMeasureSpec); 735 if (mode != MeasureSpec.UNSPECIFIED) { 736 int width = MeasureSpec.getSize(widthMeasureSpec); 737 widthMeasureSpec = MeasureSpec.makeMeasureSpec( 738 width + mOutsets.left + mOutsets.right, mode); 739 } 740 } 741 742 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 743 744 int width = getMeasuredWidth(); 745 boolean measure = false; 746 747 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY); 748 749 if (!fixedWidth && widthMode == AT_MOST) { 750 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor; 751 if (tv.type != TypedValue.TYPE_NULL) { 752 final int min; 753 if (tv.type == TypedValue.TYPE_DIMENSION) { 754 min = (int)tv.getDimension(metrics); 755 } else if (tv.type == TypedValue.TYPE_FRACTION) { 756 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth); 757 } else { 758 min = 0; 759 } 760 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::" 761 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth); 762 763 if (width < min) { 764 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY); 765 measure = true; 766 } 767 } 768 } 769 770 // TODO: Support height? 771 772 if (measure) { 773 super.onMeasure(widthMeasureSpec, heightMeasureSpec); 774 } 775 } 776 777 @Override onLayout(boolean changed, int left, int top, int right, int bottom)778 protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 779 super.onLayout(changed, left, top, right, bottom); 780 getOutsets(mOutsets); 781 if (mOutsets.left > 0) { 782 offsetLeftAndRight(-mOutsets.left); 783 } 784 if (mOutsets.top > 0) { 785 offsetTopAndBottom(-mOutsets.top); 786 } 787 if (mApplyFloatingVerticalInsets) { 788 offsetTopAndBottom(mFloatingInsets.top); 789 } 790 if (mApplyFloatingHorizontalInsets) { 791 offsetLeftAndRight(mFloatingInsets.left); 792 } 793 794 // If the application changed its SystemUI metrics, we might also have to adapt 795 // our shadow elevation. 796 updateElevation(); 797 mAllowUpdateElevation = true; 798 799 if (changed 800 && (mResizeMode == RESIZE_MODE_DOCKED_DIVIDER 801 || mDrawLegacyNavigationBarBackground)) { 802 getViewRootImpl().requestInvalidateRootRenderNode(); 803 } 804 } 805 806 @Override draw(Canvas canvas)807 public void draw(Canvas canvas) { 808 super.draw(canvas); 809 810 if (mMenuBackground != null) { 811 mMenuBackground.draw(canvas); 812 } 813 } 814 815 @Override showContextMenuForChild(View originalView)816 public boolean showContextMenuForChild(View originalView) { 817 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN); 818 } 819 820 @Override showContextMenuForChild(View originalView, float x, float y)821 public boolean showContextMenuForChild(View originalView, float x, float y) { 822 return showContextMenuForChildInternal(originalView, x, y); 823 } 824 showContextMenuForChildInternal(View originalView, float x, float y)825 private boolean showContextMenuForChildInternal(View originalView, 826 float x, float y) { 827 // Only allow one context menu at a time. 828 if (mWindow.mContextMenuHelper != null) { 829 mWindow.mContextMenuHelper.dismiss(); 830 mWindow.mContextMenuHelper = null; 831 } 832 833 // Reuse the context menu builder. 834 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback; 835 if (mWindow.mContextMenu == null) { 836 mWindow.mContextMenu = new ContextMenuBuilder(getContext()); 837 mWindow.mContextMenu.setCallback(callback); 838 } else { 839 mWindow.mContextMenu.clearAll(); 840 } 841 842 final MenuHelper helper; 843 final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y); 844 if (isPopup) { 845 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y); 846 } else { 847 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken()); 848 } 849 850 if (helper != null) { 851 // If it's a dialog, the callback needs to handle showing 852 // sub-menus. Either way, the callback is required for propagating 853 // selection to Context.onContextMenuItemSelected(). 854 callback.setShowDialogForSubmenu(!isPopup); 855 helper.setPresenterCallback(callback); 856 } 857 858 mWindow.mContextMenuHelper = helper; 859 return helper != null; 860 } 861 862 @Override startActionModeForChild(View originalView, ActionMode.Callback callback)863 public ActionMode startActionModeForChild(View originalView, 864 ActionMode.Callback callback) { 865 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY); 866 } 867 868 @Override startActionModeForChild( View child, ActionMode.Callback callback, int type)869 public ActionMode startActionModeForChild( 870 View child, ActionMode.Callback callback, int type) { 871 return startActionMode(child, callback, type); 872 } 873 874 @Override startActionMode(ActionMode.Callback callback)875 public ActionMode startActionMode(ActionMode.Callback callback) { 876 return startActionMode(callback, ActionMode.TYPE_PRIMARY); 877 } 878 879 @Override startActionMode(ActionMode.Callback callback, int type)880 public ActionMode startActionMode(ActionMode.Callback callback, int type) { 881 return startActionMode(this, callback, type); 882 } 883 startActionMode( View originatingView, ActionMode.Callback callback, int type)884 private ActionMode startActionMode( 885 View originatingView, ActionMode.Callback callback, int type) { 886 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback); 887 ActionMode mode = null; 888 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) { 889 try { 890 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type); 891 } catch (AbstractMethodError ame) { 892 // Older apps might not implement the typed version of this method. 893 if (type == ActionMode.TYPE_PRIMARY) { 894 try { 895 mode = mWindow.getCallback().onWindowStartingActionMode( 896 wrappedCallback); 897 } catch (AbstractMethodError ame2) { 898 // Older apps might not implement this callback method at all. 899 } 900 } 901 } 902 } 903 if (mode != null) { 904 if (mode.getType() == ActionMode.TYPE_PRIMARY) { 905 cleanupPrimaryActionMode(); 906 mPrimaryActionMode = mode; 907 } else if (mode.getType() == ActionMode.TYPE_FLOATING) { 908 if (mFloatingActionMode != null) { 909 mFloatingActionMode.finish(); 910 } 911 mFloatingActionMode = mode; 912 } 913 } else { 914 mode = createActionMode(type, wrappedCallback, originatingView); 915 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) { 916 setHandledActionMode(mode); 917 } else { 918 mode = null; 919 } 920 } 921 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) { 922 try { 923 mWindow.getCallback().onActionModeStarted(mode); 924 } catch (AbstractMethodError ame) { 925 // Older apps might not implement this callback method. 926 } 927 } 928 return mode; 929 } 930 cleanupPrimaryActionMode()931 private void cleanupPrimaryActionMode() { 932 if (mPrimaryActionMode != null) { 933 mPrimaryActionMode.finish(); 934 mPrimaryActionMode = null; 935 } 936 if (mPrimaryActionModeView != null) { 937 mPrimaryActionModeView.killMode(); 938 } 939 } 940 cleanupFloatingActionModeViews()941 private void cleanupFloatingActionModeViews() { 942 if (mFloatingToolbar != null) { 943 mFloatingToolbar.dismiss(); 944 mFloatingToolbar = null; 945 } 946 if (mFloatingActionModeOriginatingView != null) { 947 if (mFloatingToolbarPreDrawListener != null) { 948 mFloatingActionModeOriginatingView.getViewTreeObserver() 949 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener); 950 mFloatingToolbarPreDrawListener = null; 951 } 952 mFloatingActionModeOriginatingView = null; 953 } 954 } 955 startChanging()956 void startChanging() { 957 mChanging = true; 958 } 959 finishChanging()960 void finishChanging() { 961 mChanging = false; 962 drawableChanged(); 963 } 964 setWindowBackground(Drawable drawable)965 public void setWindowBackground(Drawable drawable) { 966 if (mOriginalBackgroundDrawable != drawable) { 967 mOriginalBackgroundDrawable = drawable; 968 updateBackgroundDrawable(); 969 if (drawable != null) { 970 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable, 971 mWindow.isTranslucent() || mWindow.isShowingWallpaper()); 972 } else { 973 mResizingBackgroundDrawable = getResizingBackgroundDrawable( 974 mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable, 975 mWindow.isTranslucent() || mWindow.isShowingWallpaper()); 976 } 977 if (mResizingBackgroundDrawable != null) { 978 mResizingBackgroundDrawable.getPadding(mBackgroundPadding); 979 } else { 980 mBackgroundPadding.setEmpty(); 981 } 982 drawableChanged(); 983 } 984 } 985 986 @Override setBackgroundDrawable(Drawable background)987 public void setBackgroundDrawable(Drawable background) { 988 // TODO: This should route through setWindowBackground, but late in the release to make this 989 // change. 990 if (mOriginalBackgroundDrawable != background) { 991 mOriginalBackgroundDrawable = background; 992 updateBackgroundDrawable(); 993 if (!View.sBrokenWindowBackground) { 994 drawableChanged(); 995 } 996 } 997 } 998 setWindowFrame(Drawable drawable)999 public void setWindowFrame(Drawable drawable) { 1000 if (getForeground() != drawable) { 1001 setForeground(drawable); 1002 if (drawable != null) { 1003 drawable.getPadding(mFramePadding); 1004 } else { 1005 mFramePadding.setEmpty(); 1006 } 1007 drawableChanged(); 1008 } 1009 } 1010 1011 @Override onWindowSystemUiVisibilityChanged(int visible)1012 public void onWindowSystemUiVisibilityChanged(int visible) { 1013 updateColorViews(null /* insets */, true /* animate */); 1014 updateDecorCaptionStatus(getResources().getConfiguration()); 1015 1016 if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) { 1017 updateStatusGuardColor(); 1018 } 1019 } 1020 1021 @Override onApplyWindowInsets(WindowInsets insets)1022 public WindowInsets onApplyWindowInsets(WindowInsets insets) { 1023 final WindowManager.LayoutParams attrs = mWindow.getAttributes(); 1024 mFloatingInsets.setEmpty(); 1025 if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) { 1026 // For dialog windows we want to make sure they don't go over the status bar or nav bar. 1027 // We consume the system insets and we will reuse them later during the measure phase. 1028 // We allow the app to ignore this and handle insets itself by using 1029 // FLAG_LAYOUT_IN_SCREEN. 1030 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) { 1031 mFloatingInsets.top = insets.getSystemWindowInsetTop(); 1032 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom(); 1033 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 1034 0, insets.getSystemWindowInsetBottom()); 1035 } 1036 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) { 1037 mFloatingInsets.left = insets.getSystemWindowInsetTop(); 1038 mFloatingInsets.right = insets.getSystemWindowInsetBottom(); 1039 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0, 1040 insets.getSystemWindowInsetRight(), 0); 1041 } 1042 } 1043 mFrameOffsets.set(insets.getSystemWindowInsetsAsRect()); 1044 insets = updateColorViews(insets, true /* animate */); 1045 insets = updateStatusGuard(insets); 1046 if (getForeground() != null) { 1047 drawableChanged(); 1048 } 1049 return insets; 1050 } 1051 1052 @Override isTransitionGroup()1053 public boolean isTransitionGroup() { 1054 return false; 1055 } 1056 getColorViewTopInset(int stableTop, int systemTop)1057 public static int getColorViewTopInset(int stableTop, int systemTop) { 1058 return Math.min(stableTop, systemTop); 1059 } 1060 getColorViewBottomInset(int stableBottom, int systemBottom)1061 public static int getColorViewBottomInset(int stableBottom, int systemBottom) { 1062 return Math.min(stableBottom, systemBottom); 1063 } 1064 getColorViewRightInset(int stableRight, int systemRight)1065 public static int getColorViewRightInset(int stableRight, int systemRight) { 1066 return Math.min(stableRight, systemRight); 1067 } 1068 getColorViewLeftInset(int stableLeft, int systemLeft)1069 public static int getColorViewLeftInset(int stableLeft, int systemLeft) { 1070 return Math.min(stableLeft, systemLeft); 1071 } 1072 isNavBarToRightEdge(int bottomInset, int rightInset)1073 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { 1074 return bottomInset == 0 && rightInset > 0; 1075 } 1076 isNavBarToLeftEdge(int bottomInset, int leftInset)1077 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { 1078 return bottomInset == 0 && leftInset > 0; 1079 } 1080 getNavBarSize(int bottomInset, int rightInset, int leftInset)1081 public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) { 1082 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset 1083 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset; 1084 } 1085 getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets, Rect contentInsets, Rect outRect, float scale)1086 public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets, 1087 Rect contentInsets, Rect outRect, float scale) { 1088 final int bottomInset = 1089 (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale); 1090 final int leftInset = 1091 (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale); 1092 final int rightInset = 1093 (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale); 1094 final int size = getNavBarSize(bottomInset, rightInset, leftInset); 1095 if (isNavBarToRightEdge(bottomInset, rightInset)) { 1096 outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight); 1097 } else if (isNavBarToLeftEdge(bottomInset, leftInset)) { 1098 outRect.set(0, 0, size, canvasHeight); 1099 } else { 1100 outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight); 1101 } 1102 } 1103 updateColorViews(WindowInsets insets, boolean animate)1104 WindowInsets updateColorViews(WindowInsets insets, boolean animate) { 1105 WindowManager.LayoutParams attrs = mWindow.getAttributes(); 1106 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility(); 1107 1108 // IME is an exceptional floating window that requires color view. 1109 final boolean isImeWindow = 1110 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD; 1111 if (!mWindow.mIsFloating || isImeWindow) { 1112 boolean disallowAnimate = !isLaidOut(); 1113 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags) 1114 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0; 1115 mLastWindowFlags = attrs.flags; 1116 1117 if (insets != null) { 1118 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(), 1119 insets.getSystemWindowInsetTop()); 1120 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(), 1121 insets.getSystemWindowInsetBottom()); 1122 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(), 1123 insets.getSystemWindowInsetRight()); 1124 mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(), 1125 insets.getSystemWindowInsetLeft()); 1126 1127 // Don't animate if the presence of stable insets has changed, because that 1128 // indicates that the window was either just added and received them for the 1129 // first time, or the window size or position has changed. 1130 boolean hasTopStableInset = insets.getStableInsetTop() != 0; 1131 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset); 1132 mLastHasTopStableInset = hasTopStableInset; 1133 1134 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0; 1135 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset); 1136 mLastHasBottomStableInset = hasBottomStableInset; 1137 1138 boolean hasRightStableInset = insets.getStableInsetRight() != 0; 1139 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset); 1140 mLastHasRightStableInset = hasRightStableInset; 1141 1142 boolean hasLeftStableInset = insets.getStableInsetLeft() != 0; 1143 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset); 1144 mLastHasLeftStableInset = hasLeftStableInset; 1145 1146 mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars(); 1147 } 1148 1149 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset); 1150 boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset); 1151 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset); 1152 updateColorViewInt(mNavigationColorViewState, sysUiVisibility, 1153 calculateNavigationBarColor(), mWindow.mNavigationBarDividerColor, navBarSize, 1154 navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge, 1155 0 /* sideInset */, animate && !disallowAnimate, 1156 mForceWindowDrawsBarBackgrounds); 1157 boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground; 1158 mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible 1159 && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0; 1160 if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) { 1161 ViewRootImpl vri = getViewRootImpl(); 1162 if (vri != null) { 1163 vri.requestInvalidateRootRenderNode(); 1164 } 1165 } 1166 1167 boolean statusBarNeedsRightInset = navBarToRightEdge 1168 && mNavigationColorViewState.present; 1169 boolean statusBarNeedsLeftInset = navBarToLeftEdge 1170 && mNavigationColorViewState.present; 1171 int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset 1172 : statusBarNeedsLeftInset ? mLastLeftInset : 0; 1173 updateColorViewInt(mStatusColorViewState, sysUiVisibility, 1174 calculateStatusBarColor(), 0, mLastTopInset, 1175 false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset, 1176 animate && !disallowAnimate, 1177 mForceWindowDrawsBarBackgrounds); 1178 } 1179 1180 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or 1181 // mForceWindowDrawsBarBackgrounds, we still need to ensure that the rest of the view 1182 // hierarchy doesn't notice it, unless they've explicitly asked for it. 1183 // 1184 // Note: We don't need to check for IN_SCREEN or INSET_DECOR because unlike the status bar, 1185 // these flags wouldn't make the window draw behind the navigation bar, unless 1186 // LAYOUT_HIDE_NAVIGATION was set. 1187 boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0; 1188 boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds 1189 && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0 1190 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 1191 && !hideNavigation) 1192 || (mLastShouldAlwaysConsumeSystemBars && hideNavigation); 1193 1194 boolean consumingNavBar = 1195 ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 1196 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0 1197 && !hideNavigation) 1198 || forceConsumingNavBar; 1199 1200 // If we didn't request fullscreen layout, but we still got it because of the 1201 // mForceWindowDrawsBarBackgrounds flag, also consume top inset. 1202 // If we should always consume system bars, only consume that if the app wanted to go to 1203 // fullscreen, as othrewise we can expect the app to handle it. 1204 boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0 1205 || (attrs.flags & FLAG_FULLSCREEN) != 0; 1206 boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0 1207 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0 1208 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0 1209 && mForceWindowDrawsBarBackgrounds 1210 && mLastTopInset != 0 1211 || (mLastShouldAlwaysConsumeSystemBars && fullscreen); 1212 1213 int consumedTop = consumingStatusBar ? mLastTopInset : 0; 1214 int consumedRight = consumingNavBar ? mLastRightInset : 0; 1215 int consumedBottom = consumingNavBar ? mLastBottomInset : 0; 1216 int consumedLeft = consumingNavBar ? mLastLeftInset : 0; 1217 1218 if (mContentRoot != null 1219 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) { 1220 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams(); 1221 if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight 1222 || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) { 1223 lp.topMargin = consumedTop; 1224 lp.rightMargin = consumedRight; 1225 lp.bottomMargin = consumedBottom; 1226 lp.leftMargin = consumedLeft; 1227 mContentRoot.setLayoutParams(lp); 1228 1229 if (insets == null) { 1230 // The insets have changed, but we're not currently in the process 1231 // of dispatching them. 1232 requestApplyInsets(); 1233 } 1234 } 1235 if (insets != null) { 1236 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom); 1237 } 1238 } 1239 1240 if (forceConsumingNavBar) { 1241 mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset); 1242 } else { 1243 mBackgroundInsets = Insets.NONE; 1244 } 1245 updateBackgroundDrawable(); 1246 1247 if (insets != null) { 1248 insets = insets.consumeStableInsets(); 1249 } 1250 return insets; 1251 } 1252 1253 /** 1254 * Updates the background drawable, applying padding to it in case we {@link #mBackgroundInsets} 1255 * are set. 1256 */ updateBackgroundDrawable()1257 private void updateBackgroundDrawable() { 1258 // Background insets can be null if super constructor calls setBackgroundDrawable. 1259 if (mBackgroundInsets == null) { 1260 mBackgroundInsets = Insets.NONE; 1261 } 1262 if (mBackgroundInsets.equals(mLastBackgroundInsets) 1263 && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) { 1264 return; 1265 } 1266 if (mOriginalBackgroundDrawable == null || mBackgroundInsets.equals(Insets.NONE)) { 1267 1268 // Call super since we are intercepting setBackground on this class. 1269 super.setBackgroundDrawable(mOriginalBackgroundDrawable); 1270 } else { 1271 1272 // Call super since we are intercepting setBackground on this class. 1273 super.setBackgroundDrawable(new InsetDrawable(mOriginalBackgroundDrawable, 1274 mBackgroundInsets.left, mBackgroundInsets.top, 1275 mBackgroundInsets.right, mBackgroundInsets.bottom) { 1276 1277 /** 1278 * Return inner padding so we don't apply the padding again in 1279 * {@link DecorView#drawableChanged()} 1280 */ 1281 @Override 1282 public boolean getPadding(Rect padding) { 1283 return getDrawable().getPadding(padding); 1284 } 1285 }); 1286 } 1287 mLastBackgroundInsets = mBackgroundInsets; 1288 mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable; 1289 } 1290 1291 @Override getBackground()1292 public Drawable getBackground() { 1293 return mOriginalBackgroundDrawable; 1294 } 1295 calculateStatusBarColor()1296 private int calculateStatusBarColor() { 1297 return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS, 1298 mSemiTransparentBarColor, mWindow.mStatusBarColor, 1299 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR, 1300 mWindow.mEnsureStatusBarContrastWhenTransparent); 1301 } 1302 calculateNavigationBarColor()1303 private int calculateNavigationBarColor() { 1304 return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION, 1305 mSemiTransparentBarColor, mWindow.mNavigationBarColor, 1306 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR, 1307 mWindow.mEnsureNavigationBarContrastWhenTransparent 1308 && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim)); 1309 } 1310 calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor, int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent)1311 public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor, 1312 int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) { 1313 if ((flags & translucentFlag) != 0) { 1314 return semiTransparentBarColor; 1315 } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) { 1316 return Color.BLACK; 1317 } else if (scrimTransparent && Color.alpha(barColor) == 0) { 1318 boolean light = (sysuiVis & lightSysuiFlag) != 0; 1319 return light ? SCRIM_LIGHT : semiTransparentBarColor; 1320 } else { 1321 return barColor; 1322 } 1323 } 1324 getCurrentColor(ColorViewState state)1325 private int getCurrentColor(ColorViewState state) { 1326 if (state.visible) { 1327 return state.color; 1328 } else { 1329 return 0; 1330 } 1331 } 1332 1333 /** 1334 * Update a color view 1335 * 1336 * @param state the color view to update. 1337 * @param sysUiVis the current systemUiVisibility to apply. 1338 * @param color the current color to apply. 1339 * @param dividerColor the current divider color to apply. 1340 * @param size the current size in the non-parent-matching dimension. 1341 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a 1342 * horizontal edge, 1343 * @param sideMargin sideMargin for the color view. 1344 * @param animate if true, the change will be animated. 1345 */ updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force)1346 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color, 1347 int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin, 1348 boolean animate, boolean force) { 1349 state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force); 1350 boolean show = state.attributes.isVisible(state.present, color, 1351 mWindow.getAttributes().flags, force); 1352 boolean showView = show && !isResizing() && size > 0; 1353 1354 boolean visibilityChanged = false; 1355 View view = state.view; 1356 1357 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size; 1358 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT; 1359 int resolvedGravity = verticalBar 1360 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity) 1361 : state.attributes.verticalGravity; 1362 1363 if (view == null) { 1364 if (showView) { 1365 state.view = view = new View(mContext); 1366 setColor(view, color, dividerColor, verticalBar, seascape); 1367 view.setTransitionName(state.attributes.transitionName); 1368 view.setId(state.attributes.id); 1369 visibilityChanged = true; 1370 view.setVisibility(INVISIBLE); 1371 state.targetVisibility = VISIBLE; 1372 1373 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight, 1374 resolvedGravity); 1375 if (seascape) { 1376 lp.leftMargin = sideMargin; 1377 } else { 1378 lp.rightMargin = sideMargin; 1379 } 1380 addView(view, lp); 1381 updateColorViewTranslations(); 1382 } 1383 } else { 1384 int vis = showView ? VISIBLE : INVISIBLE; 1385 visibilityChanged = state.targetVisibility != vis; 1386 state.targetVisibility = vis; 1387 LayoutParams lp = (LayoutParams) view.getLayoutParams(); 1388 int rightMargin = seascape ? 0 : sideMargin; 1389 int leftMargin = seascape ? sideMargin : 0; 1390 if (lp.height != resolvedHeight || lp.width != resolvedWidth 1391 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin 1392 || lp.leftMargin != leftMargin) { 1393 lp.height = resolvedHeight; 1394 lp.width = resolvedWidth; 1395 lp.gravity = resolvedGravity; 1396 lp.rightMargin = rightMargin; 1397 lp.leftMargin = leftMargin; 1398 view.setLayoutParams(lp); 1399 } 1400 if (showView) { 1401 setColor(view, color, dividerColor, verticalBar, seascape); 1402 } 1403 } 1404 if (visibilityChanged) { 1405 view.animate().cancel(); 1406 if (animate && !isResizing()) { 1407 if (showView) { 1408 if (view.getVisibility() != VISIBLE) { 1409 view.setVisibility(VISIBLE); 1410 view.setAlpha(0.0f); 1411 } 1412 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator). 1413 setDuration(mBarEnterExitDuration); 1414 } else { 1415 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator) 1416 .setDuration(mBarEnterExitDuration) 1417 .withEndAction(new Runnable() { 1418 @Override 1419 public void run() { 1420 state.view.setAlpha(1.0f); 1421 state.view.setVisibility(INVISIBLE); 1422 } 1423 }); 1424 } 1425 } else { 1426 view.setAlpha(1.0f); 1427 view.setVisibility(showView ? VISIBLE : INVISIBLE); 1428 } 1429 } 1430 state.visible = show; 1431 state.color = color; 1432 } 1433 setColor(View v, int color, int dividerColor, boolean verticalBar, boolean seascape)1434 private static void setColor(View v, int color, int dividerColor, boolean verticalBar, 1435 boolean seascape) { 1436 if (dividerColor != 0) { 1437 final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag(); 1438 if (dir == null || dir.first != verticalBar || dir.second != seascape) { 1439 final int size = Math.round( 1440 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1, 1441 v.getContext().getResources().getDisplayMetrics())); 1442 // Use an inset to make the divider line on the side that faces the app. 1443 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color), 1444 verticalBar && !seascape ? size : 0, 1445 !verticalBar ? size : 0, 1446 verticalBar && seascape ? size : 0, 0); 1447 v.setBackground(new LayerDrawable(new Drawable[] { 1448 new ColorDrawable(dividerColor), d })); 1449 v.setTag(new Pair<>(verticalBar, seascape)); 1450 } else { 1451 final LayerDrawable d = (LayerDrawable) v.getBackground(); 1452 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1)); 1453 ((ColorDrawable) inset.getDrawable()).setColor(color); 1454 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor); 1455 } 1456 } else { 1457 v.setTag(null); 1458 v.setBackgroundColor(color); 1459 } 1460 } 1461 updateColorViewTranslations()1462 private void updateColorViewTranslations() { 1463 // Put the color views back in place when they get moved off the screen 1464 // due to the the ViewRootImpl panning. 1465 int rootScrollY = mRootScrollY; 1466 if (mStatusColorViewState.view != null) { 1467 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0); 1468 } 1469 if (mNavigationColorViewState.view != null) { 1470 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0); 1471 } 1472 } 1473 1474 private WindowInsets updateStatusGuard(WindowInsets insets) { 1475 boolean showStatusGuard = false; 1476 // Show the status guard when the non-overlay contextual action bar is showing 1477 if (mPrimaryActionModeView != null) { 1478 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) { 1479 // Insets are magic! 1480 final MarginLayoutParams mlp = (MarginLayoutParams) 1481 mPrimaryActionModeView.getLayoutParams(); 1482 boolean mlpChanged = false; 1483 if (mPrimaryActionModeView.isShown()) { 1484 if (mTempRect == null) { 1485 mTempRect = new Rect(); 1486 } 1487 final Rect rect = mTempRect; 1488 1489 // Apply the insets that have not been applied by the contentParent yet. 1490 WindowInsets innerInsets = 1491 mWindow.mContentParent.computeSystemWindowInsets(insets, rect); 1492 int newTopMargin = innerInsets.getSystemWindowInsetTop(); 1493 int newLeftMargin = innerInsets.getSystemWindowInsetLeft(); 1494 int newRightMargin = innerInsets.getSystemWindowInsetRight(); 1495 1496 // Must use root window insets for the guard, because the color views consume 1497 // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but 1498 // the status guard is attached at the root. 1499 WindowInsets rootInsets = getRootWindowInsets(); 1500 int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft(); 1501 int newGuardRightMargin = rootInsets.getSystemWindowInsetRight(); 1502 1503 if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin 1504 || mlp.rightMargin != newRightMargin) { 1505 mlpChanged = true; 1506 mlp.topMargin = newTopMargin; 1507 mlp.leftMargin = newLeftMargin; 1508 mlp.rightMargin = newRightMargin; 1509 } 1510 1511 if (newTopMargin > 0 && mStatusGuard == null) { 1512 mStatusGuard = new View(mContext); 1513 mStatusGuard.setVisibility(GONE); 1514 final LayoutParams lp = new LayoutParams(MATCH_PARENT, 1515 mlp.topMargin, Gravity.LEFT | Gravity.TOP); 1516 lp.leftMargin = newGuardLeftMargin; 1517 lp.rightMargin = newGuardRightMargin; 1518 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp); 1519 } else if (mStatusGuard != null) { 1520 final LayoutParams lp = (LayoutParams) 1521 mStatusGuard.getLayoutParams(); 1522 if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin 1523 || lp.rightMargin != newGuardRightMargin) { 1524 lp.height = mlp.topMargin; 1525 lp.leftMargin = newGuardLeftMargin; 1526 lp.rightMargin = newGuardRightMargin; 1527 mStatusGuard.setLayoutParams(lp); 1528 } 1529 } 1530 1531 // The action mode's theme may differ from the app, so 1532 // always show the status guard above it if we have one. 1533 showStatusGuard = mStatusGuard != null; 1534 1535 if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) { 1536 // If it wasn't previously shown, the color may be stale 1537 updateStatusGuardColor(); 1538 } 1539 1540 // We only need to consume the insets if the action 1541 // mode is overlaid on the app content (e.g. it's 1542 // sitting in a FrameLayout, see 1543 // screen_simple_overlay_action_mode.xml). 1544 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate() 1545 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0; 1546 if (nonOverlay && showStatusGuard) { 1547 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0); 1548 } 1549 } else { 1550 // reset top margin 1551 if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) { 1552 mlpChanged = true; 1553 mlp.topMargin = 0; 1554 } 1555 } 1556 if (mlpChanged) { 1557 mPrimaryActionModeView.setLayoutParams(mlp); 1558 } 1559 } 1560 } 1561 if (mStatusGuard != null) { 1562 mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE); 1563 } 1564 return insets; 1565 } 1566 updateStatusGuardColor()1567 private void updateStatusGuardColor() { 1568 boolean lightStatusBar = 1569 (getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0; 1570 mStatusGuard.setBackgroundColor(lightStatusBar 1571 ? mContext.getColor(R.color.decor_view_status_guard_light) 1572 : mContext.getColor(R.color.decor_view_status_guard)); 1573 } 1574 1575 /** 1576 * Overrides the view outline when the activity enters picture-in-picture to ensure that it has 1577 * an opaque shadow even if the window background is completely transparent. This only applies 1578 * to activities that are currently the task root. 1579 */ updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode)1580 public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) { 1581 if (mIsInPictureInPictureMode == isInPictureInPictureMode) { 1582 return; 1583 } 1584 1585 if (isInPictureInPictureMode) { 1586 final Window.WindowControllerCallback callback = 1587 mWindow.getWindowControllerCallback(); 1588 if (callback != null && callback.isTaskRoot()) { 1589 // Call super implementation directly as we don't want to save the PIP outline 1590 // provider to be restored 1591 super.setOutlineProvider(PIP_OUTLINE_PROVIDER); 1592 } 1593 } else { 1594 // Restore the previous outline provider 1595 if (getOutlineProvider() != mLastOutlineProvider) { 1596 setOutlineProvider(mLastOutlineProvider); 1597 } 1598 } 1599 mIsInPictureInPictureMode = isInPictureInPictureMode; 1600 } 1601 1602 @Override setOutlineProvider(ViewOutlineProvider provider)1603 public void setOutlineProvider(ViewOutlineProvider provider) { 1604 super.setOutlineProvider(provider); 1605 1606 // Save the outline provider set to ensure that we can restore when the activity leaves PiP 1607 mLastOutlineProvider = provider; 1608 } 1609 drawableChanged()1610 private void drawableChanged() { 1611 if (mChanging) { 1612 return; 1613 } 1614 1615 // Fields can be null if super constructor calls setBackgroundDrawable. 1616 Rect framePadding = mFramePadding != null ? mFramePadding : new Rect(); 1617 Rect backgroundPadding = mBackgroundPadding != null ? mBackgroundPadding : new Rect(); 1618 1619 setPadding(framePadding.left + backgroundPadding.left, 1620 framePadding.top + backgroundPadding.top, 1621 framePadding.right + backgroundPadding.right, 1622 framePadding.bottom + backgroundPadding.bottom); 1623 requestLayout(); 1624 invalidate(); 1625 1626 int opacity = PixelFormat.OPAQUE; 1627 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration; 1628 if (winConfig.hasWindowShadow()) { 1629 // If the window has a shadow, it must be translucent. 1630 opacity = PixelFormat.TRANSLUCENT; 1631 } else{ 1632 // Note: If there is no background, we will assume opaque. The 1633 // common case seems to be that an application sets there to be 1634 // no background so it can draw everything itself. For that, 1635 // we would like to assume OPAQUE and let the app force it to 1636 // the slower TRANSLUCENT mode if that is really what it wants. 1637 Drawable bg = getBackground(); 1638 Drawable fg = getForeground(); 1639 if (bg != null) { 1640 if (fg == null) { 1641 opacity = bg.getOpacity(); 1642 } else if (framePadding.left <= 0 && framePadding.top <= 0 1643 && framePadding.right <= 0 && framePadding.bottom <= 0) { 1644 // If the frame padding is zero, then we can be opaque 1645 // if either the frame -or- the background is opaque. 1646 int fop = fg.getOpacity(); 1647 int bop = bg.getOpacity(); 1648 if (false) 1649 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop); 1650 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) { 1651 opacity = PixelFormat.OPAQUE; 1652 } else if (fop == PixelFormat.UNKNOWN) { 1653 opacity = bop; 1654 } else if (bop == PixelFormat.UNKNOWN) { 1655 opacity = fop; 1656 } else { 1657 opacity = Drawable.resolveOpacity(fop, bop); 1658 } 1659 } else { 1660 // For now we have to assume translucent if there is a 1661 // frame with padding... there is no way to tell if the 1662 // frame and background together will draw all pixels. 1663 if (false) 1664 Log.v(mLogTag, "Padding: " + mFramePadding); 1665 opacity = PixelFormat.TRANSLUCENT; 1666 } 1667 } 1668 if (false) 1669 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg); 1670 } 1671 1672 if (false) 1673 Log.v(mLogTag, "Selected default opacity: " + opacity); 1674 1675 mDefaultOpacity = opacity; 1676 if (mFeatureId < 0) { 1677 mWindow.setDefaultWindowFormat(opacity); 1678 } 1679 } 1680 1681 @Override onWindowFocusChanged(boolean hasWindowFocus)1682 public void onWindowFocusChanged(boolean hasWindowFocus) { 1683 super.onWindowFocusChanged(hasWindowFocus); 1684 1685 // If the user is chording a menu shortcut, release the chord since 1686 // this window lost focus 1687 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus 1688 && mWindow.mPanelChordingKey != 0) { 1689 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL); 1690 } 1691 1692 final Window.Callback cb = mWindow.getCallback(); 1693 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) { 1694 cb.onWindowFocusChanged(hasWindowFocus); 1695 } 1696 1697 if (mPrimaryActionMode != null) { 1698 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus); 1699 } 1700 if (mFloatingActionMode != null) { 1701 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus); 1702 } 1703 1704 updateElevation(); 1705 } 1706 1707 @Override onAttachedToWindow()1708 protected void onAttachedToWindow() { 1709 super.onAttachedToWindow(); 1710 1711 final Window.Callback cb = mWindow.getCallback(); 1712 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) { 1713 cb.onAttachedToWindow(); 1714 } 1715 1716 if (mFeatureId == -1) { 1717 /* 1718 * The main window has been attached, try to restore any panels 1719 * that may have been open before. This is called in cases where 1720 * an activity is being killed for configuration change and the 1721 * menu was open. When the activity is recreated, the menu 1722 * should be shown again. 1723 */ 1724 mWindow.openPanelsAfterRestore(); 1725 } 1726 1727 if (!mWindowResizeCallbacksAdded) { 1728 // If there is no window callback installed there was no window set before. Set it now. 1729 // Note that our ViewRootImpl object will not change. 1730 getViewRootImpl().addWindowCallbacks(this); 1731 mWindowResizeCallbacksAdded = true; 1732 } else if (mBackdropFrameRenderer != null) { 1733 // We are resizing and this call happened due to a configuration change. Tell the 1734 // renderer about it. 1735 mBackdropFrameRenderer.onConfigurationChange(); 1736 } 1737 mWindow.onViewRootImplSet(getViewRootImpl()); 1738 } 1739 1740 @Override onDetachedFromWindow()1741 protected void onDetachedFromWindow() { 1742 super.onDetachedFromWindow(); 1743 1744 final Window.Callback cb = mWindow.getCallback(); 1745 if (cb != null && mFeatureId < 0) { 1746 cb.onDetachedFromWindow(); 1747 } 1748 1749 if (mWindow.mDecorContentParent != null) { 1750 mWindow.mDecorContentParent.dismissPopups(); 1751 } 1752 1753 if (mPrimaryActionModePopup != null) { 1754 removeCallbacks(mShowPrimaryActionModePopup); 1755 if (mPrimaryActionModePopup.isShowing()) { 1756 mPrimaryActionModePopup.dismiss(); 1757 } 1758 mPrimaryActionModePopup = null; 1759 } 1760 if (mFloatingToolbar != null) { 1761 mFloatingToolbar.dismiss(); 1762 mFloatingToolbar = null; 1763 } 1764 1765 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false); 1766 if (st != null && st.menu != null && mFeatureId < 0) { 1767 st.menu.close(); 1768 } 1769 1770 releaseThreadedRenderer(); 1771 1772 if (mWindowResizeCallbacksAdded) { 1773 getViewRootImpl().removeWindowCallbacks(this); 1774 mWindowResizeCallbacksAdded = false; 1775 } 1776 } 1777 1778 @Override onCloseSystemDialogs(String reason)1779 public void onCloseSystemDialogs(String reason) { 1780 if (mFeatureId >= 0) { 1781 mWindow.closeAllPanels(); 1782 } 1783 } 1784 willYouTakeTheSurface()1785 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() { 1786 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null; 1787 } 1788 willYouTakeTheInputQueue()1789 public InputQueue.Callback willYouTakeTheInputQueue() { 1790 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null; 1791 } 1792 setSurfaceType(int type)1793 public void setSurfaceType(int type) { 1794 mWindow.setType(type); 1795 } 1796 setSurfaceFormat(int format)1797 public void setSurfaceFormat(int format) { 1798 mWindow.setFormat(format); 1799 } 1800 setSurfaceKeepScreenOn(boolean keepOn)1801 public void setSurfaceKeepScreenOn(boolean keepOn) { 1802 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1803 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON); 1804 } 1805 1806 @Override onRootViewScrollYChanged(int rootScrollY)1807 public void onRootViewScrollYChanged(int rootScrollY) { 1808 mRootScrollY = rootScrollY; 1809 if (mDecorCaptionView != null) { 1810 mDecorCaptionView.onRootViewScrollYChanged(rootScrollY); 1811 } 1812 updateColorViewTranslations(); 1813 } 1814 createActionMode( int type, ActionMode.Callback2 callback, View originatingView)1815 private ActionMode createActionMode( 1816 int type, ActionMode.Callback2 callback, View originatingView) { 1817 switch (type) { 1818 case ActionMode.TYPE_PRIMARY: 1819 default: 1820 return createStandaloneActionMode(callback); 1821 case ActionMode.TYPE_FLOATING: 1822 return createFloatingActionMode(originatingView, callback); 1823 } 1824 } 1825 setHandledActionMode(ActionMode mode)1826 private void setHandledActionMode(ActionMode mode) { 1827 if (mode.getType() == ActionMode.TYPE_PRIMARY) { 1828 setHandledPrimaryActionMode(mode); 1829 } else if (mode.getType() == ActionMode.TYPE_FLOATING) { 1830 setHandledFloatingActionMode(mode); 1831 } 1832 } 1833 createStandaloneActionMode(ActionMode.Callback callback)1834 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) { 1835 endOnGoingFadeAnimation(); 1836 cleanupPrimaryActionMode(); 1837 // We want to create new mPrimaryActionModeView in two cases: if there is no existing 1838 // instance at all, or if there is one, but it is detached from window. The latter case 1839 // might happen when app is resized in multi-window mode and decor view is preserved 1840 // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause 1841 // app memory leaks because killMode() is called when the dismiss animation ends and from 1842 // cleanupPrimaryActionMode() invocation above. 1843 if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) { 1844 if (mWindow.isFloating()) { 1845 // Use the action bar theme. 1846 final TypedValue outValue = new TypedValue(); 1847 final Resources.Theme baseTheme = mContext.getTheme(); 1848 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true); 1849 1850 final Context actionBarContext; 1851 if (outValue.resourceId != 0) { 1852 final Resources.Theme actionBarTheme = mContext.getResources().newTheme(); 1853 actionBarTheme.setTo(baseTheme); 1854 actionBarTheme.applyStyle(outValue.resourceId, true); 1855 1856 actionBarContext = new ContextThemeWrapper(mContext, 0); 1857 actionBarContext.getTheme().setTo(actionBarTheme); 1858 } else { 1859 actionBarContext = mContext; 1860 } 1861 1862 mPrimaryActionModeView = new ActionBarContextView(actionBarContext); 1863 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null, 1864 R.attr.actionModePopupWindowStyle); 1865 mPrimaryActionModePopup.setWindowLayoutType( 1866 WindowManager.LayoutParams.TYPE_APPLICATION); 1867 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView); 1868 mPrimaryActionModePopup.setWidth(MATCH_PARENT); 1869 1870 actionBarContext.getTheme().resolveAttribute( 1871 R.attr.actionBarSize, outValue, true); 1872 final int height = TypedValue.complexToDimensionPixelSize(outValue.data, 1873 actionBarContext.getResources().getDisplayMetrics()); 1874 mPrimaryActionModeView.setContentHeight(height); 1875 mPrimaryActionModePopup.setHeight(WRAP_CONTENT); 1876 mShowPrimaryActionModePopup = new Runnable() { 1877 public void run() { 1878 mPrimaryActionModePopup.showAtLocation( 1879 mPrimaryActionModeView.getApplicationWindowToken(), 1880 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0); 1881 endOnGoingFadeAnimation(); 1882 1883 if (shouldAnimatePrimaryActionModeView()) { 1884 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 1885 0f, 1f); 1886 mFadeAnim.addListener(new AnimatorListenerAdapter() { 1887 @Override 1888 public void onAnimationStart(Animator animation) { 1889 mPrimaryActionModeView.setVisibility(VISIBLE); 1890 } 1891 1892 @Override 1893 public void onAnimationEnd(Animator animation) { 1894 mPrimaryActionModeView.setAlpha(1f); 1895 mFadeAnim = null; 1896 } 1897 }); 1898 mFadeAnim.start(); 1899 } else { 1900 mPrimaryActionModeView.setAlpha(1f); 1901 mPrimaryActionModeView.setVisibility(VISIBLE); 1902 } 1903 } 1904 }; 1905 } else { 1906 ViewStub stub = findViewById(R.id.action_mode_bar_stub); 1907 if (stub != null) { 1908 mPrimaryActionModeView = (ActionBarContextView) stub.inflate(); 1909 mPrimaryActionModePopup = null; 1910 } 1911 } 1912 } 1913 if (mPrimaryActionModeView != null) { 1914 mPrimaryActionModeView.killMode(); 1915 ActionMode mode = new StandaloneActionMode( 1916 mPrimaryActionModeView.getContext(), mPrimaryActionModeView, 1917 callback, mPrimaryActionModePopup == null); 1918 return mode; 1919 } 1920 return null; 1921 } 1922 endOnGoingFadeAnimation()1923 private void endOnGoingFadeAnimation() { 1924 if (mFadeAnim != null) { 1925 mFadeAnim.end(); 1926 } 1927 } 1928 setHandledPrimaryActionMode(ActionMode mode)1929 private void setHandledPrimaryActionMode(ActionMode mode) { 1930 endOnGoingFadeAnimation(); 1931 mPrimaryActionMode = mode; 1932 mPrimaryActionMode.invalidate(); 1933 mPrimaryActionModeView.initForMode(mPrimaryActionMode); 1934 if (mPrimaryActionModePopup != null) { 1935 post(mShowPrimaryActionModePopup); 1936 } else { 1937 if (shouldAnimatePrimaryActionModeView()) { 1938 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f); 1939 mFadeAnim.addListener(new AnimatorListenerAdapter() { 1940 @Override 1941 public void onAnimationStart(Animator animation) { 1942 mPrimaryActionModeView.setVisibility(View.VISIBLE); 1943 } 1944 1945 @Override 1946 public void onAnimationEnd(Animator animation) { 1947 mPrimaryActionModeView.setAlpha(1f); 1948 mFadeAnim = null; 1949 } 1950 }); 1951 mFadeAnim.start(); 1952 } else { 1953 mPrimaryActionModeView.setAlpha(1f); 1954 mPrimaryActionModeView.setVisibility(View.VISIBLE); 1955 } 1956 } 1957 mPrimaryActionModeView.sendAccessibilityEvent( 1958 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED); 1959 } 1960 shouldAnimatePrimaryActionModeView()1961 boolean shouldAnimatePrimaryActionModeView() { 1962 // We only to animate the action mode in if the decor has already been laid out. 1963 // If it hasn't been laid out, it hasn't been drawn to screen yet. 1964 return isLaidOut(); 1965 } 1966 createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)1967 private ActionMode createFloatingActionMode( 1968 View originatingView, ActionMode.Callback2 callback) { 1969 if (mFloatingActionMode != null) { 1970 mFloatingActionMode.finish(); 1971 } 1972 cleanupFloatingActionModeViews(); 1973 mFloatingToolbar = new FloatingToolbar(mWindow); 1974 final FloatingActionMode mode = 1975 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar); 1976 mFloatingActionModeOriginatingView = originatingView; 1977 mFloatingToolbarPreDrawListener = 1978 new ViewTreeObserver.OnPreDrawListener() { 1979 @Override 1980 public boolean onPreDraw() { 1981 mode.updateViewLocationInWindow(); 1982 return true; 1983 } 1984 }; 1985 return mode; 1986 } 1987 setHandledFloatingActionMode(ActionMode mode)1988 private void setHandledFloatingActionMode(ActionMode mode) { 1989 mFloatingActionMode = mode; 1990 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary. 1991 mFloatingActionModeOriginatingView.getViewTreeObserver() 1992 .addOnPreDrawListener(mFloatingToolbarPreDrawListener); 1993 } 1994 1995 /** 1996 * Informs the decor if the caption is attached and visible. 1997 * @param attachedAndVisible true when the decor is visible. 1998 * Note that this will even be called if there is no caption. 1999 **/ enableCaption(boolean attachedAndVisible)2000 void enableCaption(boolean attachedAndVisible) { 2001 if (mHasCaption != attachedAndVisible) { 2002 mHasCaption = attachedAndVisible; 2003 if (getForeground() != null) { 2004 drawableChanged(); 2005 } 2006 } 2007 } 2008 setWindow(PhoneWindow phoneWindow)2009 void setWindow(PhoneWindow phoneWindow) { 2010 mWindow = phoneWindow; 2011 Context context = getContext(); 2012 if (context instanceof DecorContext) { 2013 DecorContext decorContext = (DecorContext) context; 2014 decorContext.setPhoneWindow(mWindow); 2015 } 2016 } 2017 2018 @Override getResources()2019 public Resources getResources() { 2020 // Make sure the Resources object is propogated from the Context since it can be updated in 2021 // the Context object. 2022 return getContext().getResources(); 2023 } 2024 2025 @Override onConfigurationChanged(Configuration newConfig)2026 protected void onConfigurationChanged(Configuration newConfig) { 2027 super.onConfigurationChanged(newConfig); 2028 2029 updateDecorCaptionStatus(newConfig); 2030 2031 updateAvailableWidth(); 2032 initializeElevation(); 2033 } 2034 2035 @Override onMovedToDisplay(int displayId, Configuration config)2036 public void onMovedToDisplay(int displayId, Configuration config) { 2037 super.onMovedToDisplay(displayId, config); 2038 // Have to explicitly update displayId because it may use DecorContext 2039 getContext().updateDisplay(displayId); 2040 } 2041 2042 /** 2043 * Determines if the workspace is entirely covered by the window. 2044 * @return {@code true} when the window is filling the entire screen/workspace. 2045 **/ isFillingScreen(Configuration config)2046 private boolean isFillingScreen(Configuration config) { 2047 final boolean isFullscreen = config.windowConfiguration.getWindowingMode() 2048 == WindowConfiguration.WINDOWING_MODE_FULLSCREEN; 2049 return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility()) 2050 & View.SYSTEM_UI_FLAG_FULLSCREEN)); 2051 } 2052 updateDecorCaptionStatus(Configuration config)2053 private void updateDecorCaptionStatus(Configuration config) { 2054 final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption() 2055 && !isFillingScreen(config); 2056 if (mDecorCaptionView == null && displayWindowDecor) { 2057 // Configuration now requires a caption. 2058 final LayoutInflater inflater = mWindow.getLayoutInflater(); 2059 mDecorCaptionView = createDecorCaptionView(inflater); 2060 if (mDecorCaptionView != null) { 2061 if (mDecorCaptionView.getParent() == null) { 2062 addView(mDecorCaptionView, 0, 2063 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 2064 } 2065 removeView(mContentRoot); 2066 mDecorCaptionView.addView(mContentRoot, 2067 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); 2068 } 2069 } else if (mDecorCaptionView != null) { 2070 // We might have to change the kind of surface before we do anything else. 2071 mDecorCaptionView.onConfigurationChanged(displayWindowDecor); 2072 enableCaption(displayWindowDecor); 2073 } 2074 } 2075 onResourcesLoaded(LayoutInflater inflater, int layoutResource)2076 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) { 2077 if (mBackdropFrameRenderer != null) { 2078 loadBackgroundDrawablesIfNeeded(); 2079 mBackdropFrameRenderer.onResourcesLoaded( 2080 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, 2081 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), 2082 getCurrentColor(mNavigationColorViewState)); 2083 } 2084 2085 mDecorCaptionView = createDecorCaptionView(inflater); 2086 final View root = inflater.inflate(layoutResource, null); 2087 if (mDecorCaptionView != null) { 2088 if (mDecorCaptionView.getParent() == null) { 2089 addView(mDecorCaptionView, 2090 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 2091 } 2092 mDecorCaptionView.addView(root, 2093 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT)); 2094 } else { 2095 2096 // Put it below the color views. 2097 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT)); 2098 } 2099 mContentRoot = (ViewGroup) root; 2100 initializeElevation(); 2101 } 2102 loadBackgroundDrawablesIfNeeded()2103 private void loadBackgroundDrawablesIfNeeded() { 2104 if (mResizingBackgroundDrawable == null) { 2105 mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable, 2106 mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent() 2107 || mWindow.isShowingWallpaper()); 2108 if (mResizingBackgroundDrawable == null) { 2109 // We shouldn't really get here as the background fallback should be always 2110 // available since it is defaulted by the system. 2111 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow); 2112 } 2113 } 2114 if (mCaptionBackgroundDrawable == null) { 2115 mCaptionBackgroundDrawable = getContext().getDrawable( 2116 R.drawable.decor_caption_title_focused); 2117 } 2118 if (mResizingBackgroundDrawable != null) { 2119 mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback(); 2120 mResizingBackgroundDrawable.setCallback(null); 2121 } 2122 } 2123 2124 // Free floating overlapping windows require a caption. createDecorCaptionView(LayoutInflater inflater)2125 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) { 2126 DecorCaptionView decorCaptionView = null; 2127 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) { 2128 View view = getChildAt(i); 2129 if (view instanceof DecorCaptionView) { 2130 // The decor was most likely saved from a relaunch - so reuse it. 2131 decorCaptionView = (DecorCaptionView) view; 2132 removeViewAt(i); 2133 } 2134 } 2135 final WindowManager.LayoutParams attrs = mWindow.getAttributes(); 2136 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION || 2137 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION; 2138 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration; 2139 // Only a non floating application window on one of the allowed workspaces can get a caption 2140 if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) { 2141 // Dependent on the brightness of the used title we either use the 2142 // dark or the light button frame. 2143 if (decorCaptionView == null) { 2144 decorCaptionView = inflateDecorCaptionView(inflater); 2145 } 2146 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/); 2147 } else { 2148 decorCaptionView = null; 2149 } 2150 2151 // Tell the decor if it has a visible caption. 2152 enableCaption(decorCaptionView != null); 2153 return decorCaptionView; 2154 } 2155 inflateDecorCaptionView(LayoutInflater inflater)2156 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) { 2157 final Context context = getContext(); 2158 // We make a copy of the inflater, so it has the right context associated with it. 2159 inflater = inflater.from(context); 2160 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption, 2161 null); 2162 setDecorCaptionShade(context, view); 2163 return view; 2164 } 2165 setDecorCaptionShade(Context context, DecorCaptionView view)2166 private void setDecorCaptionShade(Context context, DecorCaptionView view) { 2167 final int shade = mWindow.getDecorCaptionShade(); 2168 switch (shade) { 2169 case DECOR_CAPTION_SHADE_LIGHT: 2170 setLightDecorCaptionShade(view); 2171 break; 2172 case DECOR_CAPTION_SHADE_DARK: 2173 setDarkDecorCaptionShade(view); 2174 break; 2175 default: { 2176 TypedValue value = new TypedValue(); 2177 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true); 2178 // We invert the shade depending on brightness of the theme. Dark shade for light 2179 // theme and vice versa. Thanks to this the buttons should be visible on the 2180 // background. 2181 if (Color.luminance(value.data) < 0.5) { 2182 setLightDecorCaptionShade(view); 2183 } else { 2184 setDarkDecorCaptionShade(view); 2185 } 2186 break; 2187 } 2188 } 2189 } 2190 updateDecorCaptionShade()2191 void updateDecorCaptionShade() { 2192 if (mDecorCaptionView != null) { 2193 setDecorCaptionShade(getContext(), mDecorCaptionView); 2194 } 2195 } 2196 setLightDecorCaptionShade(DecorCaptionView view)2197 private void setLightDecorCaptionShade(DecorCaptionView view) { 2198 view.findViewById(R.id.maximize_window).setBackgroundResource( 2199 R.drawable.decor_maximize_button_light); 2200 view.findViewById(R.id.close_window).setBackgroundResource( 2201 R.drawable.decor_close_button_light); 2202 } 2203 setDarkDecorCaptionShade(DecorCaptionView view)2204 private void setDarkDecorCaptionShade(DecorCaptionView view) { 2205 view.findViewById(R.id.maximize_window).setBackgroundResource( 2206 R.drawable.decor_maximize_button_dark); 2207 view.findViewById(R.id.close_window).setBackgroundResource( 2208 R.drawable.decor_close_button_dark); 2209 } 2210 2211 /** 2212 * Returns the color used to fill areas the app has not rendered content to yet when the 2213 * user is resizing the window of an activity in multi-window mode. 2214 */ getResizingBackgroundDrawable(@ullable Drawable backgroundDrawable, @Nullable Drawable fallbackDrawable, boolean windowTranslucent)2215 public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable, 2216 @Nullable Drawable fallbackDrawable, boolean windowTranslucent) { 2217 if (backgroundDrawable != null) { 2218 return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent); 2219 } 2220 2221 if (fallbackDrawable != null) { 2222 return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent); 2223 } 2224 return new ColorDrawable(Color.BLACK); 2225 } 2226 2227 /** 2228 * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the 2229 * window is not translucent. 2230 */ enforceNonTranslucentBackground(Drawable drawable, boolean windowTranslucent)2231 private static Drawable enforceNonTranslucentBackground(Drawable drawable, 2232 boolean windowTranslucent) { 2233 if (!windowTranslucent && drawable instanceof ColorDrawable) { 2234 ColorDrawable colorDrawable = (ColorDrawable) drawable; 2235 int color = colorDrawable.getColor(); 2236 if (Color.alpha(color) != 255) { 2237 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable() 2238 .mutate(); 2239 copy.setColor( 2240 Color.argb(255, Color.red(color), Color.green(color), Color.blue(color))); 2241 return copy; 2242 } 2243 } 2244 return drawable; 2245 } 2246 clearContentView()2247 void clearContentView() { 2248 if (mDecorCaptionView != null) { 2249 mDecorCaptionView.removeContentView(); 2250 } else { 2251 // This window doesn't have caption, so we need to remove everything except our views 2252 // we might have added. 2253 for (int i = getChildCount() - 1; i >= 0; i--) { 2254 View v = getChildAt(i); 2255 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view 2256 && v != mStatusGuard) { 2257 removeViewAt(i); 2258 } 2259 } 2260 } 2261 } 2262 2263 @Override onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets)2264 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, 2265 Rect stableInsets) { 2266 if (mBackdropFrameRenderer != null) { 2267 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets); 2268 } 2269 } 2270 2271 @Override onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode)2272 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, 2273 Rect stableInsets, int resizeMode) { 2274 if (mWindow.isDestroyed()) { 2275 // If the owner's window is gone, we should not be able to come here anymore. 2276 releaseThreadedRenderer(); 2277 return; 2278 } 2279 if (mBackdropFrameRenderer != null) { 2280 return; 2281 } 2282 final ThreadedRenderer renderer = getThreadedRenderer(); 2283 if (renderer != null) { 2284 loadBackgroundDrawablesIfNeeded(); 2285 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer, 2286 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable, 2287 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState), 2288 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets, 2289 stableInsets); 2290 2291 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time. 2292 // If we want to get the shadow shown while resizing, we would need to elevate a new 2293 // element which owns the caption and has the elevation. 2294 updateElevation(); 2295 2296 updateColorViews(null /* insets */, false); 2297 } 2298 mResizeMode = resizeMode; 2299 getViewRootImpl().requestInvalidateRootRenderNode(); 2300 } 2301 2302 @Override onWindowDragResizeEnd()2303 public void onWindowDragResizeEnd() { 2304 releaseThreadedRenderer(); 2305 updateColorViews(null /* insets */, false); 2306 mResizeMode = RESIZE_MODE_INVALID; 2307 getViewRootImpl().requestInvalidateRootRenderNode(); 2308 } 2309 2310 @Override onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY)2311 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) { 2312 if (mBackdropFrameRenderer == null) { 2313 return false; 2314 } 2315 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY); 2316 } 2317 2318 @Override onRequestDraw(boolean reportNextDraw)2319 public void onRequestDraw(boolean reportNextDraw) { 2320 if (mBackdropFrameRenderer != null) { 2321 mBackdropFrameRenderer.onRequestDraw(reportNextDraw); 2322 } else if (reportNextDraw) { 2323 // If render thread is gone, just report immediately. 2324 if (isAttachedToWindow()) { 2325 getViewRootImpl().reportDrawFinish(); 2326 } 2327 } 2328 } 2329 2330 @Override onPostDraw(RecordingCanvas canvas)2331 public void onPostDraw(RecordingCanvas canvas) { 2332 drawResizingShadowIfNeeded(canvas); 2333 drawLegacyNavigationBarBackground(canvas); 2334 } 2335 initResizingPaints()2336 private void initResizingPaints() { 2337 final int startColor = mContext.getResources().getColor( 2338 R.color.resize_shadow_start_color, null); 2339 final int endColor = mContext.getResources().getColor( 2340 R.color.resize_shadow_end_color, null); 2341 final int middleColor = (startColor + endColor) / 2; 2342 mHorizontalResizeShadowPaint.setShader(new LinearGradient( 2343 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor }, 2344 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP)); 2345 mVerticalResizeShadowPaint.setShader(new LinearGradient( 2346 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor }, 2347 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP)); 2348 } 2349 drawResizingShadowIfNeeded(RecordingCanvas canvas)2350 private void drawResizingShadowIfNeeded(RecordingCanvas canvas) { 2351 if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating 2352 || mWindow.isTranslucent() 2353 || mWindow.isShowingWallpaper()) { 2354 return; 2355 } 2356 canvas.save(); 2357 canvas.translate(0, getHeight() - mFrameOffsets.bottom); 2358 canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint); 2359 canvas.restore(); 2360 canvas.save(); 2361 canvas.translate(getWidth() - mFrameOffsets.right, 0); 2362 canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint); 2363 canvas.restore(); 2364 } 2365 drawLegacyNavigationBarBackground(RecordingCanvas canvas)2366 private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) { 2367 if (!mDrawLegacyNavigationBarBackground) { 2368 return; 2369 } 2370 View v = mNavigationColorViewState.view; 2371 if (v == null) { 2372 return; 2373 } 2374 canvas.drawRect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom(), 2375 mLegacyNavigationBarBackgroundPaint); 2376 } 2377 2378 /** Release the renderer thread which is usually done when the user stops resizing. */ releaseThreadedRenderer()2379 private void releaseThreadedRenderer() { 2380 if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) { 2381 mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb); 2382 mLastBackgroundDrawableCb = null; 2383 } 2384 2385 if (mBackdropFrameRenderer != null) { 2386 mBackdropFrameRenderer.releaseRenderer(); 2387 mBackdropFrameRenderer = null; 2388 // Bring the shadow back. 2389 updateElevation(); 2390 } 2391 } 2392 isResizing()2393 private boolean isResizing() { 2394 return mBackdropFrameRenderer != null; 2395 } 2396 2397 /** 2398 * The elevation gets set for the first time and the framework needs to be informed that 2399 * the surface layer gets created with the shadow size in mind. 2400 */ initializeElevation()2401 private void initializeElevation() { 2402 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed. 2403 mAllowUpdateElevation = false; 2404 updateElevation(); 2405 } 2406 updateElevation()2407 private void updateElevation() { 2408 float elevation = 0; 2409 final boolean wasAdjustedForStack = mElevationAdjustedForStack; 2410 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null) 2411 // since the shadow is bound to the content size and not the target size. 2412 final int windowingMode = 2413 getResources().getConfiguration().windowConfiguration.getWindowingMode(); 2414 if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) { 2415 elevation = hasWindowFocus() ? 2416 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP; 2417 // Add a maximum shadow height value to the top level view. 2418 // Note that pinned stack doesn't have focus 2419 // so maximum shadow height adjustment isn't needed. 2420 // TODO(skuhne): Remove this if clause once b/22668382 got fixed. 2421 if (!mAllowUpdateElevation) { 2422 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP; 2423 } 2424 // Convert the DP elevation into physical pixels. 2425 elevation = dipToPx(elevation); 2426 mElevationAdjustedForStack = true; 2427 } else if (windowingMode == WINDOWING_MODE_PINNED) { 2428 elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP); 2429 mElevationAdjustedForStack = true; 2430 } else { 2431 mElevationAdjustedForStack = false; 2432 } 2433 2434 // Don't change the elevation if we didn't previously adjust it for the stack it was in 2435 // or it didn't change. 2436 if ((wasAdjustedForStack || mElevationAdjustedForStack) 2437 && getElevation() != elevation) { 2438 if (!isResizing()) { 2439 mWindow.setElevation(elevation); 2440 } else { 2441 // Just suppress the shadow when resizing, don't adjust surface insets because it'll 2442 // cause a flicker when drag resize for freeform window starts. #onContentDrawn() 2443 // will compensate the offset when passing to BackdropFrameRenderer. 2444 setElevation(elevation); 2445 } 2446 } 2447 } 2448 isShowingCaption()2449 boolean isShowingCaption() { 2450 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing(); 2451 } 2452 getCaptionHeight()2453 int getCaptionHeight() { 2454 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0; 2455 } 2456 2457 /** 2458 * Converts a DIP measure into physical pixels. 2459 * @param dip The dip value. 2460 * @return Returns the number of pixels. 2461 */ dipToPx(float dip)2462 private float dipToPx(float dip) { 2463 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip, 2464 getResources().getDisplayMetrics()); 2465 } 2466 2467 /** 2468 * Provide an override of the caption background drawable. 2469 */ setUserCaptionBackgroundDrawable(Drawable drawable)2470 void setUserCaptionBackgroundDrawable(Drawable drawable) { 2471 mUserCaptionBackgroundDrawable = drawable; 2472 if (mBackdropFrameRenderer != null) { 2473 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable); 2474 } 2475 } 2476 getTitleSuffix(WindowManager.LayoutParams params)2477 private static String getTitleSuffix(WindowManager.LayoutParams params) { 2478 if (params == null) { 2479 return ""; 2480 } 2481 final String[] split = params.getTitle().toString().split("\\."); 2482 if (split.length > 0) { 2483 return split[split.length - 1]; 2484 } else { 2485 return ""; 2486 } 2487 } 2488 updateLogTag(WindowManager.LayoutParams params)2489 void updateLogTag(WindowManager.LayoutParams params) { 2490 mLogTag = TAG + "[" + getTitleSuffix(params) + "]"; 2491 } 2492 updateAvailableWidth()2493 private void updateAvailableWidth() { 2494 Resources res = getResources(); 2495 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 2496 res.getConfiguration().screenWidthDp, res.getDisplayMetrics()); 2497 } 2498 2499 /** 2500 * @hide 2501 */ 2502 @Override requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId)2503 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) { 2504 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false); 2505 final Menu menu = st != null ? st.menu : null; 2506 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) { 2507 mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId); 2508 } 2509 } 2510 2511 @Override dispatchPointerCaptureChanged(boolean hasCapture)2512 public void dispatchPointerCaptureChanged(boolean hasCapture) { 2513 super.dispatchPointerCaptureChanged(hasCapture); 2514 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) { 2515 mWindow.getCallback().onPointerCaptureChanged(hasCapture); 2516 } 2517 } 2518 2519 @Override getAccessibilityViewId()2520 public int getAccessibilityViewId() { 2521 return AccessibilityNodeInfo.ROOT_ITEM_ID; 2522 } 2523 2524 @Override toString()2525 public String toString() { 2526 return "DecorView@" + Integer.toHexString(this.hashCode()) + "[" 2527 + getTitleSuffix(mWindow.getAttributes()) + "]"; 2528 } 2529 2530 private static class ColorViewState { 2531 View view = null; 2532 int targetVisibility = View.INVISIBLE; 2533 boolean present = false; 2534 boolean visible; 2535 int color; 2536 2537 final ColorViewAttributes attributes; 2538 ColorViewState(ColorViewAttributes attributes)2539 ColorViewState(ColorViewAttributes attributes) { 2540 this.attributes = attributes; 2541 } 2542 } 2543 2544 public static class ColorViewAttributes { 2545 2546 final int id; 2547 final int systemUiHideFlag; 2548 final int translucentFlag; 2549 final int verticalGravity; 2550 final int horizontalGravity; 2551 final int seascapeGravity; 2552 final String transitionName; 2553 final int hideWindowFlag; 2554 ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity, int horizontalGravity, int seascapeGravity, String transitionName, int id, int hideWindowFlag)2555 private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity, 2556 int horizontalGravity, int seascapeGravity, String transitionName, int id, 2557 int hideWindowFlag) { 2558 this.id = id; 2559 this.systemUiHideFlag = systemUiHideFlag; 2560 this.translucentFlag = translucentFlag; 2561 this.verticalGravity = verticalGravity; 2562 this.horizontalGravity = horizontalGravity; 2563 this.seascapeGravity = seascapeGravity; 2564 this.transitionName = transitionName; 2565 this.hideWindowFlag = hideWindowFlag; 2566 } 2567 isPresent(int sysUiVis, int windowFlags, boolean force)2568 public boolean isPresent(int sysUiVis, int windowFlags, boolean force) { 2569 return (sysUiVis & systemUiHideFlag) == 0 2570 && (windowFlags & hideWindowFlag) == 0 2571 && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 2572 || force); 2573 } 2574 isVisible(boolean present, int color, int windowFlags, boolean force)2575 public boolean isVisible(boolean present, int color, int windowFlags, boolean force) { 2576 return present 2577 && (color & Color.BLACK) != 0 2578 && ((windowFlags & translucentFlag) == 0 || force); 2579 } 2580 isVisible(int sysUiVis, int color, int windowFlags, boolean force)2581 public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) { 2582 final boolean present = isPresent(sysUiVis, windowFlags, force); 2583 return isVisible(present, color, windowFlags, force); 2584 } 2585 } 2586 2587 /** 2588 * Clears out internal references when the action mode is destroyed. 2589 */ 2590 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 { 2591 private final ActionMode.Callback mWrapped; 2592 ActionModeCallback2Wrapper(ActionMode.Callback wrapped)2593 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) { 2594 mWrapped = wrapped; 2595 } 2596 onCreateActionMode(ActionMode mode, Menu menu)2597 public boolean onCreateActionMode(ActionMode mode, Menu menu) { 2598 return mWrapped.onCreateActionMode(mode, menu); 2599 } 2600 onPrepareActionMode(ActionMode mode, Menu menu)2601 public boolean onPrepareActionMode(ActionMode mode, Menu menu) { 2602 requestFitSystemWindows(); 2603 return mWrapped.onPrepareActionMode(mode, menu); 2604 } 2605 onActionItemClicked(ActionMode mode, MenuItem item)2606 public boolean onActionItemClicked(ActionMode mode, MenuItem item) { 2607 return mWrapped.onActionItemClicked(mode, item); 2608 } 2609 onDestroyActionMode(ActionMode mode)2610 public void onDestroyActionMode(ActionMode mode) { 2611 mWrapped.onDestroyActionMode(mode); 2612 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion 2613 >= M; 2614 final boolean isPrimary; 2615 final boolean isFloating; 2616 if (isMncApp) { 2617 isPrimary = mode == mPrimaryActionMode; 2618 isFloating = mode == mFloatingActionMode; 2619 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) { 2620 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; " 2621 + mode + " was not the current primary action mode! Expected " 2622 + mPrimaryActionMode); 2623 } 2624 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) { 2625 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; " 2626 + mode + " was not the current floating action mode! Expected " 2627 + mFloatingActionMode); 2628 } 2629 } else { 2630 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY; 2631 isFloating = mode.getType() == ActionMode.TYPE_FLOATING; 2632 } 2633 if (isPrimary) { 2634 if (mPrimaryActionModePopup != null) { 2635 removeCallbacks(mShowPrimaryActionModePopup); 2636 } 2637 if (mPrimaryActionModeView != null) { 2638 endOnGoingFadeAnimation(); 2639 // Store action mode view reference, so we can access it safely when animation 2640 // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView, 2641 // so no need to store reference to it in separate variable. 2642 final ActionBarContextView lastActionModeView = mPrimaryActionModeView; 2643 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 2644 1f, 0f); 2645 mFadeAnim.addListener(new Animator.AnimatorListener() { 2646 2647 @Override 2648 public void onAnimationStart(Animator animation) { 2649 2650 } 2651 2652 @Override 2653 public void onAnimationEnd(Animator animation) { 2654 // If mPrimaryActionModeView has changed - it means that we've 2655 // cleared the content while preserving decor view. We don't 2656 // want to change the state of new instances accidentally here. 2657 if (lastActionModeView == mPrimaryActionModeView) { 2658 lastActionModeView.setVisibility(GONE); 2659 if (mPrimaryActionModePopup != null) { 2660 mPrimaryActionModePopup.dismiss(); 2661 } 2662 lastActionModeView.killMode(); 2663 mFadeAnim = null; 2664 requestApplyInsets(); 2665 } 2666 } 2667 2668 @Override 2669 public void onAnimationCancel(Animator animation) { 2670 2671 } 2672 2673 @Override 2674 public void onAnimationRepeat(Animator animation) { 2675 2676 } 2677 }); 2678 mFadeAnim.start(); 2679 } 2680 2681 mPrimaryActionMode = null; 2682 } else if (isFloating) { 2683 cleanupFloatingActionModeViews(); 2684 mFloatingActionMode = null; 2685 } 2686 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) { 2687 try { 2688 mWindow.getCallback().onActionModeFinished(mode); 2689 } catch (AbstractMethodError ame) { 2690 // Older apps might not implement this callback method. 2691 } 2692 } 2693 requestFitSystemWindows(); 2694 } 2695 2696 @Override onGetContentRect(ActionMode mode, View view, Rect outRect)2697 public void onGetContentRect(ActionMode mode, View view, Rect outRect) { 2698 if (mWrapped instanceof ActionMode.Callback2) { 2699 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect); 2700 } else { 2701 super.onGetContentRect(mode, view, outRect); 2702 } 2703 } 2704 } 2705 } 2706