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