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