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