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