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