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