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