DecorView.java revision c9c83a9ccbf8040c4130cb896e40019250d9d636
1/* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License 15 */ 16 17package com.android.internal.policy; 18 19import android.graphics.Outline; 20import android.view.ViewOutlineProvider; 21import android.view.accessibility.AccessibilityNodeInfo; 22import com.android.internal.R; 23import com.android.internal.policy.PhoneWindow.PanelFeatureState; 24import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback; 25import com.android.internal.view.FloatingActionMode; 26import com.android.internal.view.RootViewSurfaceTaker; 27import com.android.internal.view.StandaloneActionMode; 28import com.android.internal.view.menu.ContextMenuBuilder; 29import com.android.internal.view.menu.MenuHelper; 30import com.android.internal.widget.ActionBarContextView; 31import com.android.internal.widget.BackgroundFallback; 32import com.android.internal.widget.DecorCaptionView; 33import com.android.internal.widget.FloatingToolbar; 34 35import java.util.List; 36 37import android.animation.Animator; 38import android.animation.AnimatorListenerAdapter; 39import android.animation.ObjectAnimator; 40import android.app.ActivityManager; 41import android.content.Context; 42import android.content.res.Configuration; 43import android.content.res.Resources; 44import android.graphics.Canvas; 45import android.graphics.Color; 46import android.graphics.LinearGradient; 47import android.graphics.Paint; 48import android.graphics.PixelFormat; 49import android.graphics.Rect; 50import android.graphics.Region; 51import android.graphics.Shader; 52import android.graphics.drawable.ColorDrawable; 53import android.graphics.drawable.Drawable; 54import android.os.RemoteException; 55import android.util.DisplayMetrics; 56import android.util.Log; 57import android.util.TypedValue; 58import android.view.ActionMode; 59import android.view.ContextThemeWrapper; 60import android.view.DisplayListCanvas; 61import android.view.Gravity; 62import android.view.InputQueue; 63import android.view.KeyEvent; 64import android.view.KeyboardShortcutGroup; 65import android.view.LayoutInflater; 66import android.view.Menu; 67import android.view.MenuItem; 68import android.view.MotionEvent; 69import android.view.ThreadedRenderer; 70import android.view.View; 71import android.view.ViewGroup; 72import android.view.ViewStub; 73import android.view.ViewTreeObserver; 74import android.view.Window; 75import android.view.WindowCallbacks; 76import android.view.WindowInsets; 77import android.view.WindowManager; 78import android.view.accessibility.AccessibilityEvent; 79import android.view.accessibility.AccessibilityManager; 80import android.view.animation.AnimationUtils; 81import android.view.animation.Interpolator; 82import android.widget.FrameLayout; 83import android.widget.PopupWindow; 84 85import static android.app.ActivityManager.StackId; 86import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID; 87import static android.app.ActivityManager.StackId.FREEFORM_WORKSPACE_STACK_ID; 88import static android.app.ActivityManager.StackId.PINNED_STACK_ID; 89import static android.app.ActivityManager.StackId.INVALID_STACK_ID; 90import static android.content.res.Configuration.ORIENTATION_PORTRAIT; 91import static android.os.Build.VERSION_CODES.M; 92import static android.os.Build.VERSION_CODES.N; 93import static android.view.View.MeasureSpec.AT_MOST; 94import static android.view.View.MeasureSpec.EXACTLY; 95import static android.view.View.MeasureSpec.getMode; 96import static android.view.ViewGroup.LayoutParams.MATCH_PARENT; 97import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT; 98import static android.view.Window.DECOR_CAPTION_SHADE_DARK; 99import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT; 100import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS; 101import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN; 102import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR; 103import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_OVERSCAN; 104import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN; 105import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION; 106import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS; 107import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION; 108import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION; 109import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION; 110import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL; 111 112/** @hide */ 113public 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 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 289 void setBackgroundFallback(int resId) { 290 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null); 291 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback()); 292 } 293 294 @Override 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 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 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 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 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 784 public void draw(Canvas canvas) { 785 super.draw(canvas); 786 787 if (mMenuBackground != null) { 788 mMenuBackground.draw(canvas); 789 } 790 } 791 792 @Override 793 public boolean showContextMenuForChild(View originalView) { 794 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN); 795 } 796 797 @Override 798 public boolean showContextMenuForChild(View originalView, float x, float y) { 799 return showContextMenuForChildInternal(originalView, x, y); 800 } 801 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 840 public ActionMode startActionModeForChild(View originalView, 841 ActionMode.Callback callback) { 842 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY); 843 } 844 845 @Override 846 public ActionMode startActionModeForChild( 847 View child, ActionMode.Callback callback, int type) { 848 return startActionMode(child, callback, type); 849 } 850 851 @Override 852 public ActionMode startActionMode(ActionMode.Callback callback) { 853 return startActionMode(callback, ActionMode.TYPE_PRIMARY); 854 } 855 856 @Override 857 public ActionMode startActionMode(ActionMode.Callback callback, int type) { 858 return startActionMode(this, callback, type); 859 } 860 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 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 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 933 void startChanging() { 934 mChanging = true; 935 } 936 937 void finishChanging() { 938 mChanging = false; 939 drawableChanged(); 940 } 941 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 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 975 public void onWindowSystemUiVisibilityChanged(int visible) { 976 updateColorViews(null /* insets */, true /* animate */); 977 } 978 979 @Override 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 1012 public boolean isTransitionGroup() { 1013 return false; 1014 } 1015 1016 public static int getColorViewTopInset(int stableTop, int systemTop) { 1017 return Math.min(stableTop, systemTop); 1018 } 1019 1020 public static int getColorViewBottomInset(int stableBottom, int systemBottom) { 1021 return Math.min(stableBottom, systemBottom); 1022 } 1023 1024 public static int getColorViewRightInset(int stableRight, int systemRight) { 1025 return Math.min(stableRight, systemRight); 1026 } 1027 1028 public static int getColorViewLeftInset(int stableLeft, int systemLeft) { 1029 return Math.min(stableLeft, systemLeft); 1030 } 1031 1032 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) { 1033 return bottomInset == 0 && rightInset > 0; 1034 } 1035 1036 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) { 1037 return bottomInset == 0 && leftInset > 0; 1038 } 1039 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 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 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 1178 private int calculateStatusBarColor() { 1179 return calculateStatusBarColor(mWindow.getAttributes().flags, 1180 mSemiTransparentStatusBarColor, mWindow.mStatusBarColor); 1181 } 1182 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 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 */ 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 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(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