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