• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
17 package com.android.internal.policy;
18 
19 import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
20 import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21 import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
22 import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
23 import static android.os.Build.VERSION_CODES.M;
24 import static android.os.Build.VERSION_CODES.N;
25 import static android.view.InsetsState.ITYPE_NAVIGATION_BAR;
26 import static android.view.InsetsState.ITYPE_STATUS_BAR;
27 import static android.view.InsetsState.clearCompatInsets;
28 import static android.view.View.MeasureSpec.AT_MOST;
29 import static android.view.View.MeasureSpec.EXACTLY;
30 import static android.view.View.MeasureSpec.getMode;
31 import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
32 import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
33 import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
34 import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
35 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
36 import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
37 import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
38 import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
39 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
40 import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
41 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
42 import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
43 import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
44 import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
45 import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
46 
47 import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
48 
49 import android.animation.Animator;
50 import android.animation.AnimatorListenerAdapter;
51 import android.animation.ObjectAnimator;
52 import android.annotation.Nullable;
53 import android.annotation.TestApi;
54 import android.app.WindowConfiguration;
55 import android.compat.annotation.UnsupportedAppUsage;
56 import android.content.Context;
57 import android.content.res.Configuration;
58 import android.content.res.Resources;
59 import android.graphics.Canvas;
60 import android.graphics.Color;
61 import android.graphics.Insets;
62 import android.graphics.LinearGradient;
63 import android.graphics.Outline;
64 import android.graphics.Paint;
65 import android.graphics.PixelFormat;
66 import android.graphics.RecordingCanvas;
67 import android.graphics.Rect;
68 import android.graphics.Region;
69 import android.graphics.Shader;
70 import android.graphics.drawable.ColorDrawable;
71 import android.graphics.drawable.Drawable;
72 import android.graphics.drawable.InsetDrawable;
73 import android.graphics.drawable.LayerDrawable;
74 import android.util.DisplayMetrics;
75 import android.util.Log;
76 import android.util.Pair;
77 import android.util.TypedValue;
78 import android.view.ActionMode;
79 import android.view.ContextThemeWrapper;
80 import android.view.Gravity;
81 import android.view.InputQueue;
82 import android.view.InsetsState;
83 import android.view.InsetsState.InternalInsetsType;
84 import android.view.KeyEvent;
85 import android.view.KeyboardShortcutGroup;
86 import android.view.LayoutInflater;
87 import android.view.Menu;
88 import android.view.MenuItem;
89 import android.view.MotionEvent;
90 import android.view.PendingInsetsController;
91 import android.view.ThreadedRenderer;
92 import android.view.View;
93 import android.view.ViewGroup;
94 import android.view.ViewOutlineProvider;
95 import android.view.ViewRootImpl;
96 import android.view.ViewStub;
97 import android.view.ViewTreeObserver;
98 import android.view.Window;
99 import android.view.WindowCallbacks;
100 import android.view.WindowInsets;
101 import android.view.WindowInsetsController;
102 import android.view.WindowInsetsController.Appearance;
103 import android.view.WindowManager;
104 import android.view.accessibility.AccessibilityEvent;
105 import android.view.accessibility.AccessibilityManager;
106 import android.view.accessibility.AccessibilityNodeInfo;
107 import android.view.animation.AnimationUtils;
108 import android.view.animation.Interpolator;
109 import android.widget.FrameLayout;
110 import android.widget.PopupWindow;
111 
112 import com.android.internal.R;
113 import com.android.internal.graphics.drawable.BackgroundBlurDrawable;
114 import com.android.internal.policy.PhoneWindow.PanelFeatureState;
115 import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
116 import com.android.internal.view.FloatingActionMode;
117 import com.android.internal.view.RootViewSurfaceTaker;
118 import com.android.internal.view.StandaloneActionMode;
119 import com.android.internal.view.menu.ContextMenuBuilder;
120 import com.android.internal.view.menu.MenuHelper;
121 import com.android.internal.widget.ActionBarContextView;
122 import com.android.internal.widget.BackgroundFallback;
123 import com.android.internal.widget.DecorCaptionView;
124 import com.android.internal.widget.FloatingToolbar;
125 
126 import java.util.List;
127 import java.util.function.Consumer;
128 
129 /** @hide */
130 public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
131     private static final String TAG = "DecorView";
132 
133     private static final boolean DEBUG_MEASURE = false;
134 
135     private static final boolean SWEEP_OPEN_MENU = false;
136 
137     // The height of a window which has focus in DIP.
138     public static final int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
139     // The height of a window which has not in DIP.
140     public static final int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
141 
142     private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
143 
144     public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
145             new ColorViewAttributes(FLAG_TRANSLUCENT_STATUS,
146                     Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
147                     Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
148                     com.android.internal.R.id.statusBarBackground, ITYPE_STATUS_BAR);
149 
150     public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
151             new ColorViewAttributes(FLAG_TRANSLUCENT_NAVIGATION,
152                     Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
153                     Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
154                     com.android.internal.R.id.navigationBarBackground, ITYPE_NAVIGATION_BAR);
155 
156     // This is used to workaround an issue where the PiP shadow can be transparent if the window
157     // background is transparent
158     private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
159         @Override
160         public void getOutline(View view, Outline outline) {
161             outline.setRect(0, 0, view.getWidth(), view.getHeight());
162             outline.setAlpha(1f);
163         }
164     };
165 
166     // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
167     // size calculation takes the shadow size into account. We set the elevation currently
168     // to max until the first layout command has been executed.
169     private boolean mAllowUpdateElevation = false;
170 
171     private boolean mElevationAdjustedForStack = false;
172 
173     // Keeps track of the picture-in-picture mode for the view shadow
174     private boolean mIsInPictureInPictureMode;
175 
176     // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER
177     private ViewOutlineProvider mLastOutlineProvider;
178 
179     int mDefaultOpacity = PixelFormat.OPAQUE;
180 
181     /** The feature ID of the panel, or -1 if this is the application's DecorView */
182     private final int mFeatureId;
183 
184     private final Rect mDrawingBounds = new Rect();
185 
186     private final Rect mBackgroundPadding = new Rect();
187 
188     private final Rect mFramePadding = new Rect();
189 
190     private final Rect mFrameOffsets = new Rect();
191 
192     private boolean mHasCaption = false;
193 
194     private boolean mChanging;
195 
196     private Drawable mMenuBackground;
197     private boolean mWatchingForMenu;
198     private int mDownY;
199 
200     ActionMode mPrimaryActionMode;
201     private ActionMode mFloatingActionMode;
202     private ActionBarContextView mPrimaryActionModeView;
203     private PopupWindow mPrimaryActionModePopup;
204     private Runnable mShowPrimaryActionModePopup;
205     private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
206     private View mFloatingActionModeOriginatingView;
207     private FloatingToolbar mFloatingToolbar;
208     private ObjectAnimator mFadeAnim;
209 
210     // View added at runtime to draw under the status bar area
211     private View mStatusGuard;
212 
213     private final ColorViewState mStatusColorViewState =
214             new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
215     private final ColorViewState mNavigationColorViewState =
216             new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
217 
218     private final Interpolator mShowInterpolator;
219     private final Interpolator mHideInterpolator;
220     private final int mBarEnterExitDuration;
221     final boolean mForceWindowDrawsBarBackgrounds;
222     private final int mSemiTransparentBarColor;
223 
224     private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
225 
226     private int mLastTopInset = 0;
227     @UnsupportedAppUsage
228     private int mLastBottomInset = 0;
229     @UnsupportedAppUsage
230     private int mLastRightInset = 0;
231     @UnsupportedAppUsage
232     private int mLastLeftInset = 0;
233     private boolean mLastHasTopStableInset = false;
234     private boolean mLastHasBottomStableInset = false;
235     private boolean mLastHasRightStableInset = false;
236     private boolean mLastHasLeftStableInset = false;
237     private int mLastWindowFlags = 0;
238     private boolean mLastShouldAlwaysConsumeSystemBars = false;
239 
240     private int mRootScrollY = 0;
241 
242     @UnsupportedAppUsage
243     private PhoneWindow mWindow;
244 
245     ViewGroup mContentRoot;
246 
247     private Rect mTempRect;
248 
249     // This is the caption view for the window, containing the caption and window control
250     // buttons. The visibility of this decor depends on the workspace and the window type.
251     // If the window type does not require such a view, this member might be null.
252     private DecorCaptionView mDecorCaptionView;
253 
254     private boolean mWindowResizeCallbacksAdded = false;
255     private Drawable.Callback mLastBackgroundDrawableCb = null;
256     private BackdropFrameRenderer mBackdropFrameRenderer = null;
257     private Drawable mOriginalBackgroundDrawable;
258     private Drawable mLastOriginalBackgroundDrawable;
259     private Drawable mResizingBackgroundDrawable;
260     private BackgroundBlurDrawable mBackgroundBlurDrawable;
261     private BackgroundBlurDrawable mLastBackgroundBlurDrawable;
262 
263     /**
264      * Temporary holder for a window background when it is set before {@link #mWindow} is
265      * initialized. It will be set as the actual background once {@link #setWindow(PhoneWindow)} is
266      * called.
267      */
268     @Nullable
269     private Drawable mPendingWindowBackground;
270     private Drawable mCaptionBackgroundDrawable;
271     private Drawable mUserCaptionBackgroundDrawable;
272 
273     private float mAvailableWidth;
274 
275     String mLogTag = TAG;
276     private final Rect mFloatingInsets = new Rect();
277     private boolean mApplyFloatingVerticalInsets = false;
278     private boolean mApplyFloatingHorizontalInsets = false;
279 
280     private int mResizeMode = RESIZE_MODE_INVALID;
281     private final int mResizeShadowSize;
282     private final Paint mVerticalResizeShadowPaint = new Paint();
283     private final Paint mHorizontalResizeShadowPaint = new Paint();
284     private final Paint mLegacyNavigationBarBackgroundPaint = new Paint();
285     private Insets mBackgroundInsets = Insets.NONE;
286     private Insets mLastBackgroundInsets = Insets.NONE;
287     private boolean mDrawLegacyNavigationBarBackground;
288 
289     private PendingInsetsController mPendingInsetsController = new PendingInsetsController();
290 
291     private int mOriginalBackgroundBlurRadius = 0;
292     private int mBackgroundBlurRadius = 0;
293     private boolean mCrossWindowBlurEnabled;
294     private final ViewTreeObserver.OnPreDrawListener mBackgroundBlurOnPreDrawListener = () -> {
295         updateBackgroundBlurCorners();
296         return true;
297     };
298     private Consumer<Boolean> mCrossWindowBlurEnabledListener;
299 
DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params)300     DecorView(Context context, int featureId, PhoneWindow window,
301             WindowManager.LayoutParams params) {
302         super(context);
303         mFeatureId = featureId;
304 
305         mShowInterpolator = AnimationUtils.loadInterpolator(context,
306                 android.R.interpolator.linear_out_slow_in);
307         mHideInterpolator = AnimationUtils.loadInterpolator(context,
308                 android.R.interpolator.fast_out_linear_in);
309 
310         mBarEnterExitDuration = context.getResources().getInteger(
311                 R.integer.dock_enter_exit_duration);
312         mForceWindowDrawsBarBackgrounds = context.getResources().getBoolean(
313                 R.bool.config_forceWindowDrawsStatusBarBackground)
314                 && context.getApplicationInfo().targetSdkVersion >= N;
315         mSemiTransparentBarColor = context.getResources().getColor(
316                 R.color.system_bar_background_semi_transparent, null /* theme */);
317 
318         updateAvailableWidth();
319 
320         setWindow(window);
321 
322         updateLogTag(params);
323 
324         mResizeShadowSize = context.getResources().getDimensionPixelSize(
325                 R.dimen.resize_shadow_size);
326         initResizingPaints();
327 
328         mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
329     }
330 
setBackgroundFallback(@ullable Drawable fallbackDrawable)331     void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
332         mBackgroundFallback.setDrawable(fallbackDrawable);
333         setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
334     }
335 
336     @TestApi
getBackgroundFallback()337     public @Nullable Drawable getBackgroundFallback() {
338         return mBackgroundFallback.getDrawable();
339     }
340 
getStatusBarBackgroundView()341     @Nullable View getStatusBarBackgroundView() {
342         return mStatusColorViewState.view;
343     }
344 
getNavigationBarBackgroundView()345     @Nullable View getNavigationBarBackgroundView() {
346         return mNavigationColorViewState.view;
347     }
348 
349     @Override
gatherTransparentRegion(Region region)350     public boolean gatherTransparentRegion(Region region) {
351         boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
352         boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
353         boolean decorOpaque = super.gatherTransparentRegion(region);
354 
355         // combine bools after computation, so each method above always executes
356         return statusOpaque || navOpaque || decorOpaque;
357     }
358 
gatherTransparentRegion(ColorViewState colorViewState, Region region)359     boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
360         if (colorViewState.view != null && colorViewState.visible && isResizing()) {
361             // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
362             // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
363             // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
364             return colorViewState.view.gatherTransparentRegion(region);
365         }
366         return false; // no opaque area added
367     }
368 
369     @Override
onDraw(Canvas c)370     public void onDraw(Canvas c) {
371         super.onDraw(c);
372 
373         mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
374                 mStatusColorViewState.view, mNavigationColorViewState.view);
375     }
376 
377     @Override
dispatchKeyEvent(KeyEvent event)378     public boolean dispatchKeyEvent(KeyEvent event) {
379         final int keyCode = event.getKeyCode();
380         final int action = event.getAction();
381         final boolean isDown = action == KeyEvent.ACTION_DOWN;
382 
383         if (isDown && (event.getRepeatCount() == 0)) {
384             // First handle chording of panel key: if a panel key is held
385             // but not released, try to execute a shortcut in it.
386             if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
387                 boolean handled = dispatchKeyShortcutEvent(event);
388                 if (handled) {
389                     return true;
390                 }
391             }
392 
393             // If a panel is open, perform a shortcut on it without the
394             // chorded panel key
395             if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
396                 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
397                     return true;
398                 }
399             }
400         }
401 
402         if (!mWindow.isDestroyed()) {
403             final Window.Callback cb = mWindow.getCallback();
404             final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
405                     : super.dispatchKeyEvent(event);
406             if (handled) {
407                 return true;
408             }
409         }
410 
411         return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
412                 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
413     }
414 
415     @Override
416     public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
417         // If the panel is already prepared, then perform the shortcut using it.
418         boolean handled;
419         if (mWindow.mPreparedPanel != null) {
420             handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
421                     Menu.FLAG_PERFORM_NO_CLOSE);
422             if (handled) {
423                 if (mWindow.mPreparedPanel != null) {
424                     mWindow.mPreparedPanel.isHandled = true;
425                 }
426                 return true;
427             }
428         }
429 
430         // Shortcut not handled by the panel.  Dispatch to the view hierarchy.
431         final Window.Callback cb = mWindow.getCallback();
432         handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
433                 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
434         if (handled) {
435             return true;
436         }
437 
438         // If the panel is not prepared, then we may be trying to handle a shortcut key
439         // combination such as Control+C.  Temporarily prepare the panel then mark it
440         // unprepared again when finished to ensure that the panel will again be prepared
441         // the next time it is shown for real.
442         PhoneWindow.PanelFeatureState st =
443                 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
444         if (st != null && mWindow.mPreparedPanel == null) {
445             mWindow.preparePanel(st, ev);
446             handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
447                     Menu.FLAG_PERFORM_NO_CLOSE);
448             st.isPrepared = false;
449             if (handled) {
450                 return true;
451             }
452         }
453         return false;
454     }
455 
456     @Override
457     public boolean dispatchTouchEvent(MotionEvent ev) {
458         final Window.Callback cb = mWindow.getCallback();
459         return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
460                 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
461     }
462 
463     @Override
464     public boolean dispatchTrackballEvent(MotionEvent ev) {
465         final Window.Callback cb = mWindow.getCallback();
466         return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
467                 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
468     }
469 
470     @Override
471     public boolean dispatchGenericMotionEvent(MotionEvent ev) {
472         final Window.Callback cb = mWindow.getCallback();
473         return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
474                 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
475     }
476 
477     public boolean superDispatchKeyEvent(KeyEvent event) {
478         // Give priority to closing action modes if applicable.
479         if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
480             final int action = event.getAction();
481             // Back cancels action modes first.
482             if (mPrimaryActionMode != null) {
483                 if (action == KeyEvent.ACTION_UP) {
484                     mPrimaryActionMode.finish();
485                 }
486                 return true;
487             }
488         }
489 
490         if (super.dispatchKeyEvent(event)) {
491             return true;
492         }
493 
494         return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
495     }
496 
497     public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
498         return super.dispatchKeyShortcutEvent(event);
499     }
500 
501     public boolean superDispatchTouchEvent(MotionEvent event) {
502         return super.dispatchTouchEvent(event);
503     }
504 
505     public boolean superDispatchTrackballEvent(MotionEvent event) {
506         return super.dispatchTrackballEvent(event);
507     }
508 
509     public boolean superDispatchGenericMotionEvent(MotionEvent event) {
510         return super.dispatchGenericMotionEvent(event);
511     }
512 
513     @Override
514     public boolean onTouchEvent(MotionEvent event) {
515         return onInterceptTouchEvent(event);
516     }
517 
518     private boolean isOutOfInnerBounds(int x, int y) {
519         return x < 0 || y < 0 || x > getWidth() || y > getHeight();
520     }
521 
522     private boolean isOutOfBounds(int x, int y) {
523         return x < -5 || y < -5 || x > (getWidth() + 5)
524                 || y > (getHeight() + 5);
525     }
526 
527     @Override
528     public boolean onInterceptTouchEvent(MotionEvent event) {
529         int action = event.getAction();
530         if (mHasCaption && isShowingCaption()) {
531             // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
532             // was (starting) outside the window. Window resizing events should be handled by
533             // WindowManager.
534             // TODO: Investigate how to handle the outside touch in window manager
535             //       without generating these events.
536             //       Currently we receive these because we need to enlarge the window's
537             //       touch region so that the monitor channel receives the events
538             //       in the outside touch area.
539             if (action == MotionEvent.ACTION_DOWN) {
540                 final int x = (int) event.getX();
541                 final int y = (int) event.getY();
542                 if (isOutOfInnerBounds(x, y)) {
543                     return true;
544                 }
545             }
546         }
547 
548         if (mFeatureId >= 0) {
549             if (action == MotionEvent.ACTION_DOWN) {
550                 int x = (int)event.getX();
551                 int y = (int)event.getY();
552                 if (isOutOfBounds(x, y)) {
553                     mWindow.closePanel(mFeatureId);
554                     return true;
555                 }
556             }
557         }
558 
559         if (!SWEEP_OPEN_MENU) {
560             return false;
561         }
562 
563         if (mFeatureId >= 0) {
564             if (action == MotionEvent.ACTION_DOWN) {
565                 Log.i(mLogTag, "Watchiing!");
566                 mWatchingForMenu = true;
567                 mDownY = (int) event.getY();
568                 return false;
569             }
570 
571             if (!mWatchingForMenu) {
572                 return false;
573             }
574 
575             int y = (int)event.getY();
576             if (action == MotionEvent.ACTION_MOVE) {
577                 if (y > (mDownY+30)) {
578                     Log.i(mLogTag, "Closing!");
579                     mWindow.closePanel(mFeatureId);
580                     mWatchingForMenu = false;
581                     return true;
582                 }
583             } else if (action == MotionEvent.ACTION_UP) {
584                 mWatchingForMenu = false;
585             }
586 
587             return false;
588         }
589 
590         //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
591         //        + " (in " + getHeight() + ")");
592 
593         if (action == MotionEvent.ACTION_DOWN) {
594             int y = (int)event.getY();
595             if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
596                 Log.i(mLogTag, "Watching!");
597                 mWatchingForMenu = true;
598             }
599             return false;
600         }
601 
602         if (!mWatchingForMenu) {
603             return false;
604         }
605 
606         int y = (int)event.getY();
607         if (action == MotionEvent.ACTION_MOVE) {
608             if (y < (getHeight()-30)) {
609                 Log.i(mLogTag, "Opening!");
610                 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
611                         KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
612                 mWatchingForMenu = false;
613                 return true;
614             }
615         } else if (action == MotionEvent.ACTION_UP) {
616             mWatchingForMenu = false;
617         }
618 
619         return false;
620     }
621 
622     @Override
623     public void sendAccessibilityEvent(int eventType) {
624         if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
625             return;
626         }
627 
628         // if we are showing a feature that should be announced and one child
629         // make this child the event source since this is the feature itself
630         // otherwise the callback will take over and announce its client
631         if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
632                 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
633                 mFeatureId == Window.FEATURE_PROGRESS ||
634                 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
635                 && getChildCount() == 1) {
636             getChildAt(0).sendAccessibilityEvent(eventType);
637         } else {
638             super.sendAccessibilityEvent(eventType);
639         }
640     }
641 
642     @Override
643     public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
644         final Window.Callback cb = mWindow.getCallback();
645         if (cb != null && !mWindow.isDestroyed()) {
646             if (cb.dispatchPopulateAccessibilityEvent(event)) {
647                 return true;
648             }
649         }
650         return super.dispatchPopulateAccessibilityEventInternal(event);
651     }
652 
653     @Override
654     protected boolean setFrame(int l, int t, int r, int b) {
655         boolean changed = super.setFrame(l, t, r, b);
656         if (changed) {
657             final Rect drawingBounds = mDrawingBounds;
658             getDrawingRect(drawingBounds);
659 
660             Drawable fg = getForeground();
661             if (fg != null) {
662                 final Rect frameOffsets = mFrameOffsets;
663                 drawingBounds.left += frameOffsets.left;
664                 drawingBounds.top += frameOffsets.top;
665                 drawingBounds.right -= frameOffsets.right;
666                 drawingBounds.bottom -= frameOffsets.bottom;
667                 fg.setBounds(drawingBounds);
668                 final Rect framePadding = mFramePadding;
669                 drawingBounds.left += framePadding.left - frameOffsets.left;
670                 drawingBounds.top += framePadding.top - frameOffsets.top;
671                 drawingBounds.right -= framePadding.right - frameOffsets.right;
672                 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
673             }
674 
675             // Need to call super here as we pretend to be having the original background.
676             Drawable bg = super.getBackground();
677             if (bg != null) {
678                 bg.setBounds(drawingBounds);
679             }
680 
681             if (SWEEP_OPEN_MENU) {
682                 if (mMenuBackground == null && mFeatureId < 0
683                         && mWindow.getAttributes().height
684                         == WindowManager.LayoutParams.MATCH_PARENT) {
685                     mMenuBackground = getContext().getDrawable(
686                             R.drawable.menu_background);
687                 }
688                 if (mMenuBackground != null) {
689                     mMenuBackground.setBounds(drawingBounds.left,
690                             drawingBounds.bottom-6, drawingBounds.right,
691                             drawingBounds.bottom+20);
692                 }
693             }
694         }
695         return changed;
696     }
697 
698     @Override
699     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
700         final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
701         final boolean isPortrait =
702                 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
703 
704         final int widthMode = getMode(widthMeasureSpec);
705         final int heightMode = getMode(heightMeasureSpec);
706 
707         boolean fixedWidth = false;
708         mApplyFloatingHorizontalInsets = false;
709         if (widthMode == AT_MOST) {
710             final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
711             if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
712                 final int w;
713                 if (tvw.type == TypedValue.TYPE_DIMENSION) {
714                     w = (int) tvw.getDimension(metrics);
715                 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
716                     w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
717                 } else {
718                     w = 0;
719                 }
720                 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
721                 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
722                 if (w > 0) {
723                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(
724                             Math.min(w, widthSize), EXACTLY);
725                     fixedWidth = true;
726                 } else {
727                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(
728                             widthSize - mFloatingInsets.left - mFloatingInsets.right,
729                             AT_MOST);
730                     mApplyFloatingHorizontalInsets = true;
731                 }
732             }
733         }
734 
735         mApplyFloatingVerticalInsets = false;
736         if (heightMode == AT_MOST) {
737             final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
738                     : mWindow.mFixedHeightMinor;
739             if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
740                 final int h;
741                 if (tvh.type == TypedValue.TYPE_DIMENSION) {
742                     h = (int) tvh.getDimension(metrics);
743                 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
744                     h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
745                 } else {
746                     h = 0;
747                 }
748                 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
749                 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
750                 if (h > 0) {
751                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(
752                             Math.min(h, heightSize), EXACTLY);
753                 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
754                     heightMeasureSpec = MeasureSpec.makeMeasureSpec(
755                             heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
756                     mApplyFloatingVerticalInsets = true;
757                 }
758             }
759         }
760 
761         super.onMeasure(widthMeasureSpec, heightMeasureSpec);
762 
763         int width = getMeasuredWidth();
764         boolean measure = false;
765 
766         widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
767 
768         if (!fixedWidth && widthMode == AT_MOST) {
769             final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
770             if (tv.type != TypedValue.TYPE_NULL) {
771                 final int min;
772                 if (tv.type == TypedValue.TYPE_DIMENSION) {
773                     min = (int)tv.getDimension(metrics);
774                 } else if (tv.type == TypedValue.TYPE_FRACTION) {
775                     min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
776                 } else {
777                     min = 0;
778                 }
779                 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
780                         + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
781 
782                 if (width < min) {
783                     widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
784                     measure = true;
785                 }
786             }
787         }
788 
789         // TODO: Support height?
790 
791         if (measure) {
792             super.onMeasure(widthMeasureSpec, heightMeasureSpec);
793         }
794     }
795 
796     @Override
797     protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
798         super.onLayout(changed, left, top, right, bottom);
799         if (mApplyFloatingVerticalInsets) {
800             offsetTopAndBottom(mFloatingInsets.top);
801         }
802         if (mApplyFloatingHorizontalInsets) {
803             offsetLeftAndRight(mFloatingInsets.left);
804         }
805 
806         // If the application changed its SystemUI metrics, we might also have to adapt
807         // our shadow elevation.
808         updateElevation();
809         mAllowUpdateElevation = true;
810 
811         if (changed
812                 && (mResizeMode == RESIZE_MODE_DOCKED_DIVIDER
813                     || mDrawLegacyNavigationBarBackground)) {
814             getViewRootImpl().requestInvalidateRootRenderNode();
815         }
816     }
817 
818     @Override
819     public void draw(Canvas canvas) {
820         super.draw(canvas);
821 
822         if (mMenuBackground != null) {
823             mMenuBackground.draw(canvas);
824         }
825     }
826 
827     @Override
828     public boolean showContextMenuForChild(View originalView) {
829         return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
830     }
831 
832     @Override
833     public boolean showContextMenuForChild(View originalView, float x, float y) {
834         return showContextMenuForChildInternal(originalView, x, y);
835     }
836 
837     private boolean showContextMenuForChildInternal(View originalView,
838             float x, float y) {
839         // Only allow one context menu at a time.
840         if (mWindow.mContextMenuHelper != null) {
841             mWindow.mContextMenuHelper.dismiss();
842             mWindow.mContextMenuHelper = null;
843         }
844 
845         // Reuse the context menu builder.
846         final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
847         if (mWindow.mContextMenu == null) {
848             mWindow.mContextMenu = new ContextMenuBuilder(getContext());
849             mWindow.mContextMenu.setCallback(callback);
850         } else {
851             mWindow.mContextMenu.clearAll();
852         }
853 
854         final MenuHelper helper;
855         final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
856         if (isPopup) {
857             helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
858         } else {
859             helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
860         }
861 
862         if (helper != null) {
863             // If it's a dialog, the callback needs to handle showing
864             // sub-menus. Either way, the callback is required for propagating
865             // selection to Context.onContextMenuItemSelected().
866             callback.setShowDialogForSubmenu(!isPopup);
867             helper.setPresenterCallback(callback);
868         }
869 
870         mWindow.mContextMenuHelper = helper;
871         return helper != null;
872     }
873 
874     @Override
875     public ActionMode startActionModeForChild(View originalView,
876             ActionMode.Callback callback) {
877         return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
878     }
879 
880     @Override
881     public ActionMode startActionModeForChild(
882             View child, ActionMode.Callback callback, int type) {
883         return startActionMode(child, callback, type);
884     }
885 
886     @Override
887     public ActionMode startActionMode(ActionMode.Callback callback) {
888         return startActionMode(callback, ActionMode.TYPE_PRIMARY);
889     }
890 
891     @Override
892     public ActionMode startActionMode(ActionMode.Callback callback, int type) {
893         return startActionMode(this, callback, type);
894     }
895 
896     private ActionMode startActionMode(
897             View originatingView, ActionMode.Callback callback, int type) {
898         ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
899         ActionMode mode = null;
900         if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
901             try {
902                 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
903             } catch (AbstractMethodError ame) {
904                 // Older apps might not implement the typed version of this method.
905                 if (type == ActionMode.TYPE_PRIMARY) {
906                     try {
907                         mode = mWindow.getCallback().onWindowStartingActionMode(
908                                 wrappedCallback);
909                     } catch (AbstractMethodError ame2) {
910                         // Older apps might not implement this callback method at all.
911                     }
912                 }
913             }
914         }
915         if (mode != null) {
916             if (mode.getType() == ActionMode.TYPE_PRIMARY) {
917                 cleanupPrimaryActionMode();
918                 mPrimaryActionMode = mode;
919             } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
920                 if (mFloatingActionMode != null) {
921                     mFloatingActionMode.finish();
922                 }
923                 mFloatingActionMode = mode;
924             }
925         } else {
926             mode = createActionMode(type, wrappedCallback, originatingView);
927             if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
928                 setHandledActionMode(mode);
929             } else {
930                 mode = null;
931             }
932         }
933         if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
934             try {
935                 mWindow.getCallback().onActionModeStarted(mode);
936             } catch (AbstractMethodError ame) {
937                 // Older apps might not implement this callback method.
938             }
939         }
940         return mode;
941     }
942 
943     private void cleanupPrimaryActionMode() {
944         if (mPrimaryActionMode != null) {
945             mPrimaryActionMode.finish();
946             mPrimaryActionMode = null;
947         }
948         if (mPrimaryActionModeView != null) {
949             mPrimaryActionModeView.killMode();
950         }
951     }
952 
953     private void cleanupFloatingActionModeViews() {
954         if (mFloatingToolbar != null) {
955             mFloatingToolbar.dismiss();
956             mFloatingToolbar = null;
957         }
958         if (mFloatingActionModeOriginatingView != null) {
959             if (mFloatingToolbarPreDrawListener != null) {
960                 mFloatingActionModeOriginatingView.getViewTreeObserver()
961                     .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
962                 mFloatingToolbarPreDrawListener = null;
963             }
964             mFloatingActionModeOriginatingView = null;
965         }
966     }
967 
968     void startChanging() {
969         mChanging = true;
970     }
971 
972     void finishChanging() {
973         mChanging = false;
974         drawableChanged();
975     }
976 
977     public void setWindowBackground(Drawable drawable) {
978         if (mWindow == null) {
979             mPendingWindowBackground = drawable;
980             return;
981         }
982         if (mOriginalBackgroundDrawable != drawable) {
983             mOriginalBackgroundDrawable = drawable;
984             updateBackgroundDrawable();
985             if (drawable != null) {
986                 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
987                         mWindow.isTranslucent() || mWindow.isShowingWallpaper());
988             } else {
989                 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
990                         mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable,
991                         mWindow.isTranslucent() || mWindow.isShowingWallpaper());
992             }
993             if (mResizingBackgroundDrawable != null) {
994                 mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
995             } else {
996                 mBackgroundPadding.setEmpty();
997             }
998             if (!View.sBrokenWindowBackground) {
999                 drawableChanged();
1000             }
1001         }
1002     }
1003 
1004     @Override
1005     public void setBackgroundDrawable(Drawable background) {
1006         setWindowBackground(background);
1007     }
1008 
1009     public void setWindowFrame(Drawable drawable) {
1010         if (getForeground() != drawable) {
1011             setForeground(drawable);
1012             if (drawable != null) {
1013                 drawable.getPadding(mFramePadding);
1014             } else {
1015                 mFramePadding.setEmpty();
1016             }
1017             drawableChanged();
1018         }
1019     }
1020 
1021     @Override
1022     public void onWindowSystemUiVisibilityChanged(int visible) {
1023         updateColorViews(null /* insets */, true /* animate */);
1024         updateDecorCaptionStatus(getResources().getConfiguration());
1025 
1026         if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
1027             updateStatusGuardColor();
1028         }
1029     }
1030 
1031     @Override
1032     public void onSystemBarAppearanceChanged(@WindowInsetsController.Appearance int appearance) {
1033         updateColorViews(null /* insets */, true /* animate */);
1034     }
1035 
1036     @Override
1037     public WindowInsets onApplyWindowInsets(WindowInsets insets) {
1038         final WindowManager.LayoutParams attrs = mWindow.getAttributes();
1039         mFloatingInsets.setEmpty();
1040         if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
1041             // For dialog windows we want to make sure they don't go over the status bar or nav bar.
1042             // We consume the system insets and we will reuse them later during the measure phase.
1043             // We allow the app to ignore this and handle insets itself by using
1044             // FLAG_LAYOUT_IN_SCREEN.
1045             if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
1046                 mFloatingInsets.top = insets.getSystemWindowInsetTop();
1047                 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
1048                 insets = insets.inset(0, insets.getSystemWindowInsetTop(),
1049                         0, insets.getSystemWindowInsetBottom());
1050             }
1051             if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
1052                 mFloatingInsets.left = insets.getSystemWindowInsetTop();
1053                 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
1054                 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
1055                         insets.getSystemWindowInsetRight(), 0);
1056             }
1057         }
1058         mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
1059         insets = updateColorViews(insets, true /* animate */);
1060         insets = updateStatusGuard(insets);
1061         if (getForeground() != null) {
1062             drawableChanged();
1063         }
1064         return insets;
1065     }
1066 
1067     @Override
1068     public boolean isTransitionGroup() {
1069         return false;
1070     }
1071 
1072     public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
1073         return bottomInset == 0 && rightInset > 0;
1074     }
1075 
1076     public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
1077         return bottomInset == 0 && leftInset > 0;
1078     }
1079 
getNavBarSize(int bottomInset, int rightInset, int leftInset)1080     public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
1081         return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1082                 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
1083     }
1084 
getNavigationBarRect(int canvasWidth, int canvasHeight, Rect systemBarInsets, Rect outRect, float scale)1085     public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect systemBarInsets,
1086             Rect outRect, float scale) {
1087         final int bottomInset = (int) (systemBarInsets.bottom * scale);
1088         final int leftInset = (int) (systemBarInsets.left * scale);
1089         final int rightInset = (int) (systemBarInsets.right * scale);
1090         final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1091         if (isNavBarToRightEdge(bottomInset, rightInset)) {
1092             outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1093         } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1094             outRect.set(0, 0, size, canvasHeight);
1095         } else {
1096             outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1097         }
1098     }
1099 
updateColorViews(WindowInsets insets, boolean animate)1100     WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1101         WindowManager.LayoutParams attrs = mWindow.getAttributes();
1102         int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1103 
1104         final WindowInsetsController controller = getWindowInsetsController();
1105 
1106         // IME is an exceptional floating window that requires color view.
1107         final boolean isImeWindow =
1108                 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1109         if (!mWindow.mIsFloating || isImeWindow) {
1110             boolean disallowAnimate = !isLaidOut();
1111             disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1112                     & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1113             mLastWindowFlags = attrs.flags;
1114 
1115             final ViewRootImpl viewRoot = getViewRootImpl();
1116             final @Appearance int appearance = viewRoot != null
1117                     ? viewRoot.mWindowAttributes.insetsFlags.appearance
1118                     : controller.getSystemBarsAppearance();
1119 
1120             if (insets != null) {
1121                 final boolean clearCompatInsets = clearCompatInsets(attrs.type, attrs.flags,
1122                         getResources().getConfiguration().windowConfiguration.getWindowingMode());
1123                 final Insets stableBarInsets = insets.getInsetsIgnoringVisibility(
1124                         WindowInsets.Type.systemBars());
1125                 final Insets systemInsets = clearCompatInsets
1126                         ? Insets.NONE
1127                         : Insets.min(insets.getInsets(WindowInsets.Type.systemBars()
1128                                 | WindowInsets.Type.displayCutout()), stableBarInsets);
1129                 mLastTopInset = systemInsets.top;
1130                 mLastBottomInset = systemInsets.bottom;
1131                 mLastRightInset = systemInsets.right;
1132                 mLastLeftInset = systemInsets.left;
1133 
1134                 // Don't animate if the presence of stable insets has changed, because that
1135                 // indicates that the window was either just added and received them for the
1136                 // first time, or the window size or position has changed.
1137                 boolean hasTopStableInset = stableBarInsets.top != 0;
1138                 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1139                 mLastHasTopStableInset = hasTopStableInset;
1140 
1141                 boolean hasBottomStableInset = stableBarInsets.bottom != 0;
1142                 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1143                 mLastHasBottomStableInset = hasBottomStableInset;
1144 
1145                 boolean hasRightStableInset = stableBarInsets.right != 0;
1146                 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1147                 mLastHasRightStableInset = hasRightStableInset;
1148 
1149                 boolean hasLeftStableInset = stableBarInsets.left != 0;
1150                 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1151                 mLastHasLeftStableInset = hasLeftStableInset;
1152 
1153                 mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
1154             }
1155 
1156             boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
1157             boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1158             int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
1159             updateColorViewInt(mNavigationColorViewState, calculateNavigationBarColor(appearance),
1160                     mWindow.mNavigationBarDividerColor, navBarSize,
1161                     navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
1162                     0 /* sideInset */, animate && !disallowAnimate,
1163                     mForceWindowDrawsBarBackgrounds, controller);
1164             boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
1165             mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
1166                     && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
1167             if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
1168                 if (viewRoot != null) {
1169                     viewRoot.requestInvalidateRootRenderNode();
1170                 }
1171             }
1172 
1173             boolean statusBarNeedsRightInset = navBarToRightEdge
1174                     && mNavigationColorViewState.present;
1175             boolean statusBarNeedsLeftInset = navBarToLeftEdge
1176                     && mNavigationColorViewState.present;
1177             int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1178                     : statusBarNeedsLeftInset ? mLastLeftInset : 0;
1179             int statusBarColor = calculateStatusBarColor(appearance);
1180             updateColorViewInt(mStatusColorViewState, statusBarColor, 0,
1181                     mLastTopInset, false /* matchVertical */, statusBarNeedsLeftInset,
1182                     statusBarSideInset, animate && !disallowAnimate,
1183                     mForceWindowDrawsBarBackgrounds, controller);
1184 
1185             if (mHasCaption) {
1186                 mDecorCaptionView.getCaption().setBackgroundColor(statusBarColor);
1187                 updateDecorCaptionShade();
1188             }
1189         }
1190 
1191         // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
1192         // mForceWindowDrawsBarBackgrounds, we still need to ensure that the rest of the view
1193         // hierarchy doesn't notice it, unless they've explicitly asked for it.
1194         //
1195         // Note: We don't need to check for IN_SCREEN or INSET_DECOR because unlike the status bar,
1196         // these flags wouldn't make the window draw behind the navigation bar, unless
1197         // LAYOUT_HIDE_NAVIGATION was set.
1198         //
1199         // Note: Once the app uses the R+ Window.setDecorFitsSystemWindows(false) API we no longer
1200         // consume insets because they might no longer set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.
1201         boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
1202                 || !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
1203         boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows;
1204         boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
1205                         && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1206                         && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1207                         && decorFitsSystemWindows
1208                         && !hideNavigation)
1209                 || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
1210 
1211         boolean consumingNavBar =
1212                 ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1213                         && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1214                         && decorFitsSystemWindows
1215                         && !hideNavigation)
1216                 || forceConsumingNavBar;
1217 
1218         // If we didn't request fullscreen layout, but we still got it because of the
1219         // mForceWindowDrawsBarBackgrounds flag, also consume top inset.
1220         // If we should always consume system bars, only consume that if the app wanted to go to
1221         // fullscreen, as othrewise we can expect the app to handle it.
1222         boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
1223                 || (attrs.flags & FLAG_FULLSCREEN) != 0
1224                 || !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR));
1225         boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1226                 && decorFitsSystemWindows
1227                 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1228                 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1229                 && mForceWindowDrawsBarBackgrounds
1230                 && mLastTopInset != 0
1231                 || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
1232 
1233         int consumedTop = consumingStatusBar ? mLastTopInset : 0;
1234         int consumedRight = consumingNavBar ? mLastRightInset : 0;
1235         int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
1236         int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
1237 
1238         if (mContentRoot != null
1239                 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1240             MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
1241             if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
1242                     || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
1243                 lp.topMargin = consumedTop;
1244                 lp.rightMargin = consumedRight;
1245                 lp.bottomMargin = consumedBottom;
1246                 lp.leftMargin = consumedLeft;
1247                 mContentRoot.setLayoutParams(lp);
1248 
1249                 if (insets == null) {
1250                     // The insets have changed, but we're not currently in the process
1251                     // of dispatching them.
1252                     requestApplyInsets();
1253                 }
1254             }
1255             if (insets != null) {
1256                 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
1257             }
1258         }
1259 
1260         if (forceConsumingNavBar) {
1261             mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
1262         } else {
1263             mBackgroundInsets = Insets.NONE;
1264         }
1265         updateBackgroundDrawable();
1266 
1267         return insets;
1268     }
1269 
1270     /**
1271      * Updates the background drawable, applying padding to it in case we {@link #mBackgroundInsets}
1272      * are set.
1273      */
updateBackgroundDrawable()1274     private void updateBackgroundDrawable() {
1275         // Background insets can be null if super constructor calls setBackgroundDrawable.
1276         if (mBackgroundInsets == null) {
1277             mBackgroundInsets = Insets.NONE;
1278         }
1279 
1280         if (mBackgroundInsets.equals(mLastBackgroundInsets)
1281                 && mBackgroundBlurDrawable == mLastBackgroundBlurDrawable
1282                 && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
1283             return;
1284         }
1285 
1286         Drawable destDrawable = mOriginalBackgroundDrawable;
1287         if (mBackgroundBlurDrawable != null) {
1288             destDrawable = new LayerDrawable(new Drawable[] {mBackgroundBlurDrawable,
1289                                                              mOriginalBackgroundDrawable});
1290         }
1291 
1292         if (destDrawable != null && !mBackgroundInsets.equals(Insets.NONE)) {
1293             destDrawable = new InsetDrawable(destDrawable,
1294                     mBackgroundInsets.left, mBackgroundInsets.top,
1295                     mBackgroundInsets.right, mBackgroundInsets.bottom) {
1296 
1297                 /**
1298                  * Return inner padding so we don't apply the padding again in
1299                  * {@link DecorView#drawableChanged()}
1300                  */
1301                 @Override
1302                 public boolean getPadding(Rect padding) {
1303                     return getDrawable().getPadding(padding);
1304                 }
1305             };
1306         }
1307 
1308         // Call super since we are intercepting setBackground on this class.
1309         super.setBackgroundDrawable(destDrawable);
1310 
1311         mLastBackgroundInsets = mBackgroundInsets;
1312         mLastBackgroundBlurDrawable = mBackgroundBlurDrawable;
1313         mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
1314     }
1315 
updateBackgroundBlurCorners()1316     private void updateBackgroundBlurCorners() {
1317         if (mBackgroundBlurDrawable == null) return;
1318 
1319         float cornerRadius = 0;
1320         // If the blur radius is 0, the blur region won't be sent to surface flinger, so we don't
1321         // need to calculate the corner radius.
1322         if (mBackgroundBlurRadius != 0 && mOriginalBackgroundDrawable != null) {
1323             final Outline outline = new Outline();
1324             mOriginalBackgroundDrawable.getOutline(outline);
1325             cornerRadius = outline.mMode == Outline.MODE_ROUND_RECT ? outline.getRadius() : 0;
1326         }
1327         mBackgroundBlurDrawable.setCornerRadius(cornerRadius);
1328     }
1329 
updateBackgroundBlurRadius()1330     private void updateBackgroundBlurRadius() {
1331         if (getViewRootImpl() == null) return;
1332 
1333         mBackgroundBlurRadius = mCrossWindowBlurEnabled && mWindow.isTranslucent()
1334                 ? mOriginalBackgroundBlurRadius : 0;
1335         if (mBackgroundBlurDrawable == null && mBackgroundBlurRadius > 0) {
1336             mBackgroundBlurDrawable = getViewRootImpl().createBackgroundBlurDrawable();
1337             updateBackgroundDrawable();
1338         }
1339 
1340         if (mBackgroundBlurDrawable != null) {
1341             mBackgroundBlurDrawable.setBlurRadius(mBackgroundBlurRadius);
1342         }
1343     }
1344 
setBackgroundBlurRadius(int blurRadius)1345     void setBackgroundBlurRadius(int blurRadius) {
1346         mOriginalBackgroundBlurRadius = blurRadius;
1347         if (blurRadius > 0) {
1348             if (mCrossWindowBlurEnabledListener == null) {
1349                 mCrossWindowBlurEnabledListener = enabled -> {
1350                     mCrossWindowBlurEnabled = enabled;
1351                     updateBackgroundBlurRadius();
1352                 };
1353                 getContext().getSystemService(WindowManager.class)
1354                         .addCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
1355                 getViewTreeObserver().addOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
1356             } else {
1357                 updateBackgroundBlurRadius();
1358             }
1359         } else if (mCrossWindowBlurEnabledListener != null) {
1360             updateBackgroundBlurRadius();
1361             removeBackgroundBlurDrawable();
1362         }
1363     }
1364 
removeBackgroundBlurDrawable()1365     void removeBackgroundBlurDrawable() {
1366         if (mCrossWindowBlurEnabledListener != null) {
1367             getContext().getSystemService(WindowManager.class)
1368                     .removeCrossWindowBlurEnabledListener(mCrossWindowBlurEnabledListener);
1369             mCrossWindowBlurEnabledListener = null;
1370         }
1371         getViewTreeObserver().removeOnPreDrawListener(mBackgroundBlurOnPreDrawListener);
1372         mBackgroundBlurDrawable = null;
1373         updateBackgroundDrawable();
1374     }
1375 
1376     @Override
getBackground()1377     public Drawable getBackground() {
1378         return mOriginalBackgroundDrawable;
1379     }
1380 
calculateStatusBarColor(@ppearance int appearance)1381     private int calculateStatusBarColor(@Appearance int appearance) {
1382         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
1383                 mSemiTransparentBarColor, mWindow.mStatusBarColor,
1384                 appearance, APPEARANCE_LIGHT_STATUS_BARS,
1385                 mWindow.mEnsureStatusBarContrastWhenTransparent);
1386     }
1387 
calculateNavigationBarColor(@ppearance int appearance)1388     private int calculateNavigationBarColor(@Appearance int appearance) {
1389         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
1390                 mSemiTransparentBarColor, mWindow.mNavigationBarColor,
1391                 appearance, APPEARANCE_LIGHT_NAVIGATION_BARS,
1392                 mWindow.mEnsureNavigationBarContrastWhenTransparent
1393                         && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
1394     }
1395 
calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor, int barColor, @Appearance int appearance, @Appearance int lightAppearanceFlag, boolean scrimTransparent)1396     public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
1397             int barColor, @Appearance int appearance, @Appearance int lightAppearanceFlag,
1398             boolean scrimTransparent) {
1399         if ((flags & translucentFlag) != 0) {
1400             return semiTransparentBarColor;
1401         } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
1402             return Color.BLACK;
1403         } else if (scrimTransparent && Color.alpha(barColor) == 0) {
1404             boolean light = (appearance & lightAppearanceFlag) != 0;
1405             return light ? SCRIM_LIGHT : semiTransparentBarColor;
1406         } else {
1407             return barColor;
1408         }
1409     }
1410 
getCurrentColor(ColorViewState state)1411     private int getCurrentColor(ColorViewState state) {
1412         if (state.visible) {
1413             return state.color;
1414         } else {
1415             return 0;
1416         }
1417     }
1418 
1419     /**
1420      * Update a color view
1421      *
1422      * @param state the color view to update.
1423      * @param color the current color to apply.
1424      * @param dividerColor the current divider color to apply.
1425      * @param size the current size in the non-parent-matching dimension.
1426      * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1427      *                    horizontal edge,
1428      * @param sideMargin sideMargin for the color view.
1429      * @param animate if true, the change will be animated.
1430      */
updateColorViewInt(final ColorViewState state, int color, int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force, WindowInsetsController controller)1431     private void updateColorViewInt(final ColorViewState state, int color, int dividerColor,
1432             int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate,
1433             boolean force, WindowInsetsController controller) {
1434         state.present = state.attributes.isPresent(
1435                         controller.isRequestedVisible(state.attributes.insetsType),
1436                         mWindow.getAttributes().flags, force);
1437         boolean show = state.attributes.isVisible(state.present, color,
1438                 mWindow.getAttributes().flags, force);
1439         boolean showView = show && !isResizing() && !mHasCaption && size > 0;
1440 
1441         boolean visibilityChanged = false;
1442         View view = state.view;
1443 
1444         int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1445         int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
1446         int resolvedGravity = verticalBar
1447                 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1448                 : state.attributes.verticalGravity;
1449 
1450         if (view == null) {
1451             if (showView) {
1452                 state.view = view = new View(mContext);
1453                 setColor(view, color, dividerColor, verticalBar, seascape);
1454                 view.setTransitionName(state.attributes.transitionName);
1455                 view.setId(state.attributes.id);
1456                 visibilityChanged = true;
1457                 view.setVisibility(INVISIBLE);
1458                 state.targetVisibility = VISIBLE;
1459 
1460                 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1461                         resolvedGravity);
1462                 if (seascape) {
1463                     lp.leftMargin = sideMargin;
1464                 } else {
1465                     lp.rightMargin = sideMargin;
1466                 }
1467                 addView(view, lp);
1468                 updateColorViewTranslations();
1469             }
1470         } else {
1471             int vis = showView ? VISIBLE : INVISIBLE;
1472             visibilityChanged = state.targetVisibility != vis;
1473             state.targetVisibility = vis;
1474             LayoutParams lp = (LayoutParams) view.getLayoutParams();
1475             int rightMargin = seascape ? 0 : sideMargin;
1476             int leftMargin = seascape ? sideMargin : 0;
1477             if (lp.height != resolvedHeight || lp.width != resolvedWidth
1478                     || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1479                     || lp.leftMargin != leftMargin) {
1480                 lp.height = resolvedHeight;
1481                 lp.width = resolvedWidth;
1482                 lp.gravity = resolvedGravity;
1483                 lp.rightMargin = rightMargin;
1484                 lp.leftMargin = leftMargin;
1485                 view.setLayoutParams(lp);
1486             }
1487             if (showView) {
1488                 setColor(view, color, dividerColor, verticalBar, seascape);
1489             }
1490         }
1491         if (visibilityChanged) {
1492             view.animate().cancel();
1493             if (animate && !isResizing()) {
1494                 if (showView) {
1495                     if (view.getVisibility() != VISIBLE) {
1496                         view.setVisibility(VISIBLE);
1497                         view.setAlpha(0.0f);
1498                     }
1499                     view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1500                             setDuration(mBarEnterExitDuration);
1501                 } else {
1502                     view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1503                             .setDuration(mBarEnterExitDuration)
1504                             .withEndAction(new Runnable() {
1505                                 @Override
1506                                 public void run() {
1507                                     state.view.setAlpha(1.0f);
1508                                     state.view.setVisibility(INVISIBLE);
1509                                 }
1510                             });
1511                 }
1512             } else {
1513                 view.setAlpha(1.0f);
1514                 view.setVisibility(showView ? VISIBLE : INVISIBLE);
1515             }
1516         }
1517         state.visible = show;
1518         state.color = color;
1519     }
1520 
setColor(View v, int color, int dividerColor, boolean verticalBar, boolean seascape)1521     private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1522             boolean seascape) {
1523         if (dividerColor != 0) {
1524             final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1525             if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1526                 final int size = Math.round(
1527                         TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1528                                 v.getContext().getResources().getDisplayMetrics()));
1529                 // Use an inset to make the divider line on the side that faces the app.
1530                 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1531                         verticalBar && !seascape ? size : 0,
1532                         !verticalBar ? size : 0,
1533                         verticalBar && seascape ? size : 0, 0);
1534                 v.setBackground(new LayerDrawable(new Drawable[] {
1535                         new ColorDrawable(dividerColor), d }));
1536                 v.setTag(new Pair<>(verticalBar, seascape));
1537             } else {
1538                 final LayerDrawable d = (LayerDrawable) v.getBackground();
1539                 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1540                 ((ColorDrawable) inset.getDrawable()).setColor(color);
1541                 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1542             }
1543         } else {
1544             v.setTag(null);
1545             v.setBackgroundColor(color);
1546         }
1547     }
1548 
updateColorViewTranslations()1549     private void updateColorViewTranslations() {
1550         // Put the color views back in place when they get moved off the screen
1551         // due to the the ViewRootImpl panning.
1552         int rootScrollY = mRootScrollY;
1553         if (mStatusColorViewState.view != null) {
1554             mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1555         }
1556         if (mNavigationColorViewState.view != null) {
1557             mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1558         }
1559     }
1560 
1561     private WindowInsets updateStatusGuard(WindowInsets insets) {
1562         boolean showStatusGuard = false;
1563         // Show the status guard when the non-overlay contextual action bar is showing
1564         if (mPrimaryActionModeView != null) {
1565             if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1566                 // Insets are magic!
1567                 final MarginLayoutParams mlp = (MarginLayoutParams)
1568                         mPrimaryActionModeView.getLayoutParams();
1569                 boolean mlpChanged = false;
1570                 if (mPrimaryActionModeView.isShown()) {
1571                     if (mTempRect == null) {
1572                         mTempRect = new Rect();
1573                     }
1574                     final Rect rect = mTempRect;
1575 
1576                     // Apply the insets that have not been applied by the contentParent yet.
1577                     WindowInsets innerInsets =
1578                             mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1579                     int newTopMargin = innerInsets.getSystemWindowInsetTop();
1580                     int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
1581                     int newRightMargin = innerInsets.getSystemWindowInsetRight();
1582 
1583                     // Must use root window insets for the guard, because the color views consume
1584                     // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
1585                     // the status guard is attached at the root.
1586                     WindowInsets rootInsets = getRootWindowInsets();
1587                     int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
1588                     int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
1589 
1590                     if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
1591                             || mlp.rightMargin != newRightMargin) {
1592                         mlpChanged = true;
1593                         mlp.topMargin = newTopMargin;
1594                         mlp.leftMargin = newLeftMargin;
1595                         mlp.rightMargin = newRightMargin;
1596                     }
1597 
1598                     if (newTopMargin > 0 && mStatusGuard == null) {
1599                         mStatusGuard = new View(mContext);
1600                         mStatusGuard.setVisibility(GONE);
1601                         final LayoutParams lp = new LayoutParams(MATCH_PARENT,
1602                                 mlp.topMargin, Gravity.LEFT | Gravity.TOP);
1603                         lp.leftMargin = newGuardLeftMargin;
1604                         lp.rightMargin = newGuardRightMargin;
1605                         addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
1606                     } else if (mStatusGuard != null) {
1607                         final LayoutParams lp = (LayoutParams)
1608                                 mStatusGuard.getLayoutParams();
1609                         if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
1610                                 || lp.rightMargin != newGuardRightMargin) {
1611                             lp.height = mlp.topMargin;
1612                             lp.leftMargin = newGuardLeftMargin;
1613                             lp.rightMargin = newGuardRightMargin;
1614                             mStatusGuard.setLayoutParams(lp);
1615                         }
1616                     }
1617 
1618                     // The action mode's theme may differ from the app, so
1619                     // always show the status guard above it if we have one.
1620                     showStatusGuard = mStatusGuard != null;
1621 
1622                     if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
1623                         // If it wasn't previously shown, the color may be stale
1624                         updateStatusGuardColor();
1625                     }
1626 
1627                     // We only need to consume the insets if the action
1628                     // mode is overlaid on the app content (e.g. it's
1629                     // sitting in a FrameLayout, see
1630                     // screen_simple_overlay_action_mode.xml).
1631                     final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1632                             & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1633                     if (nonOverlay && showStatusGuard) {
1634                         insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1635                     }
1636                 } else {
1637                     // reset top margin
1638                     if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
1639                         mlpChanged = true;
1640                         mlp.topMargin = 0;
1641                     }
1642                 }
1643                 if (mlpChanged) {
1644                     mPrimaryActionModeView.setLayoutParams(mlp);
1645                 }
1646             }
1647         }
1648         if (mStatusGuard != null) {
1649             mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
1650         }
1651         return insets;
1652     }
1653 
updateStatusGuardColor()1654     private void updateStatusGuardColor() {
1655         boolean lightStatusBar =
1656                 (getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
1657         mStatusGuard.setBackgroundColor(lightStatusBar
1658                 ? mContext.getColor(R.color.decor_view_status_guard_light)
1659                 : mContext.getColor(R.color.decor_view_status_guard));
1660     }
1661 
1662     /**
1663      * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1664      * an opaque shadow even if the window background is completely transparent. This only applies
1665      * to activities that are currently the task root.
1666      */
updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode)1667     public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1668         if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1669             return;
1670         }
1671 
1672         if (isInPictureInPictureMode) {
1673             final Window.WindowControllerCallback callback =
1674                     mWindow.getWindowControllerCallback();
1675             if (callback != null && callback.isTaskRoot()) {
1676                 // Call super implementation directly as we don't want to save the PIP outline
1677                 // provider to be restored
1678                 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1679             }
1680         } else {
1681             // Restore the previous outline provider
1682             if (getOutlineProvider() != mLastOutlineProvider) {
1683                 setOutlineProvider(mLastOutlineProvider);
1684             }
1685         }
1686         mIsInPictureInPictureMode = isInPictureInPictureMode;
1687     }
1688 
1689     @Override
setOutlineProvider(ViewOutlineProvider provider)1690     public void setOutlineProvider(ViewOutlineProvider provider) {
1691         super.setOutlineProvider(provider);
1692 
1693         // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1694         mLastOutlineProvider = provider;
1695     }
1696 
drawableChanged()1697     private void drawableChanged() {
1698         if (mChanging) {
1699             return;
1700         }
1701 
1702         // Fields can be null if super constructor calls setBackgroundDrawable.
1703         Rect framePadding = mFramePadding != null ? mFramePadding : new Rect();
1704         Rect backgroundPadding = mBackgroundPadding != null ? mBackgroundPadding : new Rect();
1705 
1706         setPadding(framePadding.left + backgroundPadding.left,
1707                 framePadding.top + backgroundPadding.top,
1708                 framePadding.right + backgroundPadding.right,
1709                 framePadding.bottom + backgroundPadding.bottom);
1710         requestLayout();
1711         invalidate();
1712 
1713         int opacity = PixelFormat.OPAQUE;
1714         final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1715         final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor;
1716         // If we draw shadows in the compositor we don't need to force the surface to be
1717         // translucent.
1718         if (winConfig.hasWindowShadow() && !renderShadowsInCompositor) {
1719             // If the window has a shadow, it must be translucent.
1720             opacity = PixelFormat.TRANSLUCENT;
1721         } else{
1722             // Note: If there is no background, we will assume opaque. The
1723             // common case seems to be that an application sets there to be
1724             // no background so it can draw everything itself. For that,
1725             // we would like to assume OPAQUE and let the app force it to
1726             // the slower TRANSLUCENT mode if that is really what it wants.
1727             Drawable bg = getBackground();
1728             Drawable fg = getForeground();
1729             if (bg != null) {
1730                 if (fg == null) {
1731                     opacity = bg.getOpacity();
1732                 } else if (framePadding.left <= 0 && framePadding.top <= 0
1733                         && framePadding.right <= 0 && framePadding.bottom <= 0) {
1734                     // If the frame padding is zero, then we can be opaque
1735                     // if either the frame -or- the background is opaque.
1736                     int fop = fg.getOpacity();
1737                     int bop = bg.getOpacity();
1738                     if (false)
1739                         Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
1740                     if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1741                         opacity = PixelFormat.OPAQUE;
1742                     } else if (fop == PixelFormat.UNKNOWN) {
1743                         opacity = bop;
1744                     } else if (bop == PixelFormat.UNKNOWN) {
1745                         opacity = fop;
1746                     } else {
1747                         opacity = Drawable.resolveOpacity(fop, bop);
1748                     }
1749                 } else {
1750                     // For now we have to assume translucent if there is a
1751                     // frame with padding... there is no way to tell if the
1752                     // frame and background together will draw all pixels.
1753                     if (false)
1754                         Log.v(mLogTag, "Padding: " + mFramePadding);
1755                     opacity = PixelFormat.TRANSLUCENT;
1756                 }
1757             }
1758             if (false)
1759                 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
1760         }
1761 
1762         if (false)
1763             Log.v(mLogTag, "Selected default opacity: " + opacity);
1764 
1765         mDefaultOpacity = opacity;
1766         if (mFeatureId < 0) {
1767             mWindow.setDefaultWindowFormat(opacity);
1768         }
1769     }
1770 
1771     @Override
onWindowFocusChanged(boolean hasWindowFocus)1772     public void onWindowFocusChanged(boolean hasWindowFocus) {
1773         super.onWindowFocusChanged(hasWindowFocus);
1774 
1775         // If the user is chording a menu shortcut, release the chord since
1776         // this window lost focus
1777         if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1778                 && mWindow.mPanelChordingKey != 0) {
1779             mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1780         }
1781 
1782         final Window.Callback cb = mWindow.getCallback();
1783         if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1784             cb.onWindowFocusChanged(hasWindowFocus);
1785         }
1786 
1787         if (mPrimaryActionMode != null) {
1788             mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1789         }
1790         if (mFloatingActionMode != null) {
1791             mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1792         }
1793 
1794         updateElevation();
1795     }
1796 
1797     @Override
onAttachedToWindow()1798     protected void onAttachedToWindow() {
1799         super.onAttachedToWindow();
1800 
1801         final Window.Callback cb = mWindow.getCallback();
1802         if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1803             cb.onAttachedToWindow();
1804         }
1805 
1806         if (mFeatureId == -1) {
1807             /*
1808              * The main window has been attached, try to restore any panels
1809              * that may have been open before. This is called in cases where
1810              * an activity is being killed for configuration change and the
1811              * menu was open. When the activity is recreated, the menu
1812              * should be shown again.
1813              */
1814             mWindow.openPanelsAfterRestore();
1815         }
1816 
1817         if (!mWindowResizeCallbacksAdded) {
1818             // If there is no window callback installed there was no window set before. Set it now.
1819             // Note that our ViewRootImpl object will not change.
1820             getViewRootImpl().addWindowCallbacks(this);
1821             mWindowResizeCallbacksAdded = true;
1822         } else if (mBackdropFrameRenderer != null) {
1823             // We are resizing and this call happened due to a configuration change. Tell the
1824             // renderer about it.
1825             mBackdropFrameRenderer.onConfigurationChange();
1826         }
1827 
1828         updateBackgroundBlurRadius();
1829 
1830         mWindow.onViewRootImplSet(getViewRootImpl());
1831     }
1832 
1833     @Override
onDetachedFromWindow()1834     protected void onDetachedFromWindow() {
1835         super.onDetachedFromWindow();
1836 
1837         final Window.Callback cb = mWindow.getCallback();
1838         if (cb != null && mFeatureId < 0) {
1839             cb.onDetachedFromWindow();
1840         }
1841 
1842         if (mWindow.mDecorContentParent != null) {
1843             mWindow.mDecorContentParent.dismissPopups();
1844         }
1845 
1846         if (mPrimaryActionModePopup != null) {
1847             removeCallbacks(mShowPrimaryActionModePopup);
1848             if (mPrimaryActionModePopup.isShowing()) {
1849                 mPrimaryActionModePopup.dismiss();
1850             }
1851             mPrimaryActionModePopup = null;
1852         }
1853         if (mFloatingToolbar != null) {
1854             mFloatingToolbar.dismiss();
1855             mFloatingToolbar = null;
1856         }
1857 
1858         removeBackgroundBlurDrawable();
1859 
1860         PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1861         if (st != null && st.menu != null && mFeatureId < 0) {
1862             st.menu.close();
1863         }
1864 
1865         releaseThreadedRenderer();
1866 
1867         if (mWindowResizeCallbacksAdded) {
1868             getViewRootImpl().removeWindowCallbacks(this);
1869             mWindowResizeCallbacksAdded = false;
1870         }
1871 
1872         mPendingInsetsController.detach();
1873     }
1874 
1875     @Override
onCloseSystemDialogs(String reason)1876     public void onCloseSystemDialogs(String reason) {
1877         if (mFeatureId >= 0) {
1878             mWindow.closeAllPanels();
1879         }
1880     }
1881 
willYouTakeTheSurface()1882     public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1883         return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1884     }
1885 
willYouTakeTheInputQueue()1886     public InputQueue.Callback willYouTakeTheInputQueue() {
1887         return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1888     }
1889 
setSurfaceType(int type)1890     public void setSurfaceType(int type) {
1891         mWindow.setType(type);
1892     }
1893 
setSurfaceFormat(int format)1894     public void setSurfaceFormat(int format) {
1895         mWindow.setFormat(format);
1896     }
1897 
setSurfaceKeepScreenOn(boolean keepOn)1898     public void setSurfaceKeepScreenOn(boolean keepOn) {
1899         if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1900         else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1901     }
1902 
1903     @Override
onRootViewScrollYChanged(int rootScrollY)1904     public void onRootViewScrollYChanged(int rootScrollY) {
1905         mRootScrollY = rootScrollY;
1906         if (mDecorCaptionView != null) {
1907             mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
1908         }
1909         updateColorViewTranslations();
1910     }
1911 
1912     @Override
providePendingInsetsController()1913     public PendingInsetsController providePendingInsetsController() {
1914         return mPendingInsetsController;
1915     }
1916 
createActionMode( int type, ActionMode.Callback2 callback, View originatingView)1917     private ActionMode createActionMode(
1918             int type, ActionMode.Callback2 callback, View originatingView) {
1919         switch (type) {
1920             case ActionMode.TYPE_PRIMARY:
1921             default:
1922                 return createStandaloneActionMode(callback);
1923             case ActionMode.TYPE_FLOATING:
1924                 return createFloatingActionMode(originatingView, callback);
1925         }
1926     }
1927 
setHandledActionMode(ActionMode mode)1928     private void setHandledActionMode(ActionMode mode) {
1929         if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1930             setHandledPrimaryActionMode(mode);
1931         } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1932             setHandledFloatingActionMode(mode);
1933         }
1934     }
1935 
createStandaloneActionMode(ActionMode.Callback callback)1936     private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1937         endOnGoingFadeAnimation();
1938         cleanupPrimaryActionMode();
1939         // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1940         // instance at all, or if there is one, but it is detached from window. The latter case
1941         // might happen when app is resized in multi-window mode and decor view is preserved
1942         // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1943         // app memory leaks because killMode() is called when the dismiss animation ends and from
1944         // cleanupPrimaryActionMode() invocation above.
1945         if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
1946             if (mWindow.isFloating()) {
1947                 // Use the action bar theme.
1948                 final TypedValue outValue = new TypedValue();
1949                 final Resources.Theme baseTheme = mContext.getTheme();
1950                 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1951 
1952                 final Context actionBarContext;
1953                 if (outValue.resourceId != 0) {
1954                     final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1955                     actionBarTheme.setTo(baseTheme);
1956                     actionBarTheme.applyStyle(outValue.resourceId, true);
1957 
1958                     actionBarContext = new ContextThemeWrapper(mContext, 0);
1959                     actionBarContext.getTheme().setTo(actionBarTheme);
1960                 } else {
1961                     actionBarContext = mContext;
1962                 }
1963 
1964                 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1965                 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1966                         R.attr.actionModePopupWindowStyle);
1967                 mPrimaryActionModePopup.setWindowLayoutType(
1968                         WindowManager.LayoutParams.TYPE_APPLICATION);
1969                 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1970                 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1971 
1972                 actionBarContext.getTheme().resolveAttribute(
1973                         R.attr.actionBarSize, outValue, true);
1974                 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1975                         actionBarContext.getResources().getDisplayMetrics());
1976                 mPrimaryActionModeView.setContentHeight(height);
1977                 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1978                 mShowPrimaryActionModePopup = new Runnable() {
1979                     public void run() {
1980                         mPrimaryActionModePopup.showAtLocation(
1981                                 mPrimaryActionModeView.getApplicationWindowToken(),
1982                                 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1983                         endOnGoingFadeAnimation();
1984 
1985                         if (shouldAnimatePrimaryActionModeView()) {
1986                             mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1987                                     0f, 1f);
1988                             mFadeAnim.addListener(new AnimatorListenerAdapter() {
1989                                 @Override
1990                                 public void onAnimationStart(Animator animation) {
1991                                     mPrimaryActionModeView.setVisibility(VISIBLE);
1992                                 }
1993 
1994                                 @Override
1995                                 public void onAnimationEnd(Animator animation) {
1996                                     mPrimaryActionModeView.setAlpha(1f);
1997                                     mFadeAnim = null;
1998                                 }
1999                             });
2000                             mFadeAnim.start();
2001                         } else {
2002                             mPrimaryActionModeView.setAlpha(1f);
2003                             mPrimaryActionModeView.setVisibility(VISIBLE);
2004                         }
2005                     }
2006                 };
2007             } else {
2008                 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
2009                 if (stub != null) {
2010                     mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
2011                     mPrimaryActionModePopup = null;
2012                 }
2013             }
2014         }
2015         if (mPrimaryActionModeView != null) {
2016             mPrimaryActionModeView.killMode();
2017             ActionMode mode = new StandaloneActionMode(
2018                     mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
2019                     callback, mPrimaryActionModePopup == null);
2020             return mode;
2021         }
2022         return null;
2023     }
2024 
endOnGoingFadeAnimation()2025     private void endOnGoingFadeAnimation() {
2026         if (mFadeAnim != null) {
2027             mFadeAnim.end();
2028         }
2029     }
2030 
setHandledPrimaryActionMode(ActionMode mode)2031     private void setHandledPrimaryActionMode(ActionMode mode) {
2032         endOnGoingFadeAnimation();
2033         mPrimaryActionMode = mode;
2034         mPrimaryActionMode.invalidate();
2035         mPrimaryActionModeView.initForMode(mPrimaryActionMode);
2036         if (mPrimaryActionModePopup != null) {
2037             post(mShowPrimaryActionModePopup);
2038         } else {
2039             if (shouldAnimatePrimaryActionModeView()) {
2040                 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
2041                 mFadeAnim.addListener(new AnimatorListenerAdapter() {
2042                     @Override
2043                     public void onAnimationStart(Animator animation) {
2044                         mPrimaryActionModeView.setVisibility(View.VISIBLE);
2045                     }
2046 
2047                     @Override
2048                     public void onAnimationEnd(Animator animation) {
2049                         mPrimaryActionModeView.setAlpha(1f);
2050                         mFadeAnim = null;
2051                     }
2052                 });
2053                 mFadeAnim.start();
2054             } else {
2055                 mPrimaryActionModeView.setAlpha(1f);
2056                 mPrimaryActionModeView.setVisibility(View.VISIBLE);
2057             }
2058         }
2059         mPrimaryActionModeView.sendAccessibilityEvent(
2060                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
2061     }
2062 
shouldAnimatePrimaryActionModeView()2063     boolean shouldAnimatePrimaryActionModeView() {
2064         // We only to animate the action mode in if the decor has already been laid out.
2065         // If it hasn't been laid out, it hasn't been drawn to screen yet.
2066         return isLaidOut();
2067     }
2068 
createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)2069     private ActionMode createFloatingActionMode(
2070             View originatingView, ActionMode.Callback2 callback) {
2071         if (mFloatingActionMode != null) {
2072             mFloatingActionMode.finish();
2073         }
2074         cleanupFloatingActionModeViews();
2075         mFloatingToolbar = new FloatingToolbar(mWindow);
2076         final FloatingActionMode mode =
2077                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
2078         mFloatingActionModeOriginatingView = originatingView;
2079         mFloatingToolbarPreDrawListener =
2080             new ViewTreeObserver.OnPreDrawListener() {
2081                 @Override
2082                 public boolean onPreDraw() {
2083                     mode.updateViewLocationInWindow();
2084                     return true;
2085                 }
2086             };
2087         return mode;
2088     }
2089 
setHandledFloatingActionMode(ActionMode mode)2090     private void setHandledFloatingActionMode(ActionMode mode) {
2091         mFloatingActionMode = mode;
2092         mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
2093         mFloatingActionModeOriginatingView.getViewTreeObserver()
2094             .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
2095     }
2096 
2097     /**
2098      * Informs the decor if the caption is attached and visible.
2099      * @param attachedAndVisible true when the decor is visible.
2100      * Note that this will even be called if there is no caption.
2101      **/
enableCaption(boolean attachedAndVisible)2102     void enableCaption(boolean attachedAndVisible) {
2103         if (mHasCaption != attachedAndVisible) {
2104             mHasCaption = attachedAndVisible;
2105             if (getForeground() != null) {
2106                 drawableChanged();
2107             }
2108             notifyCaptionHeightChanged();
2109         }
2110     }
2111 
2112     /**
2113      * An interface to be called when the caption visibility or height changed, to report the
2114      * corresponding insets change to the InsetsController.
2115      */
notifyCaptionHeightChanged()2116     public void notifyCaptionHeightChanged() {
2117         getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
2118     }
2119 
setWindow(PhoneWindow phoneWindow)2120     void setWindow(PhoneWindow phoneWindow) {
2121         mWindow = phoneWindow;
2122         Context context = getContext();
2123         if (context instanceof DecorContext) {
2124             DecorContext decorContext = (DecorContext) context;
2125             decorContext.setPhoneWindow(mWindow);
2126         }
2127         if (mPendingWindowBackground != null) {
2128             Drawable background = mPendingWindowBackground;
2129             mPendingWindowBackground = null;
2130             setWindowBackground(background);
2131         }
2132     }
2133 
2134     @Override
getResources()2135     public Resources getResources() {
2136         // Make sure the Resources object is propogated from the Context since it can be updated in
2137         // the Context object.
2138         return getContext().getResources();
2139     }
2140 
2141     @Override
onConfigurationChanged(Configuration newConfig)2142     protected void onConfigurationChanged(Configuration newConfig) {
2143         super.onConfigurationChanged(newConfig);
2144 
2145         updateDecorCaptionStatus(newConfig);
2146 
2147         updateAvailableWidth();
2148         initializeElevation();
2149     }
2150 
2151     @Override
onMovedToDisplay(int displayId, Configuration config)2152     public void onMovedToDisplay(int displayId, Configuration config) {
2153         super.onMovedToDisplay(displayId, config);
2154         // Have to explicitly update displayId because it may use DecorContext
2155         getContext().updateDisplay(displayId);
2156     }
2157 
2158     /**
2159      * Determines if the workspace is entirely covered by the window.
2160      * @return {@code true} when the window is filling the entire screen/workspace.
2161      **/
isFillingScreen(Configuration config)2162     private boolean isFillingScreen(Configuration config) {
2163         final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
2164                 == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
2165         return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
2166                 & View.SYSTEM_UI_FLAG_FULLSCREEN));
2167     }
2168 
updateDecorCaptionStatus(Configuration config)2169     private void updateDecorCaptionStatus(Configuration config) {
2170         final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
2171                 && !isFillingScreen(config);
2172         if (mDecorCaptionView == null && displayWindowDecor) {
2173             // Configuration now requires a caption.
2174             final LayoutInflater inflater = mWindow.getLayoutInflater();
2175             mDecorCaptionView = createDecorCaptionView(inflater);
2176             if (mDecorCaptionView != null) {
2177                 if (mDecorCaptionView.getParent() == null) {
2178                     addView(mDecorCaptionView, 0,
2179                             new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2180                 }
2181                 removeView(mContentRoot);
2182                 mDecorCaptionView.addView(mContentRoot,
2183                         new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
2184             }
2185         } else if (mDecorCaptionView != null) {
2186             // We might have to change the kind of surface before we do anything else.
2187             mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
2188             enableCaption(displayWindowDecor);
2189         }
2190     }
2191 
onResourcesLoaded(LayoutInflater inflater, int layoutResource)2192     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
2193         if (mBackdropFrameRenderer != null) {
2194             loadBackgroundDrawablesIfNeeded();
2195             mBackdropFrameRenderer.onResourcesLoaded(
2196                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2197                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2198                     getCurrentColor(mNavigationColorViewState));
2199         }
2200 
2201         mDecorCaptionView = createDecorCaptionView(inflater);
2202         final View root = inflater.inflate(layoutResource, null);
2203         if (mDecorCaptionView != null) {
2204             if (mDecorCaptionView.getParent() == null) {
2205                 addView(mDecorCaptionView,
2206                         new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2207             }
2208             mDecorCaptionView.addView(root,
2209                     new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
2210         } else {
2211 
2212             // Put it below the color views.
2213             addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2214         }
2215         mContentRoot = (ViewGroup) root;
2216         initializeElevation();
2217     }
2218 
loadBackgroundDrawablesIfNeeded()2219     private void loadBackgroundDrawablesIfNeeded() {
2220         if (mResizingBackgroundDrawable == null) {
2221             mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
2222                     mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
2223                     || mWindow.isShowingWallpaper());
2224             if (mResizingBackgroundDrawable == null) {
2225                 // We shouldn't really get here as the background fallback should be always
2226                 // available since it is defaulted by the system.
2227                 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
2228             }
2229         }
2230         if (mCaptionBackgroundDrawable == null) {
2231             mCaptionBackgroundDrawable = getContext().getDrawable(
2232                     R.drawable.decor_caption_title_focused);
2233         }
2234         if (mResizingBackgroundDrawable != null) {
2235             mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
2236             mResizingBackgroundDrawable.setCallback(null);
2237         }
2238     }
2239 
2240     // Free floating overlapping windows require a caption.
createDecorCaptionView(LayoutInflater inflater)2241     private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
2242         DecorCaptionView decorCaptionView = null;
2243         for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
2244             View view = getChildAt(i);
2245             if (view instanceof DecorCaptionView) {
2246                 // The decor was most likely saved from a relaunch - so reuse it.
2247                 decorCaptionView = (DecorCaptionView) view;
2248                 removeViewAt(i);
2249             }
2250         }
2251         final WindowManager.LayoutParams attrs = mWindow.getAttributes();
2252         final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
2253                 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
2254         final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
2255         // Only a non floating application window on one of the allowed workspaces can get a caption
2256         if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
2257             // Dependent on the brightness of the used title we either use the
2258             // dark or the light button frame.
2259             if (decorCaptionView == null) {
2260                 decorCaptionView = inflateDecorCaptionView(inflater);
2261             }
2262             decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
2263         } else {
2264             decorCaptionView = null;
2265         }
2266 
2267         // Tell the decor if it has a visible caption.
2268         enableCaption(decorCaptionView != null);
2269         return decorCaptionView;
2270     }
2271 
inflateDecorCaptionView(LayoutInflater inflater)2272     private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
2273         final Context context = getContext();
2274         // We make a copy of the inflater, so it has the right context associated with it.
2275         inflater = inflater.from(context);
2276         final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
2277                 null);
2278         setDecorCaptionShade(view);
2279         return view;
2280     }
2281 
setDecorCaptionShade(DecorCaptionView view)2282     private void setDecorCaptionShade(DecorCaptionView view) {
2283         final int shade = mWindow.getDecorCaptionShade();
2284         switch (shade) {
2285             case DECOR_CAPTION_SHADE_LIGHT:
2286                 setLightDecorCaptionShade(view);
2287                 break;
2288             case DECOR_CAPTION_SHADE_DARK:
2289                 setDarkDecorCaptionShade(view);
2290                 break;
2291             default: {
2292                 if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
2293                     setDarkDecorCaptionShade(view);
2294                 } else {
2295                     setLightDecorCaptionShade(view);
2296                 }
2297                 break;
2298             }
2299         }
2300     }
2301 
updateDecorCaptionShade()2302     void updateDecorCaptionShade() {
2303         if (mDecorCaptionView != null) {
2304             setDecorCaptionShade(mDecorCaptionView);
2305         }
2306     }
2307 
setLightDecorCaptionShade(DecorCaptionView view)2308     private void setLightDecorCaptionShade(DecorCaptionView view) {
2309         view.findViewById(R.id.maximize_window).setBackgroundResource(
2310                 R.drawable.decor_maximize_button_light);
2311         view.findViewById(R.id.close_window).setBackgroundResource(
2312                 R.drawable.decor_close_button_light);
2313     }
2314 
setDarkDecorCaptionShade(DecorCaptionView view)2315     private void setDarkDecorCaptionShade(DecorCaptionView view) {
2316         view.findViewById(R.id.maximize_window).setBackgroundResource(
2317                 R.drawable.decor_maximize_button_dark);
2318         view.findViewById(R.id.close_window).setBackgroundResource(
2319                 R.drawable.decor_close_button_dark);
2320     }
2321 
2322     /**
2323      * Returns the color used to fill areas the app has not rendered content to yet when the
2324      * user is resizing the window of an activity in multi-window mode.
2325      */
getResizingBackgroundDrawable(@ullable Drawable backgroundDrawable, @Nullable Drawable fallbackDrawable, boolean windowTranslucent)2326     public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
2327             @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
2328         if (backgroundDrawable != null) {
2329             return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
2330         }
2331 
2332         if (fallbackDrawable != null) {
2333             return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
2334         }
2335         return new ColorDrawable(Color.BLACK);
2336     }
2337 
2338     /**
2339      * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2340      * window is not translucent.
2341      */
enforceNonTranslucentBackground(Drawable drawable, boolean windowTranslucent)2342     private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2343             boolean windowTranslucent) {
2344         if (!windowTranslucent && drawable instanceof ColorDrawable) {
2345             ColorDrawable colorDrawable = (ColorDrawable) drawable;
2346             int color = colorDrawable.getColor();
2347             if (Color.alpha(color) != 255) {
2348                 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2349                         .mutate();
2350                 copy.setColor(
2351                         Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2352                 return copy;
2353             }
2354         }
2355         return drawable;
2356     }
2357 
clearContentView()2358     void clearContentView() {
2359         if (mDecorCaptionView != null) {
2360             mDecorCaptionView.removeContentView();
2361         } else {
2362             // This window doesn't have caption, so we need to remove everything except our views
2363             // we might have added.
2364             for (int i = getChildCount() - 1; i >= 0; i--) {
2365                 View v = getChildAt(i);
2366                 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
2367                         && v != mStatusGuard) {
2368                     removeViewAt(i);
2369                 }
2370             }
2371         }
2372     }
2373 
2374     @Override
onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets)2375     public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2376             Rect stableInsets) {
2377         if (mBackdropFrameRenderer != null) {
2378             mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets);
2379         }
2380     }
2381 
2382     @Override
onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode)2383     public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
2384             Rect stableInsets, int resizeMode) {
2385         if (mWindow.isDestroyed()) {
2386             // If the owner's window is gone, we should not be able to come here anymore.
2387             releaseThreadedRenderer();
2388             return;
2389         }
2390         if (mBackdropFrameRenderer != null) {
2391             return;
2392         }
2393         final ThreadedRenderer renderer = getThreadedRenderer();
2394         if (renderer != null) {
2395             loadBackgroundDrawablesIfNeeded();
2396             WindowInsets rootInsets = getRootWindowInsets();
2397             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
2398                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2399                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2400                     getCurrentColor(mNavigationColorViewState), fullscreen,
2401                     rootInsets.getInsets(WindowInsets.Type.systemBars()));
2402 
2403             // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2404             // If we want to get the shadow shown while resizing, we would need to elevate a new
2405             // element which owns the caption and has the elevation.
2406             updateElevation();
2407 
2408             updateColorViews(null /* insets */, false);
2409         }
2410         mResizeMode = resizeMode;
2411         getViewRootImpl().requestInvalidateRootRenderNode();
2412     }
2413 
2414     @Override
onWindowDragResizeEnd()2415     public void onWindowDragResizeEnd() {
2416         releaseThreadedRenderer();
2417         updateColorViews(null /* insets */, false);
2418         mResizeMode = RESIZE_MODE_INVALID;
2419         getViewRootImpl().requestInvalidateRootRenderNode();
2420     }
2421 
2422     @Override
onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY)2423     public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2424         if (mBackdropFrameRenderer == null) {
2425             return false;
2426         }
2427         return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2428     }
2429 
2430     @Override
onRequestDraw(boolean reportNextDraw)2431     public void onRequestDraw(boolean reportNextDraw) {
2432         if (mBackdropFrameRenderer != null) {
2433             mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2434         } else if (reportNextDraw) {
2435             // If render thread is gone, just report immediately.
2436             if (isAttachedToWindow()) {
2437                 getViewRootImpl().reportDrawFinish();
2438             }
2439         }
2440     }
2441 
2442     @Override
onPostDraw(RecordingCanvas canvas)2443     public void onPostDraw(RecordingCanvas canvas) {
2444         drawResizingShadowIfNeeded(canvas);
2445         drawLegacyNavigationBarBackground(canvas);
2446     }
2447 
initResizingPaints()2448     private void initResizingPaints() {
2449         final int startColor = mContext.getResources().getColor(
2450                 R.color.resize_shadow_start_color, null);
2451         final int endColor = mContext.getResources().getColor(
2452                 R.color.resize_shadow_end_color, null);
2453         final int middleColor = (startColor + endColor) / 2;
2454         mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2455                 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2456                 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2457         mVerticalResizeShadowPaint.setShader(new LinearGradient(
2458                 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2459                 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2460     }
2461 
drawResizingShadowIfNeeded(RecordingCanvas canvas)2462     private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
2463         if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2464                 || mWindow.isTranslucent()
2465                 || mWindow.isShowingWallpaper()) {
2466             return;
2467         }
2468         canvas.save();
2469         canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2470         canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2471         canvas.restore();
2472         canvas.save();
2473         canvas.translate(getWidth() - mFrameOffsets.right, 0);
2474         canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2475         canvas.restore();
2476     }
2477 
drawLegacyNavigationBarBackground(RecordingCanvas canvas)2478     private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
2479         if (!mDrawLegacyNavigationBarBackground) {
2480             return;
2481         }
2482         View v = mNavigationColorViewState.view;
2483         if (v == null) {
2484             return;
2485         }
2486         canvas.drawRect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom(),
2487                 mLegacyNavigationBarBackgroundPaint);
2488     }
2489 
2490     /** Release the renderer thread which is usually done when the user stops resizing. */
releaseThreadedRenderer()2491     private void releaseThreadedRenderer() {
2492         if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2493             mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2494             mLastBackgroundDrawableCb = null;
2495         }
2496 
2497         if (mBackdropFrameRenderer != null) {
2498             mBackdropFrameRenderer.releaseRenderer();
2499             mBackdropFrameRenderer = null;
2500             // Bring the shadow back.
2501             updateElevation();
2502         }
2503     }
2504 
isResizing()2505     private boolean isResizing() {
2506         return mBackdropFrameRenderer != null;
2507     }
2508 
2509     /**
2510      * The elevation gets set for the first time and the framework needs to be informed that
2511      * the surface layer gets created with the shadow size in mind.
2512      */
initializeElevation()2513     private void initializeElevation() {
2514         // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2515         mAllowUpdateElevation = false;
2516         updateElevation();
2517     }
2518 
updateElevation()2519     private void updateElevation() {
2520         final int windowingMode =
2521                 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2522         final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor;
2523         // If rendering shadows in the compositor, don't set an elevation on the view
2524         if (renderShadowsInCompositor) {
2525             return;
2526         }
2527         float elevation = 0;
2528         final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2529         // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2530         // since the shadow is bound to the content size and not the target size.
2531         if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
2532             elevation = hasWindowFocus() ?
2533                     DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
2534             // Add a maximum shadow height value to the top level view.
2535             // Note that pinned stack doesn't have focus
2536             // so maximum shadow height adjustment isn't needed.
2537             // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
2538             if (!mAllowUpdateElevation) {
2539                 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2540             }
2541             // Convert the DP elevation into physical pixels.
2542             elevation = dipToPx(elevation);
2543             mElevationAdjustedForStack = true;
2544         } else if (windowingMode == WINDOWING_MODE_PINNED) {
2545             elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
2546             mElevationAdjustedForStack = true;
2547         } else {
2548             mElevationAdjustedForStack = false;
2549         }
2550 
2551         // Don't change the elevation if we didn't previously adjust it for the stack it was in
2552         // or it didn't change.
2553         if ((wasAdjustedForStack || mElevationAdjustedForStack)
2554                 && getElevation() != elevation) {
2555             if (!isResizing()) {
2556                 mWindow.setElevation(elevation);
2557             } else {
2558                 // Just suppress the shadow when resizing, don't adjust surface insets because it'll
2559                 // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
2560                 // will compensate the offset when passing to BackdropFrameRenderer.
2561                 setElevation(elevation);
2562             }
2563         }
2564     }
2565 
isShowingCaption()2566     boolean isShowingCaption() {
2567         return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
2568     }
2569 
getCaptionHeight()2570     int getCaptionHeight() {
2571         return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
2572     }
2573 
2574     /**
2575      * @hide
2576      * @return the height of insets covering the top of window content area.
2577      */
getCaptionInsetsHeight()2578     public int getCaptionInsetsHeight() {
2579         if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
2580         return getCaptionHeight();
2581     }
2582 
2583     /**
2584      * Converts a DIP measure into physical pixels.
2585      * @param dip The dip value.
2586      * @return Returns the number of pixels.
2587      */
dipToPx(float dip)2588     private float dipToPx(float dip) {
2589         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2590                 getResources().getDisplayMetrics());
2591     }
2592 
2593     /**
2594      * Provide an override of the caption background drawable.
2595      */
setUserCaptionBackgroundDrawable(Drawable drawable)2596     void setUserCaptionBackgroundDrawable(Drawable drawable) {
2597         mUserCaptionBackgroundDrawable = drawable;
2598         if (mBackdropFrameRenderer != null) {
2599             mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2600         }
2601     }
2602 
getTitleSuffix(WindowManager.LayoutParams params)2603     private static String getTitleSuffix(WindowManager.LayoutParams params) {
2604         if (params == null) {
2605             return "";
2606         }
2607         final String[] split = params.getTitle().toString().split("\\.");
2608         if (split.length > 0) {
2609             return split[split.length - 1];
2610         } else {
2611             return "";
2612         }
2613     }
2614 
updateLogTag(WindowManager.LayoutParams params)2615     void updateLogTag(WindowManager.LayoutParams params) {
2616         mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2617     }
2618 
updateAvailableWidth()2619     private void updateAvailableWidth() {
2620         Resources res = getResources();
2621         mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2622                 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2623     }
2624 
2625     /**
2626      * @hide
2627      */
2628     @Override
requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId)2629     public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2630         final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2631         final Menu menu = st != null ? st.menu : null;
2632         if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2633             mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
2634         }
2635     }
2636 
2637     @Override
dispatchPointerCaptureChanged(boolean hasCapture)2638     public void dispatchPointerCaptureChanged(boolean hasCapture) {
2639         super.dispatchPointerCaptureChanged(hasCapture);
2640         if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2641             mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2642         }
2643     }
2644 
2645     @Override
getAccessibilityViewId()2646     public int getAccessibilityViewId() {
2647         return AccessibilityNodeInfo.ROOT_ITEM_ID;
2648     }
2649 
2650     @Override
getWindowInsetsController()2651     public WindowInsetsController getWindowInsetsController() {
2652         if (isAttachedToWindow()) {
2653             return super.getWindowInsetsController();
2654         } else {
2655             return mPendingInsetsController;
2656         }
2657     }
2658 
2659     @Override
toString()2660     public String toString() {
2661         return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2662                 + getTitleSuffix(mWindow.getAttributes()) + "]";
2663     }
2664 
2665     private static class ColorViewState {
2666         View view = null;
2667         int targetVisibility = View.INVISIBLE;
2668         boolean present = false;
2669         boolean visible;
2670         int color;
2671 
2672         final ColorViewAttributes attributes;
2673 
ColorViewState(ColorViewAttributes attributes)2674         ColorViewState(ColorViewAttributes attributes) {
2675             this.attributes = attributes;
2676         }
2677     }
2678 
2679     public static class ColorViewAttributes {
2680 
2681         final int id;
2682         final int translucentFlag;
2683         final int verticalGravity;
2684         final int horizontalGravity;
2685         final int seascapeGravity;
2686         final String transitionName;
2687         final @InternalInsetsType int insetsType;
2688 
ColorViewAttributes(int translucentFlag, int verticalGravity, int horizontalGravity, int seascapeGravity, String transitionName, int id, @InternalInsetsType int insetsType)2689         private ColorViewAttributes(int translucentFlag, int verticalGravity, int horizontalGravity,
2690                 int seascapeGravity, String transitionName, int id,
2691                 @InternalInsetsType int insetsType) {
2692             this.id = id;
2693             this.translucentFlag = translucentFlag;
2694             this.verticalGravity = verticalGravity;
2695             this.horizontalGravity = horizontalGravity;
2696             this.seascapeGravity = seascapeGravity;
2697             this.transitionName = transitionName;
2698             this.insetsType = insetsType;
2699         }
2700 
isPresent(boolean requestedVisible, int windowFlags, boolean force)2701         public boolean isPresent(boolean requestedVisible, int windowFlags, boolean force) {
2702             return requestedVisible
2703                     && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || force);
2704         }
2705 
isVisible(boolean present, int color, int windowFlags, boolean force)2706         public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2707             return present
2708                     && (color & Color.BLACK) != 0
2709                     && ((windowFlags & translucentFlag) == 0  || force);
2710         }
2711 
isVisible(InsetsState state, int color, int windowFlags, boolean force)2712         public boolean isVisible(InsetsState state, int color, int windowFlags, boolean force) {
2713             final boolean present = isPresent(state.getSource(insetsType).isVisible(), windowFlags,
2714                     force);
2715             return isVisible(present, color, windowFlags, force);
2716         }
2717     }
2718 
2719     /**
2720      * Clears out internal references when the action mode is destroyed.
2721      */
2722     private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2723         private final ActionMode.Callback mWrapped;
2724 
ActionModeCallback2Wrapper(ActionMode.Callback wrapped)2725         public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2726             mWrapped = wrapped;
2727         }
2728 
onCreateActionMode(ActionMode mode, Menu menu)2729         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2730             return mWrapped.onCreateActionMode(mode, menu);
2731         }
2732 
onPrepareActionMode(ActionMode mode, Menu menu)2733         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2734             requestFitSystemWindows();
2735             return mWrapped.onPrepareActionMode(mode, menu);
2736         }
2737 
onActionItemClicked(ActionMode mode, MenuItem item)2738         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2739             return mWrapped.onActionItemClicked(mode, item);
2740         }
2741 
onDestroyActionMode(ActionMode mode)2742         public void onDestroyActionMode(ActionMode mode) {
2743             mWrapped.onDestroyActionMode(mode);
2744             final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2745                     >= M;
2746             final boolean isPrimary;
2747             final boolean isFloating;
2748             if (isMncApp) {
2749                 isPrimary = mode == mPrimaryActionMode;
2750                 isFloating = mode == mFloatingActionMode;
2751                 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
2752                     Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
2753                             + mode + " was not the current primary action mode! Expected "
2754                             + mPrimaryActionMode);
2755                 }
2756                 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
2757                     Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
2758                             + mode + " was not the current floating action mode! Expected "
2759                             + mFloatingActionMode);
2760                 }
2761             } else {
2762                 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2763                 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2764             }
2765             if (isPrimary) {
2766                 if (mPrimaryActionModePopup != null) {
2767                     removeCallbacks(mShowPrimaryActionModePopup);
2768                 }
2769                 if (mPrimaryActionModeView != null) {
2770                     endOnGoingFadeAnimation();
2771                     // Store action mode view reference, so we can access it safely when animation
2772                     // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2773                     // so no need to store reference to it in separate variable.
2774                     final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
2775                     mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2776                             1f, 0f);
2777                     mFadeAnim.addListener(new Animator.AnimatorListener() {
2778 
2779                                 @Override
2780                                 public void onAnimationStart(Animator animation) {
2781 
2782                                 }
2783 
2784                                 @Override
2785                                 public void onAnimationEnd(Animator animation) {
2786                                     // If mPrimaryActionModeView has changed - it means that we've
2787                                     // cleared the content while preserving decor view. We don't
2788                                     // want to change the state of new instances accidentally here.
2789                                     if (lastActionModeView == mPrimaryActionModeView) {
2790                                         lastActionModeView.setVisibility(GONE);
2791                                         if (mPrimaryActionModePopup != null) {
2792                                             mPrimaryActionModePopup.dismiss();
2793                                         }
2794                                         lastActionModeView.killMode();
2795                                         mFadeAnim = null;
2796                                         requestApplyInsets();
2797                                     }
2798                                 }
2799 
2800                                 @Override
2801                                 public void onAnimationCancel(Animator animation) {
2802 
2803                                 }
2804 
2805                                 @Override
2806                                 public void onAnimationRepeat(Animator animation) {
2807 
2808                                 }
2809                             });
2810                     mFadeAnim.start();
2811                 }
2812 
2813                 mPrimaryActionMode = null;
2814             } else if (isFloating) {
2815                 cleanupFloatingActionModeViews();
2816                 mFloatingActionMode = null;
2817             }
2818             if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2819                 try {
2820                     mWindow.getCallback().onActionModeFinished(mode);
2821                 } catch (AbstractMethodError ame) {
2822                     // Older apps might not implement this callback method.
2823                 }
2824             }
2825             requestFitSystemWindows();
2826         }
2827 
2828         @Override
onGetContentRect(ActionMode mode, View view, Rect outRect)2829         public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2830             if (mWrapped instanceof ActionMode.Callback2) {
2831                 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2832             } else {
2833                 super.onGetContentRect(mode, view, outRect);
2834             }
2835         }
2836     }
2837 }
2838