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