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