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