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