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