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