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