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