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