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