• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2015 The Android Open Source Project
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  *      http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License
15  */
16 
17 package com.android.internal.policy;
18 
19 import static android.app.WindowConfiguration.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 stableBarInsets = insets.getInsetsIgnoringVisibility(
1098                         WindowInsets.Type.systemBars());
1099                 final Insets systemInsets = Insets.min(
1100                         insets.getInsets(WindowInsets.Type.systemBars()
1101                                 | WindowInsets.Type.displayCutout()), stableBarInsets);
1102                 mLastTopInset = systemInsets.top;
1103                 mLastBottomInset = systemInsets.bottom;
1104                 mLastRightInset = systemInsets.right;
1105                 mLastLeftInset = systemInsets.left;
1106 
1107                 // Don't animate if the presence of stable insets has changed, because that
1108                 // indicates that the window was either just added and received them for the
1109                 // first time, or the window size or position has changed.
1110                 boolean hasTopStableInset = stableBarInsets.top != 0;
1111                 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1112                 mLastHasTopStableInset = hasTopStableInset;
1113 
1114                 boolean hasBottomStableInset = stableBarInsets.bottom != 0;
1115                 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1116                 mLastHasBottomStableInset = hasBottomStableInset;
1117 
1118                 boolean hasRightStableInset = stableBarInsets.right != 0;
1119                 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1120                 mLastHasRightStableInset = hasRightStableInset;
1121 
1122                 boolean hasLeftStableInset = stableBarInsets.left != 0;
1123                 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1124                 mLastHasLeftStableInset = hasLeftStableInset;
1125 
1126                 mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
1127             }
1128 
1129             boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
1130             boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1131             int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
1132             updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
1133                     calculateNavigationBarColor(), mWindow.mNavigationBarDividerColor, navBarSize,
1134                     navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
1135                     0 /* sideInset */, animate && !disallowAnimate,
1136                     mForceWindowDrawsBarBackgrounds, controller);
1137             boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
1138             mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
1139                     && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
1140             if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
1141                 ViewRootImpl vri = getViewRootImpl();
1142                 if (vri != null) {
1143                     vri.requestInvalidateRootRenderNode();
1144                 }
1145             }
1146 
1147             boolean statusBarNeedsRightInset = navBarToRightEdge
1148                     && mNavigationColorViewState.present;
1149             boolean statusBarNeedsLeftInset = navBarToLeftEdge
1150                     && mNavigationColorViewState.present;
1151             int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1152                     : statusBarNeedsLeftInset ? mLastLeftInset : 0;
1153             updateColorViewInt(mStatusColorViewState, sysUiVisibility,
1154                     calculateStatusBarColor(), 0, mLastTopInset,
1155                     false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1156                     animate && !disallowAnimate,
1157                     mForceWindowDrawsBarBackgrounds, controller);
1158 
1159             if (mHasCaption) {
1160                 final int captionColor = calculateStatusBarColor();
1161                 mDecorCaptionView.getCaption().setBackgroundColor(captionColor);
1162                 updateDecorCaptionShade();
1163             }
1164         }
1165 
1166         // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
1167         // mForceWindowDrawsBarBackgrounds, we still need to ensure that the rest of the view
1168         // hierarchy doesn't notice it, unless they've explicitly asked for it.
1169         //
1170         // Note: We don't need to check for IN_SCREEN or INSET_DECOR because unlike the status bar,
1171         // these flags wouldn't make the window draw behind the navigation bar, unless
1172         // LAYOUT_HIDE_NAVIGATION was set.
1173         //
1174         // Note: Once the app uses the R+ Window.setDecorFitsSystemWindows(false) API we no longer
1175         // consume insets because they might no longer set SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION.
1176         boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0
1177                 || !(controller == null || controller.isRequestedVisible(ITYPE_NAVIGATION_BAR));
1178         boolean decorFitsSystemWindows = mWindow.mDecorFitsSystemWindows;
1179         boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
1180                         && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1181                         && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1182                         && decorFitsSystemWindows
1183                         && !hideNavigation)
1184                 || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
1185 
1186         boolean consumingNavBar =
1187                 ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1188                         && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1189                         && decorFitsSystemWindows
1190                         && !hideNavigation)
1191                 || forceConsumingNavBar;
1192 
1193         // If we didn't request fullscreen layout, but we still got it because of the
1194         // mForceWindowDrawsBarBackgrounds flag, also consume top inset.
1195         // If we should always consume system bars, only consume that if the app wanted to go to
1196         // fullscreen, as othrewise we can expect the app to handle it.
1197         boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
1198                 || (attrs.flags & FLAG_FULLSCREEN) != 0
1199                 || !(controller == null || controller.isRequestedVisible(ITYPE_STATUS_BAR));
1200         boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1201                 && decorFitsSystemWindows
1202                 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1203                 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1204                 && mForceWindowDrawsBarBackgrounds
1205                 && mLastTopInset != 0
1206                 || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
1207 
1208         int consumedTop = consumingStatusBar ? mLastTopInset : 0;
1209         int consumedRight = consumingNavBar ? mLastRightInset : 0;
1210         int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
1211         int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
1212 
1213         if (mContentRoot != null
1214                 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1215             MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
1216             if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
1217                     || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
1218                 lp.topMargin = consumedTop;
1219                 lp.rightMargin = consumedRight;
1220                 lp.bottomMargin = consumedBottom;
1221                 lp.leftMargin = consumedLeft;
1222                 mContentRoot.setLayoutParams(lp);
1223 
1224                 if (insets == null) {
1225                     // The insets have changed, but we're not currently in the process
1226                     // of dispatching them.
1227                     requestApplyInsets();
1228                 }
1229             }
1230             if (insets != null) {
1231                 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
1232             }
1233         }
1234 
1235         if (forceConsumingNavBar) {
1236             mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
1237         } else {
1238             mBackgroundInsets = Insets.NONE;
1239         }
1240         updateBackgroundDrawable();
1241 
1242         return insets;
1243     }
1244 
1245     /**
1246      * Updates the background drawable, applying padding to it in case we {@link #mBackgroundInsets}
1247      * are set.
1248      */
updateBackgroundDrawable()1249     private void updateBackgroundDrawable() {
1250         // Background insets can be null if super constructor calls setBackgroundDrawable.
1251         if (mBackgroundInsets == null) {
1252             mBackgroundInsets = Insets.NONE;
1253         }
1254         if (mBackgroundInsets.equals(mLastBackgroundInsets)
1255                 && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
1256             return;
1257         }
1258         if (mOriginalBackgroundDrawable == null || mBackgroundInsets.equals(Insets.NONE)) {
1259 
1260             // Call super since we are intercepting setBackground on this class.
1261             super.setBackgroundDrawable(mOriginalBackgroundDrawable);
1262         } else {
1263 
1264             // Call super since we are intercepting setBackground on this class.
1265             super.setBackgroundDrawable(new InsetDrawable(mOriginalBackgroundDrawable,
1266                     mBackgroundInsets.left, mBackgroundInsets.top,
1267                     mBackgroundInsets.right, mBackgroundInsets.bottom) {
1268 
1269                 /**
1270                  * Return inner padding so we don't apply the padding again in
1271                  * {@link DecorView#drawableChanged()}
1272                  */
1273                 @Override
1274                 public boolean getPadding(Rect padding) {
1275                     return getDrawable().getPadding(padding);
1276                 }
1277             });
1278         }
1279         mLastBackgroundInsets = mBackgroundInsets;
1280         mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
1281     }
1282 
1283     @Override
getBackground()1284     public Drawable getBackground() {
1285         return mOriginalBackgroundDrawable;
1286     }
1287 
calculateStatusBarColor()1288     private int calculateStatusBarColor() {
1289         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
1290                 mSemiTransparentBarColor, mWindow.mStatusBarColor,
1291                 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
1292                 mWindow.mEnsureStatusBarContrastWhenTransparent);
1293     }
1294 
calculateNavigationBarColor()1295     private int calculateNavigationBarColor() {
1296         return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
1297                 mSemiTransparentBarColor, mWindow.mNavigationBarColor,
1298                 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
1299                 mWindow.mEnsureNavigationBarContrastWhenTransparent
1300                         && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
1301     }
1302 
calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor, int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent)1303     public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
1304             int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) {
1305         if ((flags & translucentFlag) != 0) {
1306             return semiTransparentBarColor;
1307         } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
1308             return Color.BLACK;
1309         } else if (scrimTransparent && Color.alpha(barColor) == 0) {
1310             boolean light = (sysuiVis & lightSysuiFlag) != 0;
1311             return light ? SCRIM_LIGHT : semiTransparentBarColor;
1312         } else {
1313             return barColor;
1314         }
1315     }
1316 
getCurrentColor(ColorViewState state)1317     private int getCurrentColor(ColorViewState state) {
1318         if (state.visible) {
1319             return state.color;
1320         } else {
1321             return 0;
1322         }
1323     }
1324 
1325     /**
1326      * Update a color view
1327      *
1328      * @param state the color view to update.
1329      * @param sysUiVis the current systemUiVisibility to apply.
1330      * @param color the current color to apply.
1331      * @param dividerColor the current divider color to apply.
1332      * @param size the current size in the non-parent-matching dimension.
1333      * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1334      *                    horizontal edge,
1335      * @param sideMargin sideMargin for the color view.
1336      * @param animate if true, the change will be animated.
1337      */
updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force, WindowInsetsController controller)1338     private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
1339             int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
1340             boolean animate, boolean force, WindowInsetsController controller) {
1341         state.present = ViewRootImpl.sNewInsetsMode != ViewRootImpl.NEW_INSETS_MODE_FULL
1342                 ? state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force)
1343                 : state.attributes.isPresent(
1344                         controller.isRequestedVisible(state.attributes.insetsType),
1345                         mWindow.getAttributes().flags, force);
1346         boolean show = state.attributes.isVisible(state.present, color,
1347                 mWindow.getAttributes().flags, force);
1348         boolean showView = show && !isResizing() && !mHasCaption && size > 0;
1349 
1350         boolean visibilityChanged = false;
1351         View view = state.view;
1352 
1353         int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1354         int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
1355         int resolvedGravity = verticalBar
1356                 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1357                 : state.attributes.verticalGravity;
1358 
1359         if (view == null) {
1360             if (showView) {
1361                 state.view = view = new View(mContext);
1362                 setColor(view, color, dividerColor, verticalBar, seascape);
1363                 view.setTransitionName(state.attributes.transitionName);
1364                 view.setId(state.attributes.id);
1365                 visibilityChanged = true;
1366                 view.setVisibility(INVISIBLE);
1367                 state.targetVisibility = VISIBLE;
1368 
1369                 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1370                         resolvedGravity);
1371                 if (seascape) {
1372                     lp.leftMargin = sideMargin;
1373                 } else {
1374                     lp.rightMargin = sideMargin;
1375                 }
1376                 addView(view, lp);
1377                 updateColorViewTranslations();
1378             }
1379         } else {
1380             int vis = showView ? VISIBLE : INVISIBLE;
1381             visibilityChanged = state.targetVisibility != vis;
1382             state.targetVisibility = vis;
1383             LayoutParams lp = (LayoutParams) view.getLayoutParams();
1384             int rightMargin = seascape ? 0 : sideMargin;
1385             int leftMargin = seascape ? sideMargin : 0;
1386             if (lp.height != resolvedHeight || lp.width != resolvedWidth
1387                     || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1388                     || lp.leftMargin != leftMargin) {
1389                 lp.height = resolvedHeight;
1390                 lp.width = resolvedWidth;
1391                 lp.gravity = resolvedGravity;
1392                 lp.rightMargin = rightMargin;
1393                 lp.leftMargin = leftMargin;
1394                 view.setLayoutParams(lp);
1395             }
1396             if (showView) {
1397                 setColor(view, color, dividerColor, verticalBar, seascape);
1398             }
1399         }
1400         if (visibilityChanged) {
1401             view.animate().cancel();
1402             if (animate && !isResizing()) {
1403                 if (showView) {
1404                     if (view.getVisibility() != VISIBLE) {
1405                         view.setVisibility(VISIBLE);
1406                         view.setAlpha(0.0f);
1407                     }
1408                     view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1409                             setDuration(mBarEnterExitDuration);
1410                 } else {
1411                     view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1412                             .setDuration(mBarEnterExitDuration)
1413                             .withEndAction(new Runnable() {
1414                                 @Override
1415                                 public void run() {
1416                                     state.view.setAlpha(1.0f);
1417                                     state.view.setVisibility(INVISIBLE);
1418                                 }
1419                             });
1420                 }
1421             } else {
1422                 view.setAlpha(1.0f);
1423                 view.setVisibility(showView ? VISIBLE : INVISIBLE);
1424             }
1425         }
1426         state.visible = show;
1427         state.color = color;
1428     }
1429 
setColor(View v, int color, int dividerColor, boolean verticalBar, boolean seascape)1430     private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1431             boolean seascape) {
1432         if (dividerColor != 0) {
1433             final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1434             if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1435                 final int size = Math.round(
1436                         TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1437                                 v.getContext().getResources().getDisplayMetrics()));
1438                 // Use an inset to make the divider line on the side that faces the app.
1439                 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1440                         verticalBar && !seascape ? size : 0,
1441                         !verticalBar ? size : 0,
1442                         verticalBar && seascape ? size : 0, 0);
1443                 v.setBackground(new LayerDrawable(new Drawable[] {
1444                         new ColorDrawable(dividerColor), d }));
1445                 v.setTag(new Pair<>(verticalBar, seascape));
1446             } else {
1447                 final LayerDrawable d = (LayerDrawable) v.getBackground();
1448                 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1449                 ((ColorDrawable) inset.getDrawable()).setColor(color);
1450                 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1451             }
1452         } else {
1453             v.setTag(null);
1454             v.setBackgroundColor(color);
1455         }
1456     }
1457 
updateColorViewTranslations()1458     private void updateColorViewTranslations() {
1459         // Put the color views back in place when they get moved off the screen
1460         // due to the the ViewRootImpl panning.
1461         int rootScrollY = mRootScrollY;
1462         if (mStatusColorViewState.view != null) {
1463             mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1464         }
1465         if (mNavigationColorViewState.view != null) {
1466             mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1467         }
1468     }
1469 
1470     private WindowInsets updateStatusGuard(WindowInsets insets) {
1471         boolean showStatusGuard = false;
1472         // Show the status guard when the non-overlay contextual action bar is showing
1473         if (mPrimaryActionModeView != null) {
1474             if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1475                 // Insets are magic!
1476                 final MarginLayoutParams mlp = (MarginLayoutParams)
1477                         mPrimaryActionModeView.getLayoutParams();
1478                 boolean mlpChanged = false;
1479                 if (mPrimaryActionModeView.isShown()) {
1480                     if (mTempRect == null) {
1481                         mTempRect = new Rect();
1482                     }
1483                     final Rect rect = mTempRect;
1484 
1485                     // Apply the insets that have not been applied by the contentParent yet.
1486                     WindowInsets innerInsets =
1487                             mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1488                     int newTopMargin = innerInsets.getSystemWindowInsetTop();
1489                     int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
1490                     int newRightMargin = innerInsets.getSystemWindowInsetRight();
1491 
1492                     // Must use root window insets for the guard, because the color views consume
1493                     // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
1494                     // the status guard is attached at the root.
1495                     WindowInsets rootInsets = getRootWindowInsets();
1496                     int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
1497                     int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
1498 
1499                     if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
1500                             || mlp.rightMargin != newRightMargin) {
1501                         mlpChanged = true;
1502                         mlp.topMargin = newTopMargin;
1503                         mlp.leftMargin = newLeftMargin;
1504                         mlp.rightMargin = newRightMargin;
1505                     }
1506 
1507                     if (newTopMargin > 0 && mStatusGuard == null) {
1508                         mStatusGuard = new View(mContext);
1509                         mStatusGuard.setVisibility(GONE);
1510                         final LayoutParams lp = new LayoutParams(MATCH_PARENT,
1511                                 mlp.topMargin, Gravity.LEFT | Gravity.TOP);
1512                         lp.leftMargin = newGuardLeftMargin;
1513                         lp.rightMargin = newGuardRightMargin;
1514                         addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
1515                     } else if (mStatusGuard != null) {
1516                         final LayoutParams lp = (LayoutParams)
1517                                 mStatusGuard.getLayoutParams();
1518                         if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
1519                                 || lp.rightMargin != newGuardRightMargin) {
1520                             lp.height = mlp.topMargin;
1521                             lp.leftMargin = newGuardLeftMargin;
1522                             lp.rightMargin = newGuardRightMargin;
1523                             mStatusGuard.setLayoutParams(lp);
1524                         }
1525                     }
1526 
1527                     // The action mode's theme may differ from the app, so
1528                     // always show the status guard above it if we have one.
1529                     showStatusGuard = mStatusGuard != null;
1530 
1531                     if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
1532                         // If it wasn't previously shown, the color may be stale
1533                         updateStatusGuardColor();
1534                     }
1535 
1536                     // We only need to consume the insets if the action
1537                     // mode is overlaid on the app content (e.g. it's
1538                     // sitting in a FrameLayout, see
1539                     // screen_simple_overlay_action_mode.xml).
1540                     final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1541                             & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1542                     if (nonOverlay && showStatusGuard) {
1543                         insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1544                     }
1545                 } else {
1546                     // reset top margin
1547                     if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
1548                         mlpChanged = true;
1549                         mlp.topMargin = 0;
1550                     }
1551                 }
1552                 if (mlpChanged) {
1553                     mPrimaryActionModeView.setLayoutParams(mlp);
1554                 }
1555             }
1556         }
1557         if (mStatusGuard != null) {
1558             mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
1559         }
1560         return insets;
1561     }
1562 
updateStatusGuardColor()1563     private void updateStatusGuardColor() {
1564         boolean lightStatusBar =
1565                 (getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
1566         mStatusGuard.setBackgroundColor(lightStatusBar
1567                 ? mContext.getColor(R.color.decor_view_status_guard_light)
1568                 : mContext.getColor(R.color.decor_view_status_guard));
1569     }
1570 
1571     /**
1572      * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1573      * an opaque shadow even if the window background is completely transparent. This only applies
1574      * to activities that are currently the task root.
1575      */
updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode)1576     public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1577         if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1578             return;
1579         }
1580 
1581         if (isInPictureInPictureMode) {
1582             final Window.WindowControllerCallback callback =
1583                     mWindow.getWindowControllerCallback();
1584             if (callback != null && callback.isTaskRoot()) {
1585                 // Call super implementation directly as we don't want to save the PIP outline
1586                 // provider to be restored
1587                 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1588             }
1589         } else {
1590             // Restore the previous outline provider
1591             if (getOutlineProvider() != mLastOutlineProvider) {
1592                 setOutlineProvider(mLastOutlineProvider);
1593             }
1594         }
1595         mIsInPictureInPictureMode = isInPictureInPictureMode;
1596     }
1597 
1598     @Override
setOutlineProvider(ViewOutlineProvider provider)1599     public void setOutlineProvider(ViewOutlineProvider provider) {
1600         super.setOutlineProvider(provider);
1601 
1602         // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1603         mLastOutlineProvider = provider;
1604     }
1605 
drawableChanged()1606     private void drawableChanged() {
1607         if (mChanging) {
1608             return;
1609         }
1610 
1611         // Fields can be null if super constructor calls setBackgroundDrawable.
1612         Rect framePadding = mFramePadding != null ? mFramePadding : new Rect();
1613         Rect backgroundPadding = mBackgroundPadding != null ? mBackgroundPadding : new Rect();
1614 
1615         setPadding(framePadding.left + backgroundPadding.left,
1616                 framePadding.top + backgroundPadding.top,
1617                 framePadding.right + backgroundPadding.right,
1618                 framePadding.bottom + backgroundPadding.bottom);
1619         requestLayout();
1620         invalidate();
1621 
1622         int opacity = PixelFormat.OPAQUE;
1623         final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1624         final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor;
1625         // If we draw shadows in the compositor we don't need to force the surface to be
1626         // translucent.
1627         if (winConfig.hasWindowShadow() && !renderShadowsInCompositor) {
1628             // If the window has a shadow, it must be translucent.
1629             opacity = PixelFormat.TRANSLUCENT;
1630         } else{
1631             // Note: If there is no background, we will assume opaque. The
1632             // common case seems to be that an application sets there to be
1633             // no background so it can draw everything itself. For that,
1634             // we would like to assume OPAQUE and let the app force it to
1635             // the slower TRANSLUCENT mode if that is really what it wants.
1636             Drawable bg = getBackground();
1637             Drawable fg = getForeground();
1638             if (bg != null) {
1639                 if (fg == null) {
1640                     opacity = bg.getOpacity();
1641                 } else if (framePadding.left <= 0 && framePadding.top <= 0
1642                         && framePadding.right <= 0 && framePadding.bottom <= 0) {
1643                     // If the frame padding is zero, then we can be opaque
1644                     // if either the frame -or- the background is opaque.
1645                     int fop = fg.getOpacity();
1646                     int bop = bg.getOpacity();
1647                     if (false)
1648                         Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
1649                     if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1650                         opacity = PixelFormat.OPAQUE;
1651                     } else if (fop == PixelFormat.UNKNOWN) {
1652                         opacity = bop;
1653                     } else if (bop == PixelFormat.UNKNOWN) {
1654                         opacity = fop;
1655                     } else {
1656                         opacity = Drawable.resolveOpacity(fop, bop);
1657                     }
1658                 } else {
1659                     // For now we have to assume translucent if there is a
1660                     // frame with padding... there is no way to tell if the
1661                     // frame and background together will draw all pixels.
1662                     if (false)
1663                         Log.v(mLogTag, "Padding: " + mFramePadding);
1664                     opacity = PixelFormat.TRANSLUCENT;
1665                 }
1666             }
1667             if (false)
1668                 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
1669         }
1670 
1671         if (false)
1672             Log.v(mLogTag, "Selected default opacity: " + opacity);
1673 
1674         mDefaultOpacity = opacity;
1675         if (mFeatureId < 0) {
1676             mWindow.setDefaultWindowFormat(opacity);
1677         }
1678     }
1679 
1680     @Override
onWindowFocusChanged(boolean hasWindowFocus)1681     public void onWindowFocusChanged(boolean hasWindowFocus) {
1682         super.onWindowFocusChanged(hasWindowFocus);
1683 
1684         // If the user is chording a menu shortcut, release the chord since
1685         // this window lost focus
1686         if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1687                 && mWindow.mPanelChordingKey != 0) {
1688             mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1689         }
1690 
1691         final Window.Callback cb = mWindow.getCallback();
1692         if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1693             cb.onWindowFocusChanged(hasWindowFocus);
1694         }
1695 
1696         if (mPrimaryActionMode != null) {
1697             mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1698         }
1699         if (mFloatingActionMode != null) {
1700             mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1701         }
1702 
1703         updateElevation();
1704     }
1705 
1706     @Override
onAttachedToWindow()1707     protected void onAttachedToWindow() {
1708         super.onAttachedToWindow();
1709 
1710         final Window.Callback cb = mWindow.getCallback();
1711         if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1712             cb.onAttachedToWindow();
1713         }
1714 
1715         if (mFeatureId == -1) {
1716             /*
1717              * The main window has been attached, try to restore any panels
1718              * that may have been open before. This is called in cases where
1719              * an activity is being killed for configuration change and the
1720              * menu was open. When the activity is recreated, the menu
1721              * should be shown again.
1722              */
1723             mWindow.openPanelsAfterRestore();
1724         }
1725 
1726         if (!mWindowResizeCallbacksAdded) {
1727             // If there is no window callback installed there was no window set before. Set it now.
1728             // Note that our ViewRootImpl object will not change.
1729             getViewRootImpl().addWindowCallbacks(this);
1730             mWindowResizeCallbacksAdded = true;
1731         } else if (mBackdropFrameRenderer != null) {
1732             // We are resizing and this call happened due to a configuration change. Tell the
1733             // renderer about it.
1734             mBackdropFrameRenderer.onConfigurationChange();
1735         }
1736         mWindow.onViewRootImplSet(getViewRootImpl());
1737     }
1738 
1739     @Override
onDetachedFromWindow()1740     protected void onDetachedFromWindow() {
1741         super.onDetachedFromWindow();
1742 
1743         final Window.Callback cb = mWindow.getCallback();
1744         if (cb != null && mFeatureId < 0) {
1745             cb.onDetachedFromWindow();
1746         }
1747 
1748         if (mWindow.mDecorContentParent != null) {
1749             mWindow.mDecorContentParent.dismissPopups();
1750         }
1751 
1752         if (mPrimaryActionModePopup != null) {
1753             removeCallbacks(mShowPrimaryActionModePopup);
1754             if (mPrimaryActionModePopup.isShowing()) {
1755                 mPrimaryActionModePopup.dismiss();
1756             }
1757             mPrimaryActionModePopup = null;
1758         }
1759         if (mFloatingToolbar != null) {
1760             mFloatingToolbar.dismiss();
1761             mFloatingToolbar = null;
1762         }
1763 
1764         PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1765         if (st != null && st.menu != null && mFeatureId < 0) {
1766             st.menu.close();
1767         }
1768 
1769         releaseThreadedRenderer();
1770 
1771         if (mWindowResizeCallbacksAdded) {
1772             getViewRootImpl().removeWindowCallbacks(this);
1773             mWindowResizeCallbacksAdded = false;
1774         }
1775 
1776         mPendingInsetsController.detach();
1777     }
1778 
1779     @Override
onCloseSystemDialogs(String reason)1780     public void onCloseSystemDialogs(String reason) {
1781         if (mFeatureId >= 0) {
1782             mWindow.closeAllPanels();
1783         }
1784     }
1785 
willYouTakeTheSurface()1786     public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1787         return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1788     }
1789 
willYouTakeTheInputQueue()1790     public InputQueue.Callback willYouTakeTheInputQueue() {
1791         return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1792     }
1793 
setSurfaceType(int type)1794     public void setSurfaceType(int type) {
1795         mWindow.setType(type);
1796     }
1797 
setSurfaceFormat(int format)1798     public void setSurfaceFormat(int format) {
1799         mWindow.setFormat(format);
1800     }
1801 
setSurfaceKeepScreenOn(boolean keepOn)1802     public void setSurfaceKeepScreenOn(boolean keepOn) {
1803         if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1804         else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1805     }
1806 
1807     @Override
onRootViewScrollYChanged(int rootScrollY)1808     public void onRootViewScrollYChanged(int rootScrollY) {
1809         mRootScrollY = rootScrollY;
1810         if (mDecorCaptionView != null) {
1811             mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
1812         }
1813         updateColorViewTranslations();
1814     }
1815 
1816     @Override
providePendingInsetsController()1817     public PendingInsetsController providePendingInsetsController() {
1818         return mPendingInsetsController;
1819     }
1820 
createActionMode( int type, ActionMode.Callback2 callback, View originatingView)1821     private ActionMode createActionMode(
1822             int type, ActionMode.Callback2 callback, View originatingView) {
1823         switch (type) {
1824             case ActionMode.TYPE_PRIMARY:
1825             default:
1826                 return createStandaloneActionMode(callback);
1827             case ActionMode.TYPE_FLOATING:
1828                 return createFloatingActionMode(originatingView, callback);
1829         }
1830     }
1831 
setHandledActionMode(ActionMode mode)1832     private void setHandledActionMode(ActionMode mode) {
1833         if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1834             setHandledPrimaryActionMode(mode);
1835         } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1836             setHandledFloatingActionMode(mode);
1837         }
1838     }
1839 
createStandaloneActionMode(ActionMode.Callback callback)1840     private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1841         endOnGoingFadeAnimation();
1842         cleanupPrimaryActionMode();
1843         // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1844         // instance at all, or if there is one, but it is detached from window. The latter case
1845         // might happen when app is resized in multi-window mode and decor view is preserved
1846         // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1847         // app memory leaks because killMode() is called when the dismiss animation ends and from
1848         // cleanupPrimaryActionMode() invocation above.
1849         if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
1850             if (mWindow.isFloating()) {
1851                 // Use the action bar theme.
1852                 final TypedValue outValue = new TypedValue();
1853                 final Resources.Theme baseTheme = mContext.getTheme();
1854                 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1855 
1856                 final Context actionBarContext;
1857                 if (outValue.resourceId != 0) {
1858                     final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1859                     actionBarTheme.setTo(baseTheme);
1860                     actionBarTheme.applyStyle(outValue.resourceId, true);
1861 
1862                     actionBarContext = new ContextThemeWrapper(mContext, 0);
1863                     actionBarContext.getTheme().setTo(actionBarTheme);
1864                 } else {
1865                     actionBarContext = mContext;
1866                 }
1867 
1868                 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1869                 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1870                         R.attr.actionModePopupWindowStyle);
1871                 mPrimaryActionModePopup.setWindowLayoutType(
1872                         WindowManager.LayoutParams.TYPE_APPLICATION);
1873                 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1874                 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1875 
1876                 actionBarContext.getTheme().resolveAttribute(
1877                         R.attr.actionBarSize, outValue, true);
1878                 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1879                         actionBarContext.getResources().getDisplayMetrics());
1880                 mPrimaryActionModeView.setContentHeight(height);
1881                 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1882                 mShowPrimaryActionModePopup = new Runnable() {
1883                     public void run() {
1884                         mPrimaryActionModePopup.showAtLocation(
1885                                 mPrimaryActionModeView.getApplicationWindowToken(),
1886                                 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1887                         endOnGoingFadeAnimation();
1888 
1889                         if (shouldAnimatePrimaryActionModeView()) {
1890                             mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1891                                     0f, 1f);
1892                             mFadeAnim.addListener(new AnimatorListenerAdapter() {
1893                                 @Override
1894                                 public void onAnimationStart(Animator animation) {
1895                                     mPrimaryActionModeView.setVisibility(VISIBLE);
1896                                 }
1897 
1898                                 @Override
1899                                 public void onAnimationEnd(Animator animation) {
1900                                     mPrimaryActionModeView.setAlpha(1f);
1901                                     mFadeAnim = null;
1902                                 }
1903                             });
1904                             mFadeAnim.start();
1905                         } else {
1906                             mPrimaryActionModeView.setAlpha(1f);
1907                             mPrimaryActionModeView.setVisibility(VISIBLE);
1908                         }
1909                     }
1910                 };
1911             } else {
1912                 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
1913                 if (stub != null) {
1914                     mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1915                     mPrimaryActionModePopup = null;
1916                 }
1917             }
1918         }
1919         if (mPrimaryActionModeView != null) {
1920             mPrimaryActionModeView.killMode();
1921             ActionMode mode = new StandaloneActionMode(
1922                     mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1923                     callback, mPrimaryActionModePopup == null);
1924             return mode;
1925         }
1926         return null;
1927     }
1928 
endOnGoingFadeAnimation()1929     private void endOnGoingFadeAnimation() {
1930         if (mFadeAnim != null) {
1931             mFadeAnim.end();
1932         }
1933     }
1934 
setHandledPrimaryActionMode(ActionMode mode)1935     private void setHandledPrimaryActionMode(ActionMode mode) {
1936         endOnGoingFadeAnimation();
1937         mPrimaryActionMode = mode;
1938         mPrimaryActionMode.invalidate();
1939         mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1940         if (mPrimaryActionModePopup != null) {
1941             post(mShowPrimaryActionModePopup);
1942         } else {
1943             if (shouldAnimatePrimaryActionModeView()) {
1944                 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1945                 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1946                     @Override
1947                     public void onAnimationStart(Animator animation) {
1948                         mPrimaryActionModeView.setVisibility(View.VISIBLE);
1949                     }
1950 
1951                     @Override
1952                     public void onAnimationEnd(Animator animation) {
1953                         mPrimaryActionModeView.setAlpha(1f);
1954                         mFadeAnim = null;
1955                     }
1956                 });
1957                 mFadeAnim.start();
1958             } else {
1959                 mPrimaryActionModeView.setAlpha(1f);
1960                 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1961             }
1962         }
1963         mPrimaryActionModeView.sendAccessibilityEvent(
1964                 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1965     }
1966 
shouldAnimatePrimaryActionModeView()1967     boolean shouldAnimatePrimaryActionModeView() {
1968         // We only to animate the action mode in if the decor has already been laid out.
1969         // If it hasn't been laid out, it hasn't been drawn to screen yet.
1970         return isLaidOut();
1971     }
1972 
createFloatingActionMode( View originatingView, ActionMode.Callback2 callback)1973     private ActionMode createFloatingActionMode(
1974             View originatingView, ActionMode.Callback2 callback) {
1975         if (mFloatingActionMode != null) {
1976             mFloatingActionMode.finish();
1977         }
1978         cleanupFloatingActionModeViews();
1979         mFloatingToolbar = new FloatingToolbar(mWindow);
1980         final FloatingActionMode mode =
1981                 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
1982         mFloatingActionModeOriginatingView = originatingView;
1983         mFloatingToolbarPreDrawListener =
1984             new ViewTreeObserver.OnPreDrawListener() {
1985                 @Override
1986                 public boolean onPreDraw() {
1987                     mode.updateViewLocationInWindow();
1988                     return true;
1989                 }
1990             };
1991         return mode;
1992     }
1993 
setHandledFloatingActionMode(ActionMode mode)1994     private void setHandledFloatingActionMode(ActionMode mode) {
1995         mFloatingActionMode = mode;
1996         mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
1997         mFloatingActionModeOriginatingView.getViewTreeObserver()
1998             .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1999     }
2000 
2001     /**
2002      * Informs the decor if the caption is attached and visible.
2003      * @param attachedAndVisible true when the decor is visible.
2004      * Note that this will even be called if there is no caption.
2005      **/
enableCaption(boolean attachedAndVisible)2006     void enableCaption(boolean attachedAndVisible) {
2007         if (mHasCaption != attachedAndVisible) {
2008             mHasCaption = attachedAndVisible;
2009             if (getForeground() != null) {
2010                 drawableChanged();
2011             }
2012         }
2013     }
2014 
2015     /**
2016      * An interface to be called when the caption visibility or height changed, to report the
2017      * corresponding insets change to the InsetsController.
2018      */
notifyCaptionHeightChanged()2019     public void notifyCaptionHeightChanged() {
2020         getWindowInsetsController().setCaptionInsetsHeight(getCaptionInsetsHeight());
2021     }
2022 
setWindow(PhoneWindow phoneWindow)2023     void setWindow(PhoneWindow phoneWindow) {
2024         mWindow = phoneWindow;
2025         Context context = getContext();
2026         if (context instanceof DecorContext) {
2027             DecorContext decorContext = (DecorContext) context;
2028             decorContext.setPhoneWindow(mWindow);
2029         }
2030         if (mPendingWindowBackground != null) {
2031             Drawable background = mPendingWindowBackground;
2032             mPendingWindowBackground = null;
2033             setWindowBackground(background);
2034         }
2035     }
2036 
2037     @Override
getResources()2038     public Resources getResources() {
2039         // Make sure the Resources object is propogated from the Context since it can be updated in
2040         // the Context object.
2041         return getContext().getResources();
2042     }
2043 
2044     @Override
onConfigurationChanged(Configuration newConfig)2045     protected void onConfigurationChanged(Configuration newConfig) {
2046         super.onConfigurationChanged(newConfig);
2047 
2048         updateDecorCaptionStatus(newConfig);
2049 
2050         updateAvailableWidth();
2051         initializeElevation();
2052     }
2053 
2054     @Override
onMovedToDisplay(int displayId, Configuration config)2055     public void onMovedToDisplay(int displayId, Configuration config) {
2056         super.onMovedToDisplay(displayId, config);
2057         // Have to explicitly update displayId because it may use DecorContext
2058         getContext().updateDisplay(displayId);
2059     }
2060 
2061     /**
2062      * Determines if the workspace is entirely covered by the window.
2063      * @return {@code true} when the window is filling the entire screen/workspace.
2064      **/
isFillingScreen(Configuration config)2065     private boolean isFillingScreen(Configuration config) {
2066         final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
2067                 == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
2068         return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
2069                 & View.SYSTEM_UI_FLAG_FULLSCREEN));
2070     }
2071 
updateDecorCaptionStatus(Configuration config)2072     private void updateDecorCaptionStatus(Configuration config) {
2073         final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
2074                 && !isFillingScreen(config);
2075         if (mDecorCaptionView == null && displayWindowDecor) {
2076             // Configuration now requires a caption.
2077             final LayoutInflater inflater = mWindow.getLayoutInflater();
2078             mDecorCaptionView = createDecorCaptionView(inflater);
2079             if (mDecorCaptionView != null) {
2080                 if (mDecorCaptionView.getParent() == null) {
2081                     addView(mDecorCaptionView, 0,
2082                             new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2083                 }
2084                 removeView(mContentRoot);
2085                 mDecorCaptionView.addView(mContentRoot,
2086                         new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
2087             }
2088         } else if (mDecorCaptionView != null) {
2089             // We might have to change the kind of surface before we do anything else.
2090             mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
2091             enableCaption(displayWindowDecor);
2092         }
2093     }
2094 
onResourcesLoaded(LayoutInflater inflater, int layoutResource)2095     void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
2096         if (mBackdropFrameRenderer != null) {
2097             loadBackgroundDrawablesIfNeeded();
2098             mBackdropFrameRenderer.onResourcesLoaded(
2099                     this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2100                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2101                     getCurrentColor(mNavigationColorViewState));
2102         }
2103 
2104         mDecorCaptionView = createDecorCaptionView(inflater);
2105         final View root = inflater.inflate(layoutResource, null);
2106         if (mDecorCaptionView != null) {
2107             if (mDecorCaptionView.getParent() == null) {
2108                 addView(mDecorCaptionView,
2109                         new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2110             }
2111             mDecorCaptionView.addView(root,
2112                     new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
2113         } else {
2114 
2115             // Put it below the color views.
2116             addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2117         }
2118         mContentRoot = (ViewGroup) root;
2119         initializeElevation();
2120     }
2121 
loadBackgroundDrawablesIfNeeded()2122     private void loadBackgroundDrawablesIfNeeded() {
2123         if (mResizingBackgroundDrawable == null) {
2124             mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
2125                     mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
2126                     || mWindow.isShowingWallpaper());
2127             if (mResizingBackgroundDrawable == null) {
2128                 // We shouldn't really get here as the background fallback should be always
2129                 // available since it is defaulted by the system.
2130                 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
2131             }
2132         }
2133         if (mCaptionBackgroundDrawable == null) {
2134             mCaptionBackgroundDrawable = getContext().getDrawable(
2135                     R.drawable.decor_caption_title_focused);
2136         }
2137         if (mResizingBackgroundDrawable != null) {
2138             mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
2139             mResizingBackgroundDrawable.setCallback(null);
2140         }
2141     }
2142 
2143     // Free floating overlapping windows require a caption.
createDecorCaptionView(LayoutInflater inflater)2144     private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
2145         DecorCaptionView decorCaptionView = null;
2146         for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
2147             View view = getChildAt(i);
2148             if (view instanceof DecorCaptionView) {
2149                 // The decor was most likely saved from a relaunch - so reuse it.
2150                 decorCaptionView = (DecorCaptionView) view;
2151                 removeViewAt(i);
2152             }
2153         }
2154         final WindowManager.LayoutParams attrs = mWindow.getAttributes();
2155         final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
2156                 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
2157         final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
2158         // Only a non floating application window on one of the allowed workspaces can get a caption
2159         if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
2160             // Dependent on the brightness of the used title we either use the
2161             // dark or the light button frame.
2162             if (decorCaptionView == null) {
2163                 decorCaptionView = inflateDecorCaptionView(inflater);
2164             }
2165             decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
2166         } else {
2167             decorCaptionView = null;
2168         }
2169 
2170         // Tell the decor if it has a visible caption.
2171         enableCaption(decorCaptionView != null);
2172         return decorCaptionView;
2173     }
2174 
inflateDecorCaptionView(LayoutInflater inflater)2175     private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
2176         final Context context = getContext();
2177         // We make a copy of the inflater, so it has the right context associated with it.
2178         inflater = inflater.from(context);
2179         final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
2180                 null);
2181         setDecorCaptionShade(view);
2182         return view;
2183     }
2184 
setDecorCaptionShade(DecorCaptionView view)2185     private void setDecorCaptionShade(DecorCaptionView view) {
2186         final int shade = mWindow.getDecorCaptionShade();
2187         switch (shade) {
2188             case DECOR_CAPTION_SHADE_LIGHT:
2189                 setLightDecorCaptionShade(view);
2190                 break;
2191             case DECOR_CAPTION_SHADE_DARK:
2192                 setDarkDecorCaptionShade(view);
2193                 break;
2194             default: {
2195                 if ((getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0) {
2196                     setDarkDecorCaptionShade(view);
2197                 } else {
2198                     setLightDecorCaptionShade(view);
2199                 }
2200                 break;
2201             }
2202         }
2203     }
2204 
updateDecorCaptionShade()2205     void updateDecorCaptionShade() {
2206         if (mDecorCaptionView != null) {
2207             setDecorCaptionShade(mDecorCaptionView);
2208         }
2209     }
2210 
setLightDecorCaptionShade(DecorCaptionView view)2211     private void setLightDecorCaptionShade(DecorCaptionView view) {
2212         view.findViewById(R.id.maximize_window).setBackgroundResource(
2213                 R.drawable.decor_maximize_button_light);
2214         view.findViewById(R.id.close_window).setBackgroundResource(
2215                 R.drawable.decor_close_button_light);
2216     }
2217 
setDarkDecorCaptionShade(DecorCaptionView view)2218     private void setDarkDecorCaptionShade(DecorCaptionView view) {
2219         view.findViewById(R.id.maximize_window).setBackgroundResource(
2220                 R.drawable.decor_maximize_button_dark);
2221         view.findViewById(R.id.close_window).setBackgroundResource(
2222                 R.drawable.decor_close_button_dark);
2223     }
2224 
2225     /**
2226      * Returns the color used to fill areas the app has not rendered content to yet when the
2227      * user is resizing the window of an activity in multi-window mode.
2228      */
getResizingBackgroundDrawable(@ullable Drawable backgroundDrawable, @Nullable Drawable fallbackDrawable, boolean windowTranslucent)2229     public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
2230             @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
2231         if (backgroundDrawable != null) {
2232             return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
2233         }
2234 
2235         if (fallbackDrawable != null) {
2236             return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
2237         }
2238         return new ColorDrawable(Color.BLACK);
2239     }
2240 
2241     /**
2242      * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2243      * window is not translucent.
2244      */
enforceNonTranslucentBackground(Drawable drawable, boolean windowTranslucent)2245     private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2246             boolean windowTranslucent) {
2247         if (!windowTranslucent && drawable instanceof ColorDrawable) {
2248             ColorDrawable colorDrawable = (ColorDrawable) drawable;
2249             int color = colorDrawable.getColor();
2250             if (Color.alpha(color) != 255) {
2251                 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2252                         .mutate();
2253                 copy.setColor(
2254                         Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2255                 return copy;
2256             }
2257         }
2258         return drawable;
2259     }
2260 
clearContentView()2261     void clearContentView() {
2262         if (mDecorCaptionView != null) {
2263             mDecorCaptionView.removeContentView();
2264         } else {
2265             // This window doesn't have caption, so we need to remove everything except our views
2266             // we might have added.
2267             for (int i = getChildCount() - 1; i >= 0; i--) {
2268                 View v = getChildAt(i);
2269                 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
2270                         && v != mStatusGuard) {
2271                     removeViewAt(i);
2272                 }
2273             }
2274         }
2275     }
2276 
2277     @Override
onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets)2278     public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2279             Rect stableInsets) {
2280         if (mBackdropFrameRenderer != null) {
2281             mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets);
2282         }
2283     }
2284 
2285     @Override
onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets, Rect stableInsets, int resizeMode)2286     public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
2287             Rect stableInsets, int resizeMode) {
2288         if (mWindow.isDestroyed()) {
2289             // If the owner's window is gone, we should not be able to come here anymore.
2290             releaseThreadedRenderer();
2291             return;
2292         }
2293         if (mBackdropFrameRenderer != null) {
2294             return;
2295         }
2296         final ThreadedRenderer renderer = getThreadedRenderer();
2297         if (renderer != null) {
2298             loadBackgroundDrawablesIfNeeded();
2299             WindowInsets rootInsets = getRootWindowInsets();
2300             mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
2301                     initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2302                     mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2303                     getCurrentColor(mNavigationColorViewState), fullscreen,
2304                     rootInsets.getInsets(WindowInsets.Type.systemBars()));
2305 
2306             // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2307             // If we want to get the shadow shown while resizing, we would need to elevate a new
2308             // element which owns the caption and has the elevation.
2309             updateElevation();
2310 
2311             updateColorViews(null /* insets */, false);
2312         }
2313         mResizeMode = resizeMode;
2314         getViewRootImpl().requestInvalidateRootRenderNode();
2315     }
2316 
2317     @Override
onWindowDragResizeEnd()2318     public void onWindowDragResizeEnd() {
2319         releaseThreadedRenderer();
2320         updateColorViews(null /* insets */, false);
2321         mResizeMode = RESIZE_MODE_INVALID;
2322         getViewRootImpl().requestInvalidateRootRenderNode();
2323     }
2324 
2325     @Override
onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY)2326     public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2327         if (mBackdropFrameRenderer == null) {
2328             return false;
2329         }
2330         return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2331     }
2332 
2333     @Override
onRequestDraw(boolean reportNextDraw)2334     public void onRequestDraw(boolean reportNextDraw) {
2335         if (mBackdropFrameRenderer != null) {
2336             mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2337         } else if (reportNextDraw) {
2338             // If render thread is gone, just report immediately.
2339             if (isAttachedToWindow()) {
2340                 getViewRootImpl().reportDrawFinish();
2341             }
2342         }
2343     }
2344 
2345     @Override
onPostDraw(RecordingCanvas canvas)2346     public void onPostDraw(RecordingCanvas canvas) {
2347         drawResizingShadowIfNeeded(canvas);
2348         drawLegacyNavigationBarBackground(canvas);
2349     }
2350 
initResizingPaints()2351     private void initResizingPaints() {
2352         final int startColor = mContext.getResources().getColor(
2353                 R.color.resize_shadow_start_color, null);
2354         final int endColor = mContext.getResources().getColor(
2355                 R.color.resize_shadow_end_color, null);
2356         final int middleColor = (startColor + endColor) / 2;
2357         mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2358                 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2359                 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2360         mVerticalResizeShadowPaint.setShader(new LinearGradient(
2361                 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2362                 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2363     }
2364 
drawResizingShadowIfNeeded(RecordingCanvas canvas)2365     private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
2366         if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2367                 || mWindow.isTranslucent()
2368                 || mWindow.isShowingWallpaper()) {
2369             return;
2370         }
2371         canvas.save();
2372         canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2373         canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2374         canvas.restore();
2375         canvas.save();
2376         canvas.translate(getWidth() - mFrameOffsets.right, 0);
2377         canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2378         canvas.restore();
2379     }
2380 
drawLegacyNavigationBarBackground(RecordingCanvas canvas)2381     private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
2382         if (!mDrawLegacyNavigationBarBackground) {
2383             return;
2384         }
2385         View v = mNavigationColorViewState.view;
2386         if (v == null) {
2387             return;
2388         }
2389         canvas.drawRect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom(),
2390                 mLegacyNavigationBarBackgroundPaint);
2391     }
2392 
2393     /** Release the renderer thread which is usually done when the user stops resizing. */
releaseThreadedRenderer()2394     private void releaseThreadedRenderer() {
2395         if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2396             mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2397             mLastBackgroundDrawableCb = null;
2398         }
2399 
2400         if (mBackdropFrameRenderer != null) {
2401             mBackdropFrameRenderer.releaseRenderer();
2402             mBackdropFrameRenderer = null;
2403             // Bring the shadow back.
2404             updateElevation();
2405         }
2406     }
2407 
isResizing()2408     private boolean isResizing() {
2409         return mBackdropFrameRenderer != null;
2410     }
2411 
2412     /**
2413      * The elevation gets set for the first time and the framework needs to be informed that
2414      * the surface layer gets created with the shadow size in mind.
2415      */
initializeElevation()2416     private void initializeElevation() {
2417         // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2418         mAllowUpdateElevation = false;
2419         updateElevation();
2420     }
2421 
updateElevation()2422     private void updateElevation() {
2423         final int windowingMode =
2424                 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2425         final boolean renderShadowsInCompositor = mWindow.mRenderShadowsInCompositor;
2426         // If rendering shadows in the compositor, don't set an elevation on the view
2427         if (renderShadowsInCompositor) {
2428             return;
2429         }
2430         float elevation = 0;
2431         final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2432         // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2433         // since the shadow is bound to the content size and not the target size.
2434         if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
2435             elevation = hasWindowFocus() ?
2436                     DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
2437             // Add a maximum shadow height value to the top level view.
2438             // Note that pinned stack doesn't have focus
2439             // so maximum shadow height adjustment isn't needed.
2440             // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
2441             if (!mAllowUpdateElevation) {
2442                 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2443             }
2444             // Convert the DP elevation into physical pixels.
2445             elevation = dipToPx(elevation);
2446             mElevationAdjustedForStack = true;
2447         } else if (windowingMode == WINDOWING_MODE_PINNED) {
2448             elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
2449             mElevationAdjustedForStack = true;
2450         } else {
2451             mElevationAdjustedForStack = false;
2452         }
2453 
2454         // Don't change the elevation if we didn't previously adjust it for the stack it was in
2455         // or it didn't change.
2456         if ((wasAdjustedForStack || mElevationAdjustedForStack)
2457                 && getElevation() != elevation) {
2458             if (!isResizing()) {
2459                 mWindow.setElevation(elevation);
2460             } else {
2461                 // Just suppress the shadow when resizing, don't adjust surface insets because it'll
2462                 // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
2463                 // will compensate the offset when passing to BackdropFrameRenderer.
2464                 setElevation(elevation);
2465             }
2466         }
2467     }
2468 
isShowingCaption()2469     boolean isShowingCaption() {
2470         return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
2471     }
2472 
getCaptionHeight()2473     int getCaptionHeight() {
2474         return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
2475     }
2476 
2477     /**
2478      * @hide
2479      * @return the height of insets covering the top of window content area.
2480      */
getCaptionInsetsHeight()2481     public int getCaptionInsetsHeight() {
2482         if (!mWindow.isOverlayWithDecorCaptionEnabled()) return 0;
2483         return getCaptionHeight();
2484     }
2485 
2486     /**
2487      * Converts a DIP measure into physical pixels.
2488      * @param dip The dip value.
2489      * @return Returns the number of pixels.
2490      */
dipToPx(float dip)2491     private float dipToPx(float dip) {
2492         return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2493                 getResources().getDisplayMetrics());
2494     }
2495 
2496     /**
2497      * Provide an override of the caption background drawable.
2498      */
setUserCaptionBackgroundDrawable(Drawable drawable)2499     void setUserCaptionBackgroundDrawable(Drawable drawable) {
2500         mUserCaptionBackgroundDrawable = drawable;
2501         if (mBackdropFrameRenderer != null) {
2502             mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2503         }
2504     }
2505 
getTitleSuffix(WindowManager.LayoutParams params)2506     private static String getTitleSuffix(WindowManager.LayoutParams params) {
2507         if (params == null) {
2508             return "";
2509         }
2510         final String[] split = params.getTitle().toString().split("\\.");
2511         if (split.length > 0) {
2512             return split[split.length - 1];
2513         } else {
2514             return "";
2515         }
2516     }
2517 
updateLogTag(WindowManager.LayoutParams params)2518     void updateLogTag(WindowManager.LayoutParams params) {
2519         mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2520     }
2521 
updateAvailableWidth()2522     private void updateAvailableWidth() {
2523         Resources res = getResources();
2524         mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2525                 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2526     }
2527 
2528     /**
2529      * @hide
2530      */
2531     @Override
requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId)2532     public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2533         final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2534         final Menu menu = st != null ? st.menu : null;
2535         if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2536             mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
2537         }
2538     }
2539 
2540     @Override
dispatchPointerCaptureChanged(boolean hasCapture)2541     public void dispatchPointerCaptureChanged(boolean hasCapture) {
2542         super.dispatchPointerCaptureChanged(hasCapture);
2543         if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2544             mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2545         }
2546     }
2547 
2548     @Override
getAccessibilityViewId()2549     public int getAccessibilityViewId() {
2550         return AccessibilityNodeInfo.ROOT_ITEM_ID;
2551     }
2552 
2553     @Override
getWindowInsetsController()2554     public WindowInsetsController getWindowInsetsController() {
2555         if (isAttachedToWindow()) {
2556             return super.getWindowInsetsController();
2557         } else {
2558             return mPendingInsetsController;
2559         }
2560     }
2561 
2562     @Override
toString()2563     public String toString() {
2564         return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2565                 + getTitleSuffix(mWindow.getAttributes()) + "]";
2566     }
2567 
2568     private static class ColorViewState {
2569         View view = null;
2570         int targetVisibility = View.INVISIBLE;
2571         boolean present = false;
2572         boolean visible;
2573         int color;
2574 
2575         final ColorViewAttributes attributes;
2576 
ColorViewState(ColorViewAttributes attributes)2577         ColorViewState(ColorViewAttributes attributes) {
2578             this.attributes = attributes;
2579         }
2580     }
2581 
2582     public static class ColorViewAttributes {
2583 
2584         final int id;
2585         final int systemUiHideFlag;
2586         final int translucentFlag;
2587         final int verticalGravity;
2588         final int horizontalGravity;
2589         final int seascapeGravity;
2590         final String transitionName;
2591         final int hideWindowFlag;
2592         final @InternalInsetsType int insetsType;
2593 
ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity, int horizontalGravity, int seascapeGravity, String transitionName, int id, int hideWindowFlag, @InternalInsetsType int insetsType)2594         private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2595                 int horizontalGravity, int seascapeGravity, String transitionName, int id,
2596                 int hideWindowFlag, @InternalInsetsType int insetsType) {
2597             this.id = id;
2598             this.systemUiHideFlag = systemUiHideFlag;
2599             this.translucentFlag = translucentFlag;
2600             this.verticalGravity = verticalGravity;
2601             this.horizontalGravity = horizontalGravity;
2602             this.seascapeGravity = seascapeGravity;
2603             this.transitionName = transitionName;
2604             this.hideWindowFlag = hideWindowFlag;
2605             this.insetsType = insetsType;
2606         }
2607 
2608         // TODO(b/118118435): remove after migration
isPresent(int sysUiVis, int windowFlags, boolean force)2609         public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2610             return (sysUiVis & systemUiHideFlag) == 0
2611                     && (windowFlags & hideWindowFlag) == 0
2612                     && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2613                     || force);
2614         }
2615 
isPresent(boolean requestedVisible, int windowFlags, boolean force)2616         public boolean isPresent(boolean requestedVisible, int windowFlags, boolean force) {
2617             return requestedVisible
2618                     && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 || force);
2619         }
2620 
isVisible(boolean present, int color, int windowFlags, boolean force)2621         public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2622             return present
2623                     && (color & Color.BLACK) != 0
2624                     && ((windowFlags & translucentFlag) == 0  || force);
2625         }
2626 
2627         // TODO(b/118118435): remove after migration
isVisible(int sysUiVis, int color, int windowFlags, boolean force)2628         public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2629             final boolean present = isPresent(sysUiVis, windowFlags, force);
2630             return isVisible(present, color, windowFlags, force);
2631         }
2632 
isVisible(InsetsState state, int color, int windowFlags, boolean force)2633         public boolean isVisible(InsetsState state, int color, int windowFlags, boolean force) {
2634             final boolean present = isPresent(state.getSource(insetsType).isVisible(), windowFlags,
2635                     force);
2636             return isVisible(present, color, windowFlags, force);
2637         }
2638     }
2639 
2640     /**
2641      * Clears out internal references when the action mode is destroyed.
2642      */
2643     private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2644         private final ActionMode.Callback mWrapped;
2645 
ActionModeCallback2Wrapper(ActionMode.Callback wrapped)2646         public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2647             mWrapped = wrapped;
2648         }
2649 
onCreateActionMode(ActionMode mode, Menu menu)2650         public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2651             return mWrapped.onCreateActionMode(mode, menu);
2652         }
2653 
onPrepareActionMode(ActionMode mode, Menu menu)2654         public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2655             requestFitSystemWindows();
2656             return mWrapped.onPrepareActionMode(mode, menu);
2657         }
2658 
onActionItemClicked(ActionMode mode, MenuItem item)2659         public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2660             return mWrapped.onActionItemClicked(mode, item);
2661         }
2662 
onDestroyActionMode(ActionMode mode)2663         public void onDestroyActionMode(ActionMode mode) {
2664             mWrapped.onDestroyActionMode(mode);
2665             final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2666                     >= M;
2667             final boolean isPrimary;
2668             final boolean isFloating;
2669             if (isMncApp) {
2670                 isPrimary = mode == mPrimaryActionMode;
2671                 isFloating = mode == mFloatingActionMode;
2672                 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
2673                     Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
2674                             + mode + " was not the current primary action mode! Expected "
2675                             + mPrimaryActionMode);
2676                 }
2677                 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
2678                     Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
2679                             + mode + " was not the current floating action mode! Expected "
2680                             + mFloatingActionMode);
2681                 }
2682             } else {
2683                 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2684                 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2685             }
2686             if (isPrimary) {
2687                 if (mPrimaryActionModePopup != null) {
2688                     removeCallbacks(mShowPrimaryActionModePopup);
2689                 }
2690                 if (mPrimaryActionModeView != null) {
2691                     endOnGoingFadeAnimation();
2692                     // Store action mode view reference, so we can access it safely when animation
2693                     // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2694                     // so no need to store reference to it in separate variable.
2695                     final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
2696                     mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2697                             1f, 0f);
2698                     mFadeAnim.addListener(new Animator.AnimatorListener() {
2699 
2700                                 @Override
2701                                 public void onAnimationStart(Animator animation) {
2702 
2703                                 }
2704 
2705                                 @Override
2706                                 public void onAnimationEnd(Animator animation) {
2707                                     // If mPrimaryActionModeView has changed - it means that we've
2708                                     // cleared the content while preserving decor view. We don't
2709                                     // want to change the state of new instances accidentally here.
2710                                     if (lastActionModeView == mPrimaryActionModeView) {
2711                                         lastActionModeView.setVisibility(GONE);
2712                                         if (mPrimaryActionModePopup != null) {
2713                                             mPrimaryActionModePopup.dismiss();
2714                                         }
2715                                         lastActionModeView.killMode();
2716                                         mFadeAnim = null;
2717                                         requestApplyInsets();
2718                                     }
2719                                 }
2720 
2721                                 @Override
2722                                 public void onAnimationCancel(Animator animation) {
2723 
2724                                 }
2725 
2726                                 @Override
2727                                 public void onAnimationRepeat(Animator animation) {
2728 
2729                                 }
2730                             });
2731                     mFadeAnim.start();
2732                 }
2733 
2734                 mPrimaryActionMode = null;
2735             } else if (isFloating) {
2736                 cleanupFloatingActionModeViews();
2737                 mFloatingActionMode = null;
2738             }
2739             if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2740                 try {
2741                     mWindow.getCallback().onActionModeFinished(mode);
2742                 } catch (AbstractMethodError ame) {
2743                     // Older apps might not implement this callback method.
2744                 }
2745             }
2746             requestFitSystemWindows();
2747         }
2748 
2749         @Override
onGetContentRect(ActionMode mode, View view, Rect outRect)2750         public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2751             if (mWrapped instanceof ActionMode.Callback2) {
2752                 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2753             } else {
2754                 super.onGetContentRect(mode, view, outRect);
2755             }
2756         }
2757     }
2758 }
2759