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