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