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