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