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