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