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