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