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