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