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