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