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