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