OSDN Git Service

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