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