2 * Copyright (C) 2015 The Android Open Source Project
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
8 * http://www.apache.org/licenses/LICENSE-2.0
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
17 package com.android.internal.policy;
19 import android.app.WindowConfiguration;
20 import android.graphics.Outline;
21 import android.graphics.drawable.InsetDrawable;
22 import android.graphics.drawable.LayerDrawable;
23 import android.util.Pair;
24 import android.view.ViewOutlineProvider;
25 import android.view.accessibility.AccessibilityNodeInfo;
26 import com.android.internal.R;
27 import com.android.internal.policy.PhoneWindow.PanelFeatureState;
28 import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
29 import com.android.internal.view.FloatingActionMode;
30 import com.android.internal.view.RootViewSurfaceTaker;
31 import com.android.internal.view.StandaloneActionMode;
32 import com.android.internal.view.menu.ContextMenuBuilder;
33 import com.android.internal.view.menu.MenuHelper;
34 import com.android.internal.widget.ActionBarContextView;
35 import com.android.internal.widget.BackgroundFallback;
36 import com.android.internal.widget.DecorCaptionView;
37 import com.android.internal.widget.FloatingToolbar;
39 import java.util.List;
41 import android.animation.Animator;
42 import android.animation.AnimatorListenerAdapter;
43 import android.animation.ObjectAnimator;
44 import android.content.Context;
45 import android.content.res.Configuration;
46 import android.content.res.Resources;
47 import android.graphics.Canvas;
48 import android.graphics.Color;
49 import android.graphics.LinearGradient;
50 import android.graphics.Paint;
51 import android.graphics.PixelFormat;
52 import android.graphics.Rect;
53 import android.graphics.Region;
54 import android.graphics.Shader;
55 import android.graphics.drawable.ColorDrawable;
56 import android.graphics.drawable.Drawable;
57 import android.util.DisplayMetrics;
58 import android.util.Log;
59 import android.util.TypedValue;
60 import android.view.ActionMode;
61 import android.view.ContextThemeWrapper;
62 import android.view.DisplayListCanvas;
63 import android.view.Gravity;
64 import android.view.InputQueue;
65 import android.view.KeyEvent;
66 import android.view.KeyboardShortcutGroup;
67 import android.view.LayoutInflater;
68 import android.view.Menu;
69 import android.view.MenuItem;
70 import android.view.MotionEvent;
71 import android.view.ThreadedRenderer;
72 import android.view.View;
73 import android.view.ViewGroup;
74 import android.view.ViewStub;
75 import android.view.ViewTreeObserver;
76 import android.view.Window;
77 import android.view.WindowCallbacks;
78 import android.view.WindowInsets;
79 import android.view.WindowManager;
80 import android.view.accessibility.AccessibilityEvent;
81 import android.view.accessibility.AccessibilityManager;
82 import android.view.animation.AnimationUtils;
83 import android.view.animation.Interpolator;
84 import android.widget.FrameLayout;
85 import android.widget.PopupWindow;
87 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
88 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
89 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
90 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
91 import static android.os.Build.VERSION_CODES.M;
92 import static android.os.Build.VERSION_CODES.N;
93 import static android.view.View.MeasureSpec.AT_MOST;
94 import static android.view.View.MeasureSpec.EXACTLY;
95 import static android.view.View.MeasureSpec.getMode;
96 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
97 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
98 import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
99 import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
100 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
101 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
102 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
103 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
104 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
105 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
106 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
107 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
108 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
109 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
112 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
113 private static final String TAG = "DecorView";
115 private static final boolean DEBUG_MEASURE = false;
117 private static final boolean SWEEP_OPEN_MENU = false;
119 // The height of a window which has focus in DIP.
120 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
121 // The height of a window which has not in DIP.
122 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
124 public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
125 new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
126 Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
127 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
128 com.android.internal.R.id.statusBarBackground,
131 public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
132 new ColorViewAttributes(
133 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
134 Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
135 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
136 com.android.internal.R.id.navigationBarBackground,
137 0 /* hideWindowFlag */);
139 // This is used to workaround an issue where the PiP shadow can be transparent if the window
140 // background is transparent
141 private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
143 public void getOutline(View view, Outline outline) {
144 outline.setRect(0, 0, view.getWidth(), view.getHeight());
145 outline.setAlpha(1f);
149 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
150 // size calculation takes the shadow size into account. We set the elevation currently
151 // to max until the first layout command has been executed.
152 private boolean mAllowUpdateElevation = false;
154 private boolean mElevationAdjustedForStack = false;
156 // Keeps track of the picture-in-picture mode for the view shadow
157 private boolean mIsInPictureInPictureMode;
159 // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER
160 private ViewOutlineProvider mLastOutlineProvider;
162 int mDefaultOpacity = PixelFormat.OPAQUE;
164 /** The feature ID of the panel, or -1 if this is the application's DecorView */
165 private final int mFeatureId;
167 private final Rect mDrawingBounds = new Rect();
169 private final Rect mBackgroundPadding = new Rect();
171 private final Rect mFramePadding = new Rect();
173 private final Rect mFrameOffsets = new Rect();
175 private boolean mHasCaption = false;
177 private boolean mChanging;
179 private Drawable mMenuBackground;
180 private boolean mWatchingForMenu;
183 ActionMode mPrimaryActionMode;
184 private ActionMode mFloatingActionMode;
185 private ActionBarContextView mPrimaryActionModeView;
186 private PopupWindow mPrimaryActionModePopup;
187 private Runnable mShowPrimaryActionModePopup;
188 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
189 private View mFloatingActionModeOriginatingView;
190 private FloatingToolbar mFloatingToolbar;
191 private ObjectAnimator mFadeAnim;
193 // View added at runtime to draw under the status bar area
194 private View mStatusGuard;
196 private final ColorViewState mStatusColorViewState =
197 new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
198 private final ColorViewState mNavigationColorViewState =
199 new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
201 private final Interpolator mShowInterpolator;
202 private final Interpolator mHideInterpolator;
203 private final int mBarEnterExitDuration;
204 final boolean mForceWindowDrawsStatusBarBackground;
205 private final int mSemiTransparentStatusBarColor;
207 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
209 private int mLastTopInset = 0;
210 private int mLastBottomInset = 0;
211 private int mLastRightInset = 0;
212 private int mLastLeftInset = 0;
213 private boolean mLastHasTopStableInset = false;
214 private boolean mLastHasBottomStableInset = false;
215 private boolean mLastHasRightStableInset = false;
216 private boolean mLastHasLeftStableInset = false;
217 private int mLastWindowFlags = 0;
218 private boolean mLastShouldAlwaysConsumeNavBar = false;
220 private int mRootScrollY = 0;
222 private PhoneWindow mWindow;
224 ViewGroup mContentRoot;
226 private Rect mTempRect;
227 private Rect mOutsets = new Rect();
229 // This is the caption view for the window, containing the caption and window control
230 // buttons. The visibility of this decor depends on the workspace and the window type.
231 // If the window type does not require such a view, this member might be null.
232 DecorCaptionView mDecorCaptionView;
234 private boolean mWindowResizeCallbacksAdded = false;
235 private Drawable.Callback mLastBackgroundDrawableCb = null;
236 private BackdropFrameRenderer mBackdropFrameRenderer = null;
237 private Drawable mResizingBackgroundDrawable;
238 private Drawable mCaptionBackgroundDrawable;
239 private Drawable mUserCaptionBackgroundDrawable;
241 private float mAvailableWidth;
243 String mLogTag = TAG;
244 private final Rect mFloatingInsets = new Rect();
245 private boolean mApplyFloatingVerticalInsets = false;
246 private boolean mApplyFloatingHorizontalInsets = false;
248 private int mResizeMode = RESIZE_MODE_INVALID;
249 private final int mResizeShadowSize;
250 private final Paint mVerticalResizeShadowPaint = new Paint();
251 private final Paint mHorizontalResizeShadowPaint = new Paint();
253 DecorView(Context context, int featureId, PhoneWindow window,
254 WindowManager.LayoutParams params) {
256 mFeatureId = featureId;
258 mShowInterpolator = AnimationUtils.loadInterpolator(context,
259 android.R.interpolator.linear_out_slow_in);
260 mHideInterpolator = AnimationUtils.loadInterpolator(context,
261 android.R.interpolator.fast_out_linear_in);
263 mBarEnterExitDuration = context.getResources().getInteger(
264 R.integer.dock_enter_exit_duration);
265 mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean(
266 R.bool.config_forceWindowDrawsStatusBarBackground)
267 && context.getApplicationInfo().targetSdkVersion >= N;
268 mSemiTransparentStatusBarColor = context.getResources().getColor(
269 R.color.system_bar_background_semi_transparent, null /* theme */);
271 updateAvailableWidth();
275 updateLogTag(params);
277 mResizeShadowSize = context.getResources().getDimensionPixelSize(
278 R.dimen.resize_shadow_size);
279 initResizingPaints();
282 void setBackgroundFallback(int resId) {
283 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
284 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
288 public boolean gatherTransparentRegion(Region region) {
289 boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
290 boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
291 boolean decorOpaque = super.gatherTransparentRegion(region);
293 // combine bools after computation, so each method above always executes
294 return statusOpaque || navOpaque || decorOpaque;
297 boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
298 if (colorViewState.view != null && colorViewState.visible && isResizing()) {
299 // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
300 // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
301 // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
302 return colorViewState.view.gatherTransparentRegion(region);
304 return false; // no opaque area added
308 public void onDraw(Canvas c) {
311 mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
312 mStatusColorViewState.view, mNavigationColorViewState.view);
316 public boolean dispatchKeyEvent(KeyEvent event) {
317 final int keyCode = event.getKeyCode();
318 final int action = event.getAction();
319 final boolean isDown = action == KeyEvent.ACTION_DOWN;
321 if (isDown && (event.getRepeatCount() == 0)) {
322 // First handle chording of panel key: if a panel key is held
323 // but not released, try to execute a shortcut in it.
324 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
325 boolean handled = dispatchKeyShortcutEvent(event);
331 // If a panel is open, perform a shortcut on it without the
333 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
334 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
340 if (!mWindow.isDestroyed()) {
341 final Window.Callback cb = mWindow.getCallback();
342 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
343 : super.dispatchKeyEvent(event);
349 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
350 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
354 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
355 // If the panel is already prepared, then perform the shortcut using it.
357 if (mWindow.mPreparedPanel != null) {
358 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
359 Menu.FLAG_PERFORM_NO_CLOSE);
361 if (mWindow.mPreparedPanel != null) {
362 mWindow.mPreparedPanel.isHandled = true;
368 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
369 final Window.Callback cb = mWindow.getCallback();
370 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
371 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
376 // If the panel is not prepared, then we may be trying to handle a shortcut key
377 // combination such as Control+C. Temporarily prepare the panel then mark it
378 // unprepared again when finished to ensure that the panel will again be prepared
379 // the next time it is shown for real.
380 PhoneWindow.PanelFeatureState st =
381 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
382 if (st != null && mWindow.mPreparedPanel == null) {
383 mWindow.preparePanel(st, ev);
384 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
385 Menu.FLAG_PERFORM_NO_CLOSE);
386 st.isPrepared = false;
395 public boolean dispatchTouchEvent(MotionEvent ev) {
396 final Window.Callback cb = mWindow.getCallback();
397 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
398 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
402 public boolean dispatchTrackballEvent(MotionEvent ev) {
403 final Window.Callback cb = mWindow.getCallback();
404 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
405 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
409 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
410 final Window.Callback cb = mWindow.getCallback();
411 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
412 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
415 public boolean superDispatchKeyEvent(KeyEvent event) {
416 // Give priority to closing action modes if applicable.
417 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
418 final int action = event.getAction();
419 // Back cancels action modes first.
420 if (mPrimaryActionMode != null) {
421 if (action == KeyEvent.ACTION_UP) {
422 mPrimaryActionMode.finish();
428 if (super.dispatchKeyEvent(event)) {
432 return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
435 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
436 return super.dispatchKeyShortcutEvent(event);
439 public boolean superDispatchTouchEvent(MotionEvent event) {
440 return super.dispatchTouchEvent(event);
443 public boolean superDispatchTrackballEvent(MotionEvent event) {
444 return super.dispatchTrackballEvent(event);
447 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
448 return super.dispatchGenericMotionEvent(event);
452 public boolean onTouchEvent(MotionEvent event) {
453 return onInterceptTouchEvent(event);
456 private boolean isOutOfInnerBounds(int x, int y) {
457 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
460 private boolean isOutOfBounds(int x, int y) {
461 return x < -5 || y < -5 || x > (getWidth() + 5)
462 || y > (getHeight() + 5);
466 public boolean onInterceptTouchEvent(MotionEvent event) {
467 int action = event.getAction();
468 if (mHasCaption && isShowingCaption()) {
469 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
470 // was (starting) outside the window. Window resizing events should be handled by
472 // TODO: Investigate how to handle the outside touch in window manager
473 // without generating these events.
474 // Currently we receive these because we need to enlarge the window's
475 // touch region so that the monitor channel receives the events
476 // in the outside touch area.
477 if (action == MotionEvent.ACTION_DOWN) {
478 final int x = (int) event.getX();
479 final int y = (int) event.getY();
480 if (isOutOfInnerBounds(x, y)) {
486 if (mFeatureId >= 0) {
487 if (action == MotionEvent.ACTION_DOWN) {
488 int x = (int)event.getX();
489 int y = (int)event.getY();
490 if (isOutOfBounds(x, y)) {
491 mWindow.closePanel(mFeatureId);
497 if (!SWEEP_OPEN_MENU) {
501 if (mFeatureId >= 0) {
502 if (action == MotionEvent.ACTION_DOWN) {
503 Log.i(mLogTag, "Watchiing!");
504 mWatchingForMenu = true;
505 mDownY = (int) event.getY();
509 if (!mWatchingForMenu) {
513 int y = (int)event.getY();
514 if (action == MotionEvent.ACTION_MOVE) {
515 if (y > (mDownY+30)) {
516 Log.i(mLogTag, "Closing!");
517 mWindow.closePanel(mFeatureId);
518 mWatchingForMenu = false;
521 } else if (action == MotionEvent.ACTION_UP) {
522 mWatchingForMenu = false;
528 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
529 // + " (in " + getHeight() + ")");
531 if (action == MotionEvent.ACTION_DOWN) {
532 int y = (int)event.getY();
533 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
534 Log.i(mLogTag, "Watching!");
535 mWatchingForMenu = true;
540 if (!mWatchingForMenu) {
544 int y = (int)event.getY();
545 if (action == MotionEvent.ACTION_MOVE) {
546 if (y < (getHeight()-30)) {
547 Log.i(mLogTag, "Opening!");
548 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
549 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
550 mWatchingForMenu = false;
553 } else if (action == MotionEvent.ACTION_UP) {
554 mWatchingForMenu = false;
561 public void sendAccessibilityEvent(int eventType) {
562 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
566 // if we are showing a feature that should be announced and one child
567 // make this child the event source since this is the feature itself
568 // otherwise the callback will take over and announce its client
569 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
570 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
571 mFeatureId == Window.FEATURE_PROGRESS ||
572 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
573 && getChildCount() == 1) {
574 getChildAt(0).sendAccessibilityEvent(eventType);
576 super.sendAccessibilityEvent(eventType);
581 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
582 final Window.Callback cb = mWindow.getCallback();
583 if (cb != null && !mWindow.isDestroyed()) {
584 if (cb.dispatchPopulateAccessibilityEvent(event)) {
588 return super.dispatchPopulateAccessibilityEventInternal(event);
592 protected boolean setFrame(int l, int t, int r, int b) {
593 boolean changed = super.setFrame(l, t, r, b);
595 final Rect drawingBounds = mDrawingBounds;
596 getDrawingRect(drawingBounds);
598 Drawable fg = getForeground();
600 final Rect frameOffsets = mFrameOffsets;
601 drawingBounds.left += frameOffsets.left;
602 drawingBounds.top += frameOffsets.top;
603 drawingBounds.right -= frameOffsets.right;
604 drawingBounds.bottom -= frameOffsets.bottom;
605 fg.setBounds(drawingBounds);
606 final Rect framePadding = mFramePadding;
607 drawingBounds.left += framePadding.left - frameOffsets.left;
608 drawingBounds.top += framePadding.top - frameOffsets.top;
609 drawingBounds.right -= framePadding.right - frameOffsets.right;
610 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
613 Drawable bg = getBackground();
615 bg.setBounds(drawingBounds);
618 if (SWEEP_OPEN_MENU) {
619 if (mMenuBackground == null && mFeatureId < 0
620 && mWindow.getAttributes().height
621 == WindowManager.LayoutParams.MATCH_PARENT) {
622 mMenuBackground = getContext().getDrawable(
623 R.drawable.menu_background);
625 if (mMenuBackground != null) {
626 mMenuBackground.setBounds(drawingBounds.left,
627 drawingBounds.bottom-6, drawingBounds.right,
628 drawingBounds.bottom+20);
636 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
637 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
638 final boolean isPortrait =
639 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
641 final int widthMode = getMode(widthMeasureSpec);
642 final int heightMode = getMode(heightMeasureSpec);
644 boolean fixedWidth = false;
645 mApplyFloatingHorizontalInsets = false;
646 if (widthMode == AT_MOST) {
647 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
648 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
650 if (tvw.type == TypedValue.TYPE_DIMENSION) {
651 w = (int) tvw.getDimension(metrics);
652 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
653 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
657 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
658 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
660 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
661 Math.min(w, widthSize), EXACTLY);
664 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
665 widthSize - mFloatingInsets.left - mFloatingInsets.right,
667 mApplyFloatingHorizontalInsets = true;
672 mApplyFloatingVerticalInsets = false;
673 if (heightMode == AT_MOST) {
674 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
675 : mWindow.mFixedHeightMinor;
676 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
678 if (tvh.type == TypedValue.TYPE_DIMENSION) {
679 h = (int) tvh.getDimension(metrics);
680 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
681 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
685 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
686 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
688 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
689 Math.min(h, heightSize), EXACTLY);
690 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
691 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
692 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
693 mApplyFloatingVerticalInsets = true;
698 getOutsets(mOutsets);
699 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
700 int mode = MeasureSpec.getMode(heightMeasureSpec);
701 if (mode != MeasureSpec.UNSPECIFIED) {
702 int height = MeasureSpec.getSize(heightMeasureSpec);
703 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
704 height + mOutsets.top + mOutsets.bottom, mode);
707 if (mOutsets.left > 0 || mOutsets.right > 0) {
708 int mode = MeasureSpec.getMode(widthMeasureSpec);
709 if (mode != MeasureSpec.UNSPECIFIED) {
710 int width = MeasureSpec.getSize(widthMeasureSpec);
711 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
712 width + mOutsets.left + mOutsets.right, mode);
716 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
718 int width = getMeasuredWidth();
719 boolean measure = false;
721 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
723 if (!fixedWidth && widthMode == AT_MOST) {
724 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
725 if (tv.type != TypedValue.TYPE_NULL) {
727 if (tv.type == TypedValue.TYPE_DIMENSION) {
728 min = (int)tv.getDimension(metrics);
729 } else if (tv.type == TypedValue.TYPE_FRACTION) {
730 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
734 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
735 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
738 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
744 // TODO: Support height?
747 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
752 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
753 super.onLayout(changed, left, top, right, bottom);
754 getOutsets(mOutsets);
755 if (mOutsets.left > 0) {
756 offsetLeftAndRight(-mOutsets.left);
758 if (mOutsets.top > 0) {
759 offsetTopAndBottom(-mOutsets.top);
761 if (mApplyFloatingVerticalInsets) {
762 offsetTopAndBottom(mFloatingInsets.top);
764 if (mApplyFloatingHorizontalInsets) {
765 offsetLeftAndRight(mFloatingInsets.left);
768 // If the application changed its SystemUI metrics, we might also have to adapt
769 // our shadow elevation.
771 mAllowUpdateElevation = true;
773 if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
774 getViewRootImpl().requestInvalidateRootRenderNode();
779 public void draw(Canvas canvas) {
782 if (mMenuBackground != null) {
783 mMenuBackground.draw(canvas);
788 public boolean showContextMenuForChild(View originalView) {
789 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
793 public boolean showContextMenuForChild(View originalView, float x, float y) {
794 return showContextMenuForChildInternal(originalView, x, y);
797 private boolean showContextMenuForChildInternal(View originalView,
799 // Only allow one context menu at a time.
800 if (mWindow.mContextMenuHelper != null) {
801 mWindow.mContextMenuHelper.dismiss();
802 mWindow.mContextMenuHelper = null;
805 // Reuse the context menu builder.
806 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
807 if (mWindow.mContextMenu == null) {
808 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
809 mWindow.mContextMenu.setCallback(callback);
811 mWindow.mContextMenu.clearAll();
814 final MenuHelper helper;
815 final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
817 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
819 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
822 if (helper != null) {
823 // If it's a dialog, the callback needs to handle showing
824 // sub-menus. Either way, the callback is required for propagating
825 // selection to Context.onContextMenuItemSelected().
826 callback.setShowDialogForSubmenu(!isPopup);
827 helper.setPresenterCallback(callback);
830 mWindow.mContextMenuHelper = helper;
831 return helper != null;
835 public ActionMode startActionModeForChild(View originalView,
836 ActionMode.Callback callback) {
837 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
841 public ActionMode startActionModeForChild(
842 View child, ActionMode.Callback callback, int type) {
843 return startActionMode(child, callback, type);
847 public ActionMode startActionMode(ActionMode.Callback callback) {
848 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
852 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
853 return startActionMode(this, callback, type);
856 private ActionMode startActionMode(
857 View originatingView, ActionMode.Callback callback, int type) {
858 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
859 ActionMode mode = null;
860 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
862 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
863 } catch (AbstractMethodError ame) {
864 // Older apps might not implement the typed version of this method.
865 if (type == ActionMode.TYPE_PRIMARY) {
867 mode = mWindow.getCallback().onWindowStartingActionMode(
869 } catch (AbstractMethodError ame2) {
870 // Older apps might not implement this callback method at all.
876 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
877 cleanupPrimaryActionMode();
878 mPrimaryActionMode = mode;
879 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
880 if (mFloatingActionMode != null) {
881 mFloatingActionMode.finish();
883 mFloatingActionMode = mode;
886 mode = createActionMode(type, wrappedCallback, originatingView);
887 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
888 setHandledActionMode(mode);
893 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
895 mWindow.getCallback().onActionModeStarted(mode);
896 } catch (AbstractMethodError ame) {
897 // Older apps might not implement this callback method.
903 private void cleanupPrimaryActionMode() {
904 if (mPrimaryActionMode != null) {
905 mPrimaryActionMode.finish();
906 mPrimaryActionMode = null;
908 if (mPrimaryActionModeView != null) {
909 mPrimaryActionModeView.killMode();
913 private void cleanupFloatingActionModeViews() {
914 if (mFloatingToolbar != null) {
915 mFloatingToolbar.dismiss();
916 mFloatingToolbar = null;
918 if (mFloatingActionModeOriginatingView != null) {
919 if (mFloatingToolbarPreDrawListener != null) {
920 mFloatingActionModeOriginatingView.getViewTreeObserver()
921 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
922 mFloatingToolbarPreDrawListener = null;
924 mFloatingActionModeOriginatingView = null;
928 void startChanging() {
932 void finishChanging() {
937 public void setWindowBackground(Drawable drawable) {
938 if (getBackground() != drawable) {
939 setBackgroundDrawable(drawable);
940 if (drawable != null) {
941 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
942 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
944 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
945 getContext(), 0, mWindow.mBackgroundFallbackResource,
946 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
948 if (mResizingBackgroundDrawable != null) {
949 mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
951 mBackgroundPadding.setEmpty();
957 public void setWindowFrame(Drawable drawable) {
958 if (getForeground() != drawable) {
959 setForeground(drawable);
960 if (drawable != null) {
961 drawable.getPadding(mFramePadding);
963 mFramePadding.setEmpty();
970 public void onWindowSystemUiVisibilityChanged(int visible) {
971 updateColorViews(null /* insets */, true /* animate */);
975 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
976 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
977 mFloatingInsets.setEmpty();
978 if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
979 // For dialog windows we want to make sure they don't go over the status bar or nav bar.
980 // We consume the system insets and we will reuse them later during the measure phase.
981 // We allow the app to ignore this and handle insets itself by using
982 // FLAG_LAYOUT_IN_SCREEN.
983 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
984 mFloatingInsets.top = insets.getSystemWindowInsetTop();
985 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
986 insets = insets.inset(0, insets.getSystemWindowInsetTop(),
987 0, insets.getSystemWindowInsetBottom());
989 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
990 mFloatingInsets.left = insets.getSystemWindowInsetTop();
991 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
992 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
993 insets.getSystemWindowInsetRight(), 0);
996 mFrameOffsets.set(insets.getSystemWindowInsets());
997 insets = updateColorViews(insets, true /* animate */);
998 insets = updateStatusGuard(insets);
999 if (getForeground() != null) {
1006 public boolean isTransitionGroup() {
1010 public static int getColorViewTopInset(int stableTop, int systemTop) {
1011 return Math.min(stableTop, systemTop);
1014 public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
1015 return Math.min(stableBottom, systemBottom);
1018 public static int getColorViewRightInset(int stableRight, int systemRight) {
1019 return Math.min(stableRight, systemRight);
1022 public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
1023 return Math.min(stableLeft, systemLeft);
1026 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
1027 return bottomInset == 0 && rightInset > 0;
1030 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
1031 return bottomInset == 0 && leftInset > 0;
1034 public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
1035 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1036 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
1039 public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
1040 Rect contentInsets, Rect outRect) {
1041 final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
1042 final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
1043 final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
1044 final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1045 if (isNavBarToRightEdge(bottomInset, rightInset)) {
1046 outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1047 } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1048 outRect.set(0, 0, size, canvasHeight);
1050 outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1054 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1055 WindowManager.LayoutParams attrs = mWindow.getAttributes();
1056 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1058 // IME is an exceptional floating window that requires color view.
1059 final boolean isImeWindow =
1060 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1061 if (!mWindow.mIsFloating || isImeWindow) {
1062 boolean disallowAnimate = !isLaidOut();
1063 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1064 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1065 mLastWindowFlags = attrs.flags;
1067 if (insets != null) {
1068 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
1069 insets.getSystemWindowInsetTop());
1070 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
1071 insets.getSystemWindowInsetBottom());
1072 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
1073 insets.getSystemWindowInsetRight());
1074 mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
1075 insets.getSystemWindowInsetLeft());
1077 // Don't animate if the presence of stable insets has changed, because that
1078 // indicates that the window was either just added and received them for the
1079 // first time, or the window size or position has changed.
1080 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
1081 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1082 mLastHasTopStableInset = hasTopStableInset;
1084 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
1085 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1086 mLastHasBottomStableInset = hasBottomStableInset;
1088 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
1089 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1090 mLastHasRightStableInset = hasRightStableInset;
1092 boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
1093 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1094 mLastHasLeftStableInset = hasLeftStableInset;
1096 mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
1099 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
1100 boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1101 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
1102 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
1103 mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
1104 navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
1105 0 /* sideInset */, animate && !disallowAnimate, false /* force */);
1107 boolean statusBarNeedsRightInset = navBarToRightEdge
1108 && mNavigationColorViewState.present;
1109 boolean statusBarNeedsLeftInset = navBarToLeftEdge
1110 && mNavigationColorViewState.present;
1111 int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1112 : statusBarNeedsLeftInset ? mLastLeftInset : 0;
1113 updateColorViewInt(mStatusColorViewState, sysUiVisibility,
1114 calculateStatusBarColor(), 0, mLastTopInset,
1115 false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1116 animate && !disallowAnimate,
1117 mForceWindowDrawsStatusBarBackground);
1120 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
1121 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
1122 // explicitly asked for it.
1123 boolean consumingNavBar =
1124 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1125 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1126 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
1127 || mLastShouldAlwaysConsumeNavBar;
1129 // If we didn't request fullscreen layout, but we still got it because of the
1130 // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
1131 boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1132 && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1133 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1134 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1135 && mForceWindowDrawsStatusBarBackground
1136 && mLastTopInset != 0;
1138 int consumedTop = consumingStatusBar ? mLastTopInset : 0;
1139 int consumedRight = consumingNavBar ? mLastRightInset : 0;
1140 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
1141 int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
1143 if (mContentRoot != null
1144 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1145 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
1146 if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
1147 || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
1148 lp.topMargin = consumedTop;
1149 lp.rightMargin = consumedRight;
1150 lp.bottomMargin = consumedBottom;
1151 lp.leftMargin = consumedLeft;
1152 mContentRoot.setLayoutParams(lp);
1154 if (insets == null) {
1155 // The insets have changed, but we're not currently in the process
1156 // of dispatching them.
1157 requestApplyInsets();
1160 if (insets != null) {
1161 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
1165 if (insets != null) {
1166 insets = insets.consumeStableInsets();
1171 private int calculateStatusBarColor() {
1172 return calculateStatusBarColor(mWindow.getAttributes().flags,
1173 mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
1176 public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
1177 int statusBarColor) {
1178 return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
1179 : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
1183 private int getCurrentColor(ColorViewState state) {
1184 if (state.visible) {
1192 * Update a color view
1194 * @param state the color view to update.
1195 * @param sysUiVis the current systemUiVisibility to apply.
1196 * @param color the current color to apply.
1197 * @param dividerColor the current divider color to apply.
1198 * @param size the current size in the non-parent-matching dimension.
1199 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1201 * @param sideMargin sideMargin for the color view.
1202 * @param animate if true, the change will be animated.
1204 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
1205 int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
1206 boolean animate, boolean force) {
1207 state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
1208 boolean show = state.attributes.isVisible(state.present, color,
1209 mWindow.getAttributes().flags, force);
1210 boolean showView = show && !isResizing() && size > 0;
1212 boolean visibilityChanged = false;
1213 View view = state.view;
1215 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1216 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
1217 int resolvedGravity = verticalBar
1218 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1219 : state.attributes.verticalGravity;
1223 state.view = view = new View(mContext);
1224 setColor(view, color, dividerColor, verticalBar, seascape);
1225 view.setTransitionName(state.attributes.transitionName);
1226 view.setId(state.attributes.id);
1227 visibilityChanged = true;
1228 view.setVisibility(INVISIBLE);
1229 state.targetVisibility = VISIBLE;
1231 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1234 lp.leftMargin = sideMargin;
1236 lp.rightMargin = sideMargin;
1239 updateColorViewTranslations();
1242 int vis = showView ? VISIBLE : INVISIBLE;
1243 visibilityChanged = state.targetVisibility != vis;
1244 state.targetVisibility = vis;
1245 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1246 int rightMargin = seascape ? 0 : sideMargin;
1247 int leftMargin = seascape ? sideMargin : 0;
1248 if (lp.height != resolvedHeight || lp.width != resolvedWidth
1249 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1250 || lp.leftMargin != leftMargin) {
1251 lp.height = resolvedHeight;
1252 lp.width = resolvedWidth;
1253 lp.gravity = resolvedGravity;
1254 lp.rightMargin = rightMargin;
1255 lp.leftMargin = leftMargin;
1256 view.setLayoutParams(lp);
1259 setColor(view, color, dividerColor, verticalBar, seascape);
1262 if (visibilityChanged) {
1263 view.animate().cancel();
1264 if (animate && !isResizing()) {
1266 if (view.getVisibility() != VISIBLE) {
1267 view.setVisibility(VISIBLE);
1268 view.setAlpha(0.0f);
1270 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1271 setDuration(mBarEnterExitDuration);
1273 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1274 .setDuration(mBarEnterExitDuration)
1275 .withEndAction(new Runnable() {
1278 state.view.setAlpha(1.0f);
1279 state.view.setVisibility(INVISIBLE);
1284 view.setAlpha(1.0f);
1285 view.setVisibility(showView ? VISIBLE : INVISIBLE);
1288 state.visible = show;
1289 state.color = color;
1292 private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1294 if (dividerColor != 0) {
1295 final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1296 if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1297 final int size = Math.round(
1298 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1299 v.getContext().getResources().getDisplayMetrics()));
1300 // Use an inset to make the divider line on the side that faces the app.
1301 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1302 verticalBar && !seascape ? size : 0,
1303 !verticalBar ? size : 0,
1304 verticalBar && seascape ? size : 0, 0);
1305 v.setBackground(new LayerDrawable(new Drawable[] {
1306 new ColorDrawable(dividerColor), d }));
1307 v.setTag(new Pair<>(verticalBar, seascape));
1309 final LayerDrawable d = (LayerDrawable) v.getBackground();
1310 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1311 ((ColorDrawable) inset.getDrawable()).setColor(color);
1312 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1316 v.setBackgroundColor(color);
1320 private void updateColorViewTranslations() {
1321 // Put the color views back in place when they get moved off the screen
1322 // due to the the ViewRootImpl panning.
1323 int rootScrollY = mRootScrollY;
1324 if (mStatusColorViewState.view != null) {
1325 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1327 if (mNavigationColorViewState.view != null) {
1328 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1332 private WindowInsets updateStatusGuard(WindowInsets insets) {
1333 boolean showStatusGuard = false;
1334 // Show the status guard when the non-overlay contextual action bar is showing
1335 if (mPrimaryActionModeView != null) {
1336 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1337 // Insets are magic!
1338 final MarginLayoutParams mlp = (MarginLayoutParams)
1339 mPrimaryActionModeView.getLayoutParams();
1340 boolean mlpChanged = false;
1341 if (mPrimaryActionModeView.isShown()) {
1342 if (mTempRect == null) {
1343 mTempRect = new Rect();
1345 final Rect rect = mTempRect;
1347 // If the parent doesn't consume the insets, manually
1348 // apply the default system window insets.
1349 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1350 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1351 if (mlp.topMargin != newMargin) {
1353 mlp.topMargin = insets.getSystemWindowInsetTop();
1355 if (mStatusGuard == null) {
1356 mStatusGuard = new View(mContext);
1357 mStatusGuard.setBackgroundColor(mContext.getColor(
1358 R.color.decor_view_status_guard));
1359 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1360 new LayoutParams(LayoutParams.MATCH_PARENT,
1361 mlp.topMargin, Gravity.START | Gravity.TOP));
1363 final LayoutParams lp = (LayoutParams)
1364 mStatusGuard.getLayoutParams();
1365 if (lp.height != mlp.topMargin) {
1366 lp.height = mlp.topMargin;
1367 mStatusGuard.setLayoutParams(lp);
1372 // The action mode's theme may differ from the app, so
1373 // always show the status guard above it if we have one.
1374 showStatusGuard = mStatusGuard != null;
1376 // We only need to consume the insets if the action
1377 // mode is overlaid on the app content (e.g. it's
1378 // sitting in a FrameLayout, see
1379 // screen_simple_overlay_action_mode.xml).
1380 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1381 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1382 if (nonOverlay && showStatusGuard) {
1383 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1387 if (mlp.topMargin != 0) {
1393 mPrimaryActionModeView.setLayoutParams(mlp);
1397 if (mStatusGuard != null) {
1398 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1404 * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1405 * an opaque shadow even if the window background is completely transparent. This only applies
1406 * to activities that are currently the task root.
1408 public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1409 if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1413 if (isInPictureInPictureMode) {
1414 final Window.WindowControllerCallback callback =
1415 mWindow.getWindowControllerCallback();
1416 if (callback != null && callback.isTaskRoot()) {
1417 // Call super implementation directly as we don't want to save the PIP outline
1418 // provider to be restored
1419 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1422 // Restore the previous outline provider
1423 if (getOutlineProvider() != mLastOutlineProvider) {
1424 setOutlineProvider(mLastOutlineProvider);
1427 mIsInPictureInPictureMode = isInPictureInPictureMode;
1431 public void setOutlineProvider(ViewOutlineProvider provider) {
1432 super.setOutlineProvider(provider);
1434 // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1435 mLastOutlineProvider = provider;
1438 private void drawableChanged() {
1443 setPadding(mFramePadding.left + mBackgroundPadding.left,
1444 mFramePadding.top + mBackgroundPadding.top,
1445 mFramePadding.right + mBackgroundPadding.right,
1446 mFramePadding.bottom + mBackgroundPadding.bottom);
1450 int opacity = PixelFormat.OPAQUE;
1451 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1452 if (winConfig.hasWindowShadow()) {
1453 // If the window has a shadow, it must be translucent.
1454 opacity = PixelFormat.TRANSLUCENT;
1456 // Note: If there is no background, we will assume opaque. The
1457 // common case seems to be that an application sets there to be
1458 // no background so it can draw everything itself. For that,
1459 // we would like to assume OPAQUE and let the app force it to
1460 // the slower TRANSLUCENT mode if that is really what it wants.
1461 Drawable bg = getBackground();
1462 Drawable fg = getForeground();
1465 opacity = bg.getOpacity();
1466 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1467 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1468 // If the frame padding is zero, then we can be opaque
1469 // if either the frame -or- the background is opaque.
1470 int fop = fg.getOpacity();
1471 int bop = bg.getOpacity();
1473 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
1474 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1475 opacity = PixelFormat.OPAQUE;
1476 } else if (fop == PixelFormat.UNKNOWN) {
1478 } else if (bop == PixelFormat.UNKNOWN) {
1481 opacity = Drawable.resolveOpacity(fop, bop);
1484 // For now we have to assume translucent if there is a
1485 // frame with padding... there is no way to tell if the
1486 // frame and background together will draw all pixels.
1488 Log.v(mLogTag, "Padding: " + mFramePadding);
1489 opacity = PixelFormat.TRANSLUCENT;
1493 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
1497 Log.v(mLogTag, "Selected default opacity: " + opacity);
1499 mDefaultOpacity = opacity;
1500 if (mFeatureId < 0) {
1501 mWindow.setDefaultWindowFormat(opacity);
1506 public void onWindowFocusChanged(boolean hasWindowFocus) {
1507 super.onWindowFocusChanged(hasWindowFocus);
1509 // If the user is chording a menu shortcut, release the chord since
1510 // this window lost focus
1511 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1512 && mWindow.mPanelChordingKey != 0) {
1513 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1516 final Window.Callback cb = mWindow.getCallback();
1517 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1518 cb.onWindowFocusChanged(hasWindowFocus);
1521 if (mPrimaryActionMode != null) {
1522 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1524 if (mFloatingActionMode != null) {
1525 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1532 protected void onAttachedToWindow() {
1533 super.onAttachedToWindow();
1535 final Window.Callback cb = mWindow.getCallback();
1536 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1537 cb.onAttachedToWindow();
1540 if (mFeatureId == -1) {
1542 * The main window has been attached, try to restore any panels
1543 * that may have been open before. This is called in cases where
1544 * an activity is being killed for configuration change and the
1545 * menu was open. When the activity is recreated, the menu
1546 * should be shown again.
1548 mWindow.openPanelsAfterRestore();
1551 if (!mWindowResizeCallbacksAdded) {
1552 // If there is no window callback installed there was no window set before. Set it now.
1553 // Note that our ViewRootImpl object will not change.
1554 getViewRootImpl().addWindowCallbacks(this);
1555 mWindowResizeCallbacksAdded = true;
1556 } else if (mBackdropFrameRenderer != null) {
1557 // We are resizing and this call happened due to a configuration change. Tell the
1558 // renderer about it.
1559 mBackdropFrameRenderer.onConfigurationChange();
1561 mWindow.onViewRootImplSet(getViewRootImpl());
1565 protected void onDetachedFromWindow() {
1566 super.onDetachedFromWindow();
1568 final Window.Callback cb = mWindow.getCallback();
1569 if (cb != null && mFeatureId < 0) {
1570 cb.onDetachedFromWindow();
1573 if (mWindow.mDecorContentParent != null) {
1574 mWindow.mDecorContentParent.dismissPopups();
1577 if (mPrimaryActionModePopup != null) {
1578 removeCallbacks(mShowPrimaryActionModePopup);
1579 if (mPrimaryActionModePopup.isShowing()) {
1580 mPrimaryActionModePopup.dismiss();
1582 mPrimaryActionModePopup = null;
1584 if (mFloatingToolbar != null) {
1585 mFloatingToolbar.dismiss();
1586 mFloatingToolbar = null;
1589 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1590 if (st != null && st.menu != null && mFeatureId < 0) {
1594 releaseThreadedRenderer();
1596 if (mWindowResizeCallbacksAdded) {
1597 getViewRootImpl().removeWindowCallbacks(this);
1598 mWindowResizeCallbacksAdded = false;
1603 public void onCloseSystemDialogs(String reason) {
1604 if (mFeatureId >= 0) {
1605 mWindow.closeAllPanels();
1609 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1610 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1613 public InputQueue.Callback willYouTakeTheInputQueue() {
1614 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1617 public void setSurfaceType(int type) {
1618 mWindow.setType(type);
1621 public void setSurfaceFormat(int format) {
1622 mWindow.setFormat(format);
1625 public void setSurfaceKeepScreenOn(boolean keepOn) {
1626 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1627 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1631 public void onRootViewScrollYChanged(int rootScrollY) {
1632 mRootScrollY = rootScrollY;
1633 updateColorViewTranslations();
1636 private ActionMode createActionMode(
1637 int type, ActionMode.Callback2 callback, View originatingView) {
1639 case ActionMode.TYPE_PRIMARY:
1641 return createStandaloneActionMode(callback);
1642 case ActionMode.TYPE_FLOATING:
1643 return createFloatingActionMode(originatingView, callback);
1647 private void setHandledActionMode(ActionMode mode) {
1648 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1649 setHandledPrimaryActionMode(mode);
1650 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1651 setHandledFloatingActionMode(mode);
1655 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1656 endOnGoingFadeAnimation();
1657 cleanupPrimaryActionMode();
1658 // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1659 // instance at all, or if there is one, but it is detached from window. The latter case
1660 // might happen when app is resized in multi-window mode and decor view is preserved
1661 // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1662 // app memory leaks because killMode() is called when the dismiss animation ends and from
1663 // cleanupPrimaryActionMode() invocation above.
1664 if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
1665 if (mWindow.isFloating()) {
1666 // Use the action bar theme.
1667 final TypedValue outValue = new TypedValue();
1668 final Resources.Theme baseTheme = mContext.getTheme();
1669 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1671 final Context actionBarContext;
1672 if (outValue.resourceId != 0) {
1673 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1674 actionBarTheme.setTo(baseTheme);
1675 actionBarTheme.applyStyle(outValue.resourceId, true);
1677 actionBarContext = new ContextThemeWrapper(mContext, 0);
1678 actionBarContext.getTheme().setTo(actionBarTheme);
1680 actionBarContext = mContext;
1683 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1684 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1685 R.attr.actionModePopupWindowStyle);
1686 mPrimaryActionModePopup.setWindowLayoutType(
1687 WindowManager.LayoutParams.TYPE_APPLICATION);
1688 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1689 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1691 actionBarContext.getTheme().resolveAttribute(
1692 R.attr.actionBarSize, outValue, true);
1693 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1694 actionBarContext.getResources().getDisplayMetrics());
1695 mPrimaryActionModeView.setContentHeight(height);
1696 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1697 mShowPrimaryActionModePopup = new Runnable() {
1699 mPrimaryActionModePopup.showAtLocation(
1700 mPrimaryActionModeView.getApplicationWindowToken(),
1701 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1702 endOnGoingFadeAnimation();
1704 if (shouldAnimatePrimaryActionModeView()) {
1705 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1707 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1709 public void onAnimationStart(Animator animation) {
1710 mPrimaryActionModeView.setVisibility(VISIBLE);
1714 public void onAnimationEnd(Animator animation) {
1715 mPrimaryActionModeView.setAlpha(1f);
1721 mPrimaryActionModeView.setAlpha(1f);
1722 mPrimaryActionModeView.setVisibility(VISIBLE);
1727 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
1729 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1730 mPrimaryActionModePopup = null;
1734 if (mPrimaryActionModeView != null) {
1735 mPrimaryActionModeView.killMode();
1736 ActionMode mode = new StandaloneActionMode(
1737 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1738 callback, mPrimaryActionModePopup == null);
1744 private void endOnGoingFadeAnimation() {
1745 if (mFadeAnim != null) {
1750 private void setHandledPrimaryActionMode(ActionMode mode) {
1751 endOnGoingFadeAnimation();
1752 mPrimaryActionMode = mode;
1753 mPrimaryActionMode.invalidate();
1754 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1755 if (mPrimaryActionModePopup != null) {
1756 post(mShowPrimaryActionModePopup);
1758 if (shouldAnimatePrimaryActionModeView()) {
1759 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1760 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1762 public void onAnimationStart(Animator animation) {
1763 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1767 public void onAnimationEnd(Animator animation) {
1768 mPrimaryActionModeView.setAlpha(1f);
1774 mPrimaryActionModeView.setAlpha(1f);
1775 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1778 mPrimaryActionModeView.sendAccessibilityEvent(
1779 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1782 boolean shouldAnimatePrimaryActionModeView() {
1783 // We only to animate the action mode in if the decor has already been laid out.
1784 // If it hasn't been laid out, it hasn't been drawn to screen yet.
1788 private ActionMode createFloatingActionMode(
1789 View originatingView, ActionMode.Callback2 callback) {
1790 if (mFloatingActionMode != null) {
1791 mFloatingActionMode.finish();
1793 cleanupFloatingActionModeViews();
1794 mFloatingToolbar = new FloatingToolbar(mWindow);
1795 final FloatingActionMode mode =
1796 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
1797 mFloatingActionModeOriginatingView = originatingView;
1798 mFloatingToolbarPreDrawListener =
1799 new ViewTreeObserver.OnPreDrawListener() {
1801 public boolean onPreDraw() {
1802 mode.updateViewLocationInWindow();
1809 private void setHandledFloatingActionMode(ActionMode mode) {
1810 mFloatingActionMode = mode;
1811 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1812 mFloatingActionModeOriginatingView.getViewTreeObserver()
1813 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1817 * Informs the decor if the caption is attached and visible.
1818 * @param attachedAndVisible true when the decor is visible.
1819 * Note that this will even be called if there is no caption.
1821 void enableCaption(boolean attachedAndVisible) {
1822 if (mHasCaption != attachedAndVisible) {
1823 mHasCaption = attachedAndVisible;
1824 if (getForeground() != null) {
1830 void setWindow(PhoneWindow phoneWindow) {
1831 mWindow = phoneWindow;
1832 Context context = getContext();
1833 if (context instanceof DecorContext) {
1834 DecorContext decorContext = (DecorContext) context;
1835 decorContext.setPhoneWindow(mWindow);
1840 public Resources getResources() {
1841 // Make sure the Resources object is propogated from the Context since it can be updated in
1842 // the Context object.
1843 return getContext().getResources();
1847 protected void onConfigurationChanged(Configuration newConfig) {
1848 super.onConfigurationChanged(newConfig);
1850 final boolean displayWindowDecor =
1851 newConfig.windowConfiguration.hasWindowDecorCaption();
1852 if (mDecorCaptionView == null && displayWindowDecor) {
1853 // Configuration now requires a caption.
1854 final LayoutInflater inflater = mWindow.getLayoutInflater();
1855 mDecorCaptionView = createDecorCaptionView(inflater);
1856 if (mDecorCaptionView != null) {
1857 if (mDecorCaptionView.getParent() == null) {
1858 addView(mDecorCaptionView, 0,
1859 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1861 removeView(mContentRoot);
1862 mDecorCaptionView.addView(mContentRoot,
1863 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1865 } else if (mDecorCaptionView != null) {
1866 // We might have to change the kind of surface before we do anything else.
1867 mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
1868 enableCaption(displayWindowDecor);
1871 updateAvailableWidth();
1872 initializeElevation();
1875 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
1876 if (mBackdropFrameRenderer != null) {
1877 loadBackgroundDrawablesIfNeeded();
1878 mBackdropFrameRenderer.onResourcesLoaded(
1879 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
1880 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1881 getCurrentColor(mNavigationColorViewState));
1884 mDecorCaptionView = createDecorCaptionView(inflater);
1885 final View root = inflater.inflate(layoutResource, null);
1886 if (mDecorCaptionView != null) {
1887 if (mDecorCaptionView.getParent() == null) {
1888 addView(mDecorCaptionView,
1889 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1891 mDecorCaptionView.addView(root,
1892 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1895 // Put it below the color views.
1896 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1898 mContentRoot = (ViewGroup) root;
1899 initializeElevation();
1902 private void loadBackgroundDrawablesIfNeeded() {
1903 if (mResizingBackgroundDrawable == null) {
1904 mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
1905 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource,
1906 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
1907 if (mResizingBackgroundDrawable == null) {
1908 // We shouldn't really get here as the background fallback should be always
1909 // available since it is defaulted by the system.
1910 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
1913 if (mCaptionBackgroundDrawable == null) {
1914 mCaptionBackgroundDrawable = getContext().getDrawable(
1915 R.drawable.decor_caption_title_focused);
1917 if (mResizingBackgroundDrawable != null) {
1918 mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
1919 mResizingBackgroundDrawable.setCallback(null);
1923 // Free floating overlapping windows require a caption.
1924 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
1925 DecorCaptionView decorCaptionView = null;
1926 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
1927 View view = getChildAt(i);
1928 if (view instanceof DecorCaptionView) {
1929 // The decor was most likely saved from a relaunch - so reuse it.
1930 decorCaptionView = (DecorCaptionView) view;
1934 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
1935 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
1936 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
1937 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1938 // Only a non floating application window on one of the allowed workspaces can get a caption
1939 if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
1940 // Dependent on the brightness of the used title we either use the
1941 // dark or the light button frame.
1942 if (decorCaptionView == null) {
1943 decorCaptionView = inflateDecorCaptionView(inflater);
1945 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
1947 decorCaptionView = null;
1950 // Tell the decor if it has a visible caption.
1951 enableCaption(decorCaptionView != null);
1952 return decorCaptionView;
1955 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1956 final Context context = getContext();
1957 // We make a copy of the inflater, so it has the right context associated with it.
1958 inflater = inflater.from(context);
1959 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1961 setDecorCaptionShade(context, view);
1965 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1966 final int shade = mWindow.getDecorCaptionShade();
1968 case DECOR_CAPTION_SHADE_LIGHT:
1969 setLightDecorCaptionShade(view);
1971 case DECOR_CAPTION_SHADE_DARK:
1972 setDarkDecorCaptionShade(view);
1975 TypedValue value = new TypedValue();
1976 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1977 // We invert the shade depending on brightness of the theme. Dark shade for light
1978 // theme and vice versa. Thanks to this the buttons should be visible on the
1980 if (Color.luminance(value.data) < 0.5) {
1981 setLightDecorCaptionShade(view);
1983 setDarkDecorCaptionShade(view);
1990 void updateDecorCaptionShade() {
1991 if (mDecorCaptionView != null) {
1992 setDecorCaptionShade(getContext(), mDecorCaptionView);
1996 private void setLightDecorCaptionShade(DecorCaptionView view) {
1997 view.findViewById(R.id.pip_window).setBackgroundResource(
1998 R.drawable.decor_pip_button_light);
1999 view.findViewById(R.id.minimize_window).setBackgroundResource(
2000 R.drawable.decor_minimize_button_light);
2001 view.findViewById(R.id.maximize_window).setBackgroundResource(
2002 R.drawable.decor_maximize_button_light);
2003 view.findViewById(R.id.close_window).setBackgroundResource(
2004 R.drawable.decor_close_button_light);
2007 private void setDarkDecorCaptionShade(DecorCaptionView view) {
2008 view.findViewById(R.id.pip_window).setBackgroundResource(
2009 R.drawable.decor_pip_button_dark);
2010 view.findViewById(R.id.minimize_window).setBackgroundResource(
2011 R.drawable.decor_minimize_button_dark);
2012 view.findViewById(R.id.maximize_window).setBackgroundResource(
2013 R.drawable.decor_maximize_button_dark);
2014 view.findViewById(R.id.close_window).setBackgroundResource(
2015 R.drawable.decor_close_button_dark);
2019 * Returns the color used to fill areas the app has not rendered content to yet when the
2020 * user is resizing the window of an activity in multi-window mode.
2022 public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
2023 int backgroundFallbackRes, boolean windowTranslucent) {
2024 if (backgroundRes != 0) {
2025 final Drawable drawable = context.getDrawable(backgroundRes);
2026 if (drawable != null) {
2027 return enforceNonTranslucentBackground(drawable, windowTranslucent);
2031 if (backgroundFallbackRes != 0) {
2032 final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
2033 if (fallbackDrawable != null) {
2034 return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
2037 return new ColorDrawable(Color.BLACK);
2041 * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2042 * window is not translucent.
2044 private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2045 boolean windowTranslucent) {
2046 if (!windowTranslucent && drawable instanceof ColorDrawable) {
2047 ColorDrawable colorDrawable = (ColorDrawable) drawable;
2048 int color = colorDrawable.getColor();
2049 if (Color.alpha(color) != 255) {
2050 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2053 Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2060 void clearContentView() {
2061 if (mDecorCaptionView != null) {
2062 mDecorCaptionView.removeContentView();
2064 // This window doesn't have caption, so we need to remove everything except our views
2065 // we might have added.
2066 for (int i = getChildCount() - 1; i >= 0; i--) {
2067 View v = getChildAt(i);
2068 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
2069 && v != mStatusGuard) {
2077 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2078 Rect stableInsets) {
2079 if (mBackdropFrameRenderer != null) {
2080 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
2085 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
2086 Rect stableInsets, int resizeMode) {
2087 if (mWindow.isDestroyed()) {
2088 // If the owner's window is gone, we should not be able to come here anymore.
2089 releaseThreadedRenderer();
2092 if (mBackdropFrameRenderer != null) {
2095 final ThreadedRenderer renderer = getThreadedRenderer();
2096 if (renderer != null) {
2097 loadBackgroundDrawablesIfNeeded();
2098 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
2099 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2100 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2101 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
2102 stableInsets, resizeMode);
2104 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2105 // If we want to get the shadow shown while resizing, we would need to elevate a new
2106 // element which owns the caption and has the elevation.
2109 updateColorViews(null /* insets */, false);
2111 mResizeMode = resizeMode;
2112 getViewRootImpl().requestInvalidateRootRenderNode();
2116 public void onWindowDragResizeEnd() {
2117 releaseThreadedRenderer();
2118 updateColorViews(null /* insets */, false);
2119 mResizeMode = RESIZE_MODE_INVALID;
2120 getViewRootImpl().requestInvalidateRootRenderNode();
2124 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2125 if (mBackdropFrameRenderer == null) {
2128 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2132 public void onRequestDraw(boolean reportNextDraw) {
2133 if (mBackdropFrameRenderer != null) {
2134 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2135 } else if (reportNextDraw) {
2136 // If render thread is gone, just report immediately.
2137 if (isAttachedToWindow()) {
2138 getViewRootImpl().reportDrawFinish();
2144 public void onPostDraw(DisplayListCanvas canvas) {
2145 drawResizingShadowIfNeeded(canvas);
2148 private void initResizingPaints() {
2149 final int startColor = mContext.getResources().getColor(
2150 R.color.resize_shadow_start_color, null);
2151 final int endColor = mContext.getResources().getColor(
2152 R.color.resize_shadow_end_color, null);
2153 final int middleColor = (startColor + endColor) / 2;
2154 mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2155 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2156 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2157 mVerticalResizeShadowPaint.setShader(new LinearGradient(
2158 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2159 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2162 private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
2163 if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2164 || mWindow.isTranslucent()
2165 || mWindow.isShowingWallpaper()) {
2169 canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2170 canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2173 canvas.translate(getWidth() - mFrameOffsets.right, 0);
2174 canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2178 /** Release the renderer thread which is usually done when the user stops resizing. */
2179 private void releaseThreadedRenderer() {
2180 if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2181 mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2182 mLastBackgroundDrawableCb = null;
2185 if (mBackdropFrameRenderer != null) {
2186 mBackdropFrameRenderer.releaseRenderer();
2187 mBackdropFrameRenderer = null;
2188 // Bring the shadow back.
2193 private boolean isResizing() {
2194 return mBackdropFrameRenderer != null;
2198 * The elevation gets set for the first time and the framework needs to be informed that
2199 * the surface layer gets created with the shadow size in mind.
2201 private void initializeElevation() {
2202 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2203 mAllowUpdateElevation = false;
2207 private void updateElevation() {
2208 float elevation = 0;
2209 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2210 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2211 // since the shadow is bound to the content size and not the target size.
2212 final int windowingMode =
2213 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2214 if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
2215 elevation = hasWindowFocus() ?
2216 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
2217 // Add a maximum shadow height value to the top level view.
2218 // Note that pinned stack doesn't have focus
2219 // so maximum shadow height adjustment isn't needed.
2220 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
2221 if (!mAllowUpdateElevation) {
2222 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2224 // Convert the DP elevation into physical pixels.
2225 elevation = dipToPx(elevation);
2226 mElevationAdjustedForStack = true;
2227 } else if (windowingMode == WINDOWING_MODE_PINNED) {
2228 elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
2229 mElevationAdjustedForStack = true;
2231 mElevationAdjustedForStack = false;
2234 // region @boringdroid
2235 if ((windowingMode == WINDOWING_MODE_FREEFORM) && isResizing()) {
2236 elevation = dipToPx(DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP);
2237 mElevationAdjustedForStack = true;
2240 // Don't change the elevation if we didn't previously adjust it for the stack it was in
2241 // or it didn't change.
2242 if ((wasAdjustedForStack || mElevationAdjustedForStack)
2243 && getElevation() != elevation) {
2244 mWindow.setElevation(elevation);
2248 boolean isShowingCaption() {
2249 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
2252 int getCaptionHeight() {
2253 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
2257 * Converts a DIP measure into physical pixels.
2258 * @param dip The dip value.
2259 * @return Returns the number of pixels.
2261 private float dipToPx(float dip) {
2262 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2263 getResources().getDisplayMetrics());
2267 * Provide an override of the caption background drawable.
2269 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2270 mUserCaptionBackgroundDrawable = drawable;
2271 if (mBackdropFrameRenderer != null) {
2272 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2276 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2277 if (params == null) {
2280 final String[] split = params.getTitle().toString().split("\\.");
2281 if (split.length > 0) {
2282 return split[split.length - 1];
2288 void updateLogTag(WindowManager.LayoutParams params) {
2289 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2292 private void updateAvailableWidth() {
2293 Resources res = getResources();
2294 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2295 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2302 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2303 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2304 final Menu menu = st != null ? st.menu : null;
2305 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2306 mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
2311 public void dispatchPointerCaptureChanged(boolean hasCapture) {
2312 super.dispatchPointerCaptureChanged(hasCapture);
2313 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2314 mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2319 public int getAccessibilityViewId() {
2320 return AccessibilityNodeInfo.ROOT_ITEM_ID;
2324 public String toString() {
2325 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2326 + getTitleSuffix(mWindow.getAttributes()) + "]";
2329 private static class ColorViewState {
2331 int targetVisibility = View.INVISIBLE;
2332 boolean present = false;
2336 final ColorViewAttributes attributes;
2338 ColorViewState(ColorViewAttributes attributes) {
2339 this.attributes = attributes;
2343 public static class ColorViewAttributes {
2346 final int systemUiHideFlag;
2347 final int translucentFlag;
2348 final int verticalGravity;
2349 final int horizontalGravity;
2350 final int seascapeGravity;
2351 final String transitionName;
2352 final int hideWindowFlag;
2354 private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2355 int horizontalGravity, int seascapeGravity, String transitionName, int id,
2356 int hideWindowFlag) {
2358 this.systemUiHideFlag = systemUiHideFlag;
2359 this.translucentFlag = translucentFlag;
2360 this.verticalGravity = verticalGravity;
2361 this.horizontalGravity = horizontalGravity;
2362 this.seascapeGravity = seascapeGravity;
2363 this.transitionName = transitionName;
2364 this.hideWindowFlag = hideWindowFlag;
2367 public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2368 return (sysUiVis & systemUiHideFlag) == 0
2369 && (windowFlags & hideWindowFlag) == 0
2370 && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2374 public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2376 && (color & Color.BLACK) != 0
2377 && ((windowFlags & translucentFlag) == 0 || force);
2380 public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2381 final boolean present = isPresent(sysUiVis, windowFlags, force);
2382 return isVisible(present, color, windowFlags, force);
2387 * Clears out internal references when the action mode is destroyed.
2389 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2390 private final ActionMode.Callback mWrapped;
2392 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2396 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2397 return mWrapped.onCreateActionMode(mode, menu);
2400 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2401 requestFitSystemWindows();
2402 return mWrapped.onPrepareActionMode(mode, menu);
2405 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2406 return mWrapped.onActionItemClicked(mode, item);
2409 public void onDestroyActionMode(ActionMode mode) {
2410 mWrapped.onDestroyActionMode(mode);
2411 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2413 final boolean isPrimary;
2414 final boolean isFloating;
2416 isPrimary = mode == mPrimaryActionMode;
2417 isFloating = mode == mFloatingActionMode;
2418 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
2419 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
2420 + mode + " was not the current primary action mode! Expected "
2421 + mPrimaryActionMode);
2423 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
2424 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
2425 + mode + " was not the current floating action mode! Expected "
2426 + mFloatingActionMode);
2429 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2430 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2433 if (mPrimaryActionModePopup != null) {
2434 removeCallbacks(mShowPrimaryActionModePopup);
2436 if (mPrimaryActionModeView != null) {
2437 endOnGoingFadeAnimation();
2438 // Store action mode view reference, so we can access it safely when animation
2439 // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2440 // so no need to store reference to it in separate variable.
2441 final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
2442 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2444 mFadeAnim.addListener(new Animator.AnimatorListener() {
2447 public void onAnimationStart(Animator animation) {
2452 public void onAnimationEnd(Animator animation) {
2453 // If mPrimaryActionModeView has changed - it means that we've
2454 // cleared the content while preserving decor view. We don't
2455 // want to change the state of new instances accidentally here.
2456 if (lastActionModeView == mPrimaryActionModeView) {
2457 lastActionModeView.setVisibility(GONE);
2458 if (mPrimaryActionModePopup != null) {
2459 mPrimaryActionModePopup.dismiss();
2461 lastActionModeView.killMode();
2467 public void onAnimationCancel(Animator animation) {
2472 public void onAnimationRepeat(Animator animation) {
2479 mPrimaryActionMode = null;
2480 } else if (isFloating) {
2481 cleanupFloatingActionModeViews();
2482 mFloatingActionMode = null;
2484 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2486 mWindow.getCallback().onActionModeFinished(mode);
2487 } catch (AbstractMethodError ame) {
2488 // Older apps might not implement this callback method.
2491 requestFitSystemWindows();
2495 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2496 if (mWrapped instanceof ActionMode.Callback2) {
2497 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2499 super.onGetContentRect(mode, view, outRect);