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.inset(0, insets.getSystemWindowInsetTop(),
987                        0, insets.getSystemWindowInsetBottom());
988            }
989            if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
990                mFloatingInsets.left = insets.getSystemWindowInsetTop();
991                mFloatingInsets.right = insets.getSystemWindowInsetBottom();
992                insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
993                        insets.getSystemWindowInsetRight(), 0);
994            }
995        }
996        mFrameOffsets.set(insets.getSystemWindowInsets());
997        insets = updateColorViews(insets, true /* animate */);
998        insets = updateStatusGuard(insets);
999        if (getForeground() != null) {
1000            drawableChanged();
1001        }
1002        return insets;
1003    }
1004
1005    @Override
1006    public boolean isTransitionGroup() {
1007        return false;
1008    }
1009
1010    public static int getColorViewTopInset(int stableTop, int systemTop) {
1011        return Math.min(stableTop, systemTop);
1012    }
1013
1014    public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
1015        return Math.min(stableBottom, systemBottom);
1016    }
1017
1018    public static int getColorViewRightInset(int stableRight, int systemRight) {
1019        return Math.min(stableRight, systemRight);
1020    }
1021
1022    public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
1023        return Math.min(stableLeft, systemLeft);
1024    }
1025
1026    public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
1027        return bottomInset == 0 && rightInset > 0;
1028    }
1029
1030    public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
1031        return bottomInset == 0 && leftInset > 0;
1032    }
1033
1034    public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
1035        return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1036                : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
1037    }
1038
1039    public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
1040            Rect contentInsets, Rect outRect) {
1041        final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
1042        final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
1043        final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
1044        final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1045        if (isNavBarToRightEdge(bottomInset, rightInset)) {
1046            outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1047        } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1048            outRect.set(0, 0, size, canvasHeight);
1049        } else {
1050            outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1051        }
1052    }
1053
1054    WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1055        WindowManager.LayoutParams attrs = mWindow.getAttributes();
1056        int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1057
1058        // IME is an exceptional floating window that requires color view.
1059        final boolean isImeWindow =
1060                mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1061        if (!mWindow.mIsFloating || isImeWindow) {
1062            boolean disallowAnimate = !isLaidOut();
1063            disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1064                    & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1065            mLastWindowFlags = attrs.flags;
1066
1067            if (insets != null) {
1068                mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
1069                        insets.getSystemWindowInsetTop());
1070                mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
1071                        insets.getSystemWindowInsetBottom());
1072                mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
1073                        insets.getSystemWindowInsetRight());
1074                mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
1075                        insets.getSystemWindowInsetLeft());
1076
1077                // Don't animate if the presence of stable insets has changed, because that
1078                // indicates that the window was either just added and received them for the
1079                // first time, or the window size or position has changed.
1080                boolean hasTopStableInset = insets.getStableInsetTop() != 0;
1081                disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1082                mLastHasTopStableInset = hasTopStableInset;
1083
1084                boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
1085                disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1086                mLastHasBottomStableInset = hasBottomStableInset;
1087
1088                boolean hasRightStableInset = insets.getStableInsetRight() != 0;
1089                disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1090                mLastHasRightStableInset = hasRightStableInset;
1091
1092                boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
1093                disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1094                mLastHasLeftStableInset = hasLeftStableInset;
1095
1096                mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
1097            }
1098
1099            boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
1100            boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1101            int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
1102            updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
1103                    mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
1104                    navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
1105                    0 /* sideInset */, animate && !disallowAnimate, false /* force */);
1106
1107            boolean statusBarNeedsRightInset = navBarToRightEdge
1108                    && mNavigationColorViewState.present;
1109            boolean statusBarNeedsLeftInset = navBarToLeftEdge
1110                    && mNavigationColorViewState.present;
1111            int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1112                    : statusBarNeedsLeftInset ? mLastLeftInset : 0;
1113            updateColorViewInt(mStatusColorViewState, sysUiVisibility,
1114                    calculateStatusBarColor(), 0, mLastTopInset,
1115                    false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1116                    animate && !disallowAnimate,
1117                    mForceWindowDrawsStatusBarBackground);
1118        }
1119
1120        // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
1121        // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
1122        // explicitly asked for it.
1123        boolean consumingNavBar =
1124                (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1125                        && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1126                        && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
1127                || mLastShouldAlwaysConsumeNavBar;
1128
1129        // If we didn't request fullscreen layout, but we still got it because of the
1130        // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
1131        boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1132                && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1133                && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1134                && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1135                && mForceWindowDrawsStatusBarBackground
1136                && mLastTopInset != 0;
1137
1138        int consumedTop = consumingStatusBar ? mLastTopInset : 0;
1139        int consumedRight = consumingNavBar ? mLastRightInset : 0;
1140        int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
1141        int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
1142
1143        if (mContentRoot != null
1144                && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1145            MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
1146            if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
1147                    || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
1148                lp.topMargin = consumedTop;
1149                lp.rightMargin = consumedRight;
1150                lp.bottomMargin = consumedBottom;
1151                lp.leftMargin = consumedLeft;
1152                mContentRoot.setLayoutParams(lp);
1153
1154                if (insets == null) {
1155                    // The insets have changed, but we're not currently in the process
1156                    // of dispatching them.
1157                    requestApplyInsets();
1158                }
1159            }
1160            if (insets != null) {
1161                insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
1162            }
1163        }
1164
1165        if (insets != null) {
1166            insets = insets.consumeStableInsets();
1167        }
1168        return insets;
1169    }
1170
1171    private int calculateStatusBarColor() {
1172        return calculateStatusBarColor(mWindow.getAttributes().flags,
1173                mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
1174    }
1175
1176    public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
1177            int statusBarColor) {
1178        return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
1179                : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
1180                : Color.BLACK;
1181    }
1182
1183    private int getCurrentColor(ColorViewState state) {
1184        if (state.visible) {
1185            return state.color;
1186        } else {
1187            return 0;
1188        }
1189    }
1190
1191    /**
1192     * Update a color view
1193     *
1194     * @param state the color view to update.
1195     * @param sysUiVis the current systemUiVisibility to apply.
1196     * @param color the current color to apply.
1197     * @param dividerColor the current divider color to apply.
1198     * @param size the current size in the non-parent-matching dimension.
1199     * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1200     *                    horizontal edge,
1201     * @param sideMargin sideMargin for the color view.
1202     * @param animate if true, the change will be animated.
1203     */
1204    private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
1205            int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
1206            boolean animate, boolean force) {
1207        state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
1208        boolean show = state.attributes.isVisible(state.present, color,
1209                mWindow.getAttributes().flags, force);
1210        boolean showView = show && !isResizing() && size > 0;
1211
1212        boolean visibilityChanged = false;
1213        View view = state.view;
1214
1215        int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1216        int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
1217        int resolvedGravity = verticalBar
1218                ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1219                : state.attributes.verticalGravity;
1220
1221        if (view == null) {
1222            if (showView) {
1223                state.view = view = new View(mContext);
1224                setColor(view, color, dividerColor, verticalBar, seascape);
1225                view.setTransitionName(state.attributes.transitionName);
1226                view.setId(state.attributes.id);
1227                visibilityChanged = true;
1228                view.setVisibility(INVISIBLE);
1229                state.targetVisibility = VISIBLE;
1230
1231                LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1232                        resolvedGravity);
1233                if (seascape) {
1234                    lp.leftMargin = sideMargin;
1235                } else {
1236                    lp.rightMargin = sideMargin;
1237                }
1238                addView(view, lp);
1239                updateColorViewTranslations();
1240            }
1241        } else {
1242            int vis = showView ? VISIBLE : INVISIBLE;
1243            visibilityChanged = state.targetVisibility != vis;
1244            state.targetVisibility = vis;
1245            LayoutParams lp = (LayoutParams) view.getLayoutParams();
1246            int rightMargin = seascape ? 0 : sideMargin;
1247            int leftMargin = seascape ? sideMargin : 0;
1248            if (lp.height != resolvedHeight || lp.width != resolvedWidth
1249                    || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1250                    || lp.leftMargin != leftMargin) {
1251                lp.height = resolvedHeight;
1252                lp.width = resolvedWidth;
1253                lp.gravity = resolvedGravity;
1254                lp.rightMargin = rightMargin;
1255                lp.leftMargin = leftMargin;
1256                view.setLayoutParams(lp);
1257            }
1258            if (showView) {
1259                setColor(view, color, dividerColor, verticalBar, seascape);
1260            }
1261        }
1262        if (visibilityChanged) {
1263            view.animate().cancel();
1264            if (animate && !isResizing()) {
1265                if (showView) {
1266                    if (view.getVisibility() != VISIBLE) {
1267                        view.setVisibility(VISIBLE);
1268                        view.setAlpha(0.0f);
1269                    }
1270                    view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1271                            setDuration(mBarEnterExitDuration);
1272                } else {
1273                    view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1274                            .setDuration(mBarEnterExitDuration)
1275                            .withEndAction(new Runnable() {
1276                                @Override
1277                                public void run() {
1278                                    state.view.setAlpha(1.0f);
1279                                    state.view.setVisibility(INVISIBLE);
1280                                }
1281                            });
1282                }
1283            } else {
1284                view.setAlpha(1.0f);
1285                view.setVisibility(showView ? VISIBLE : INVISIBLE);
1286            }
1287        }
1288        state.visible = show;
1289        state.color = color;
1290    }
1291
1292    private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1293            boolean seascape) {
1294        if (dividerColor != 0) {
1295            final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1296            if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1297                final int size = Math.round(
1298                        TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1299                                v.getContext().getResources().getDisplayMetrics()));
1300                // Use an inset to make the divider line on the side that faces the app.
1301                final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1302                        verticalBar && !seascape ? size : 0,
1303                        !verticalBar ? size : 0,
1304                        verticalBar && seascape ? size : 0, 0);
1305                v.setBackground(new LayerDrawable(new Drawable[] {
1306                        new ColorDrawable(dividerColor), d }));
1307                v.setTag(new Pair<>(verticalBar, seascape));
1308            } else {
1309                final LayerDrawable d = (LayerDrawable) v.getBackground();
1310                final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1311                ((ColorDrawable) inset.getDrawable()).setColor(color);
1312                ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1313            }
1314        } else {
1315            v.setTag(null);
1316            v.setBackgroundColor(color);
1317        }
1318    }
1319
1320    private void updateColorViewTranslations() {
1321        // Put the color views back in place when they get moved off the screen
1322        // due to the the ViewRootImpl panning.
1323        int rootScrollY = mRootScrollY;
1324        if (mStatusColorViewState.view != null) {
1325            mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1326        }
1327        if (mNavigationColorViewState.view != null) {
1328            mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1329        }
1330    }
1331
1332    private WindowInsets updateStatusGuard(WindowInsets insets) {
1333        boolean showStatusGuard = false;
1334        // Show the status guard when the non-overlay contextual action bar is showing
1335        if (mPrimaryActionModeView != null) {
1336            if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1337                // Insets are magic!
1338                final MarginLayoutParams mlp = (MarginLayoutParams)
1339                        mPrimaryActionModeView.getLayoutParams();
1340                boolean mlpChanged = false;
1341                if (mPrimaryActionModeView.isShown()) {
1342                    if (mTempRect == null) {
1343                        mTempRect = new Rect();
1344                    }
1345                    final Rect rect = mTempRect;
1346
1347                    // If the parent doesn't consume the insets, manually
1348                    // apply the default system window insets.
1349                    mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1350                    final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1351                    if (mlp.topMargin != newMargin) {
1352                        mlpChanged = true;
1353                        mlp.topMargin = insets.getSystemWindowInsetTop();
1354
1355                        if (mStatusGuard == null) {
1356                            mStatusGuard = new View(mContext);
1357                            mStatusGuard.setBackgroundColor(mContext.getColor(
1358                                    R.color.decor_view_status_guard));
1359                            addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1360                                    new LayoutParams(LayoutParams.MATCH_PARENT,
1361                                            mlp.topMargin, Gravity.START | Gravity.TOP));
1362                        } else {
1363                            final LayoutParams lp = (LayoutParams)
1364                                    mStatusGuard.getLayoutParams();
1365                            if (lp.height != mlp.topMargin) {
1366                                lp.height = mlp.topMargin;
1367                                mStatusGuard.setLayoutParams(lp);
1368                            }
1369                        }
1370                    }
1371
1372                    // The action mode's theme may differ from the app, so
1373                    // always show the status guard above it if we have one.
1374                    showStatusGuard = mStatusGuard != null;
1375
1376                    // We only need to consume the insets if the action
1377                    // mode is overlaid on the app content (e.g. it's
1378                    // sitting in a FrameLayout, see
1379                    // screen_simple_overlay_action_mode.xml).
1380                    final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1381                            & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1382                    if (nonOverlay && showStatusGuard) {
1383                        insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1384                    }
1385                } else {
1386                    // reset top margin
1387                    if (mlp.topMargin != 0) {
1388                        mlpChanged = true;
1389                        mlp.topMargin = 0;
1390                    }
1391                }
1392                if (mlpChanged) {
1393                    mPrimaryActionModeView.setLayoutParams(mlp);
1394                }
1395            }
1396        }
1397        if (mStatusGuard != null) {
1398            mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1399        }
1400        return insets;
1401    }
1402
1403    /**
1404     * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1405     * an opaque shadow even if the window background is completely transparent. This only applies
1406     * to activities that are currently the task root.
1407     */
1408    public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1409        if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1410            return;
1411        }
1412
1413        if (isInPictureInPictureMode) {
1414            final Window.WindowControllerCallback callback =
1415                    mWindow.getWindowControllerCallback();
1416            if (callback != null && callback.isTaskRoot()) {
1417                // Call super implementation directly as we don't want to save the PIP outline
1418                // provider to be restored
1419                super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1420            }
1421        } else {
1422            // Restore the previous outline provider
1423            if (getOutlineProvider() != mLastOutlineProvider) {
1424                setOutlineProvider(mLastOutlineProvider);
1425            }
1426        }
1427        mIsInPictureInPictureMode = isInPictureInPictureMode;
1428    }
1429
1430    @Override
1431    public void setOutlineProvider(ViewOutlineProvider provider) {
1432        super.setOutlineProvider(provider);
1433
1434        // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1435        mLastOutlineProvider = provider;
1436    }
1437
1438    private void drawableChanged() {
1439        if (mChanging) {
1440            return;
1441        }
1442
1443        setPadding(mFramePadding.left + mBackgroundPadding.left,
1444                mFramePadding.top + mBackgroundPadding.top,
1445                mFramePadding.right + mBackgroundPadding.right,
1446                mFramePadding.bottom + mBackgroundPadding.bottom);
1447        requestLayout();
1448        invalidate();
1449
1450        int opacity = PixelFormat.OPAQUE;
1451        final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1452        if (winConfig.hasWindowShadow()) {
1453            // If the window has a shadow, it must be translucent.
1454            opacity = PixelFormat.TRANSLUCENT;
1455        } else{
1456            // Note: If there is no background, we will assume opaque. The
1457            // common case seems to be that an application sets there to be
1458            // no background so it can draw everything itself. For that,
1459            // we would like to assume OPAQUE and let the app force it to
1460            // the slower TRANSLUCENT mode if that is really what it wants.
1461            Drawable bg = getBackground();
1462            Drawable fg = getForeground();
1463            if (bg != null) {
1464                if (fg == null) {
1465                    opacity = bg.getOpacity();
1466                } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1467                        && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1468                    // If the frame padding is zero, then we can be opaque
1469                    // if either the frame -or- the background is opaque.
1470                    int fop = fg.getOpacity();
1471                    int bop = bg.getOpacity();
1472                    if (false)
1473                        Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
1474                    if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1475                        opacity = PixelFormat.OPAQUE;
1476                    } else if (fop == PixelFormat.UNKNOWN) {
1477                        opacity = bop;
1478                    } else if (bop == PixelFormat.UNKNOWN) {
1479                        opacity = fop;
1480                    } else {
1481                        opacity = Drawable.resolveOpacity(fop, bop);
1482                    }
1483                } else {
1484                    // For now we have to assume translucent if there is a
1485                    // frame with padding... there is no way to tell if the
1486                    // frame and background together will draw all pixels.
1487                    if (false)
1488                        Log.v(mLogTag, "Padding: " + mFramePadding);
1489                    opacity = PixelFormat.TRANSLUCENT;
1490                }
1491            }
1492            if (false)
1493                Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
1494        }
1495
1496        if (false)
1497            Log.v(mLogTag, "Selected default opacity: " + opacity);
1498
1499        mDefaultOpacity = opacity;
1500        if (mFeatureId < 0) {
1501            mWindow.setDefaultWindowFormat(opacity);
1502        }
1503    }
1504
1505    @Override
1506    public void onWindowFocusChanged(boolean hasWindowFocus) {
1507        super.onWindowFocusChanged(hasWindowFocus);
1508
1509        // If the user is chording a menu shortcut, release the chord since
1510        // this window lost focus
1511        if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1512                && mWindow.mPanelChordingKey != 0) {
1513            mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1514        }
1515
1516        final Window.Callback cb = mWindow.getCallback();
1517        if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1518            cb.onWindowFocusChanged(hasWindowFocus);
1519        }
1520
1521        if (mPrimaryActionMode != null) {
1522            mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1523        }
1524        if (mFloatingActionMode != null) {
1525            mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1526        }
1527
1528        updateElevation();
1529    }
1530
1531    @Override
1532    protected void onAttachedToWindow() {
1533        super.onAttachedToWindow();
1534
1535        final Window.Callback cb = mWindow.getCallback();
1536        if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1537            cb.onAttachedToWindow();
1538        }
1539
1540        if (mFeatureId == -1) {
1541            /*
1542             * The main window has been attached, try to restore any panels
1543             * that may have been open before. This is called in cases where
1544             * an activity is being killed for configuration change and the
1545             * menu was open. When the activity is recreated, the menu
1546             * should be shown again.
1547             */
1548            mWindow.openPanelsAfterRestore();
1549        }
1550
1551        if (!mWindowResizeCallbacksAdded) {
1552            // If there is no window callback installed there was no window set before. Set it now.
1553            // Note that our ViewRootImpl object will not change.
1554            getViewRootImpl().addWindowCallbacks(this);
1555            mWindowResizeCallbacksAdded = true;
1556        } else if (mBackdropFrameRenderer != null) {
1557            // We are resizing and this call happened due to a configuration change. Tell the
1558            // renderer about it.
1559            mBackdropFrameRenderer.onConfigurationChange();
1560        }
1561        mWindow.onViewRootImplSet(getViewRootImpl());
1562    }
1563
1564    @Override
1565    protected void onDetachedFromWindow() {
1566        super.onDetachedFromWindow();
1567
1568        final Window.Callback cb = mWindow.getCallback();
1569        if (cb != null && mFeatureId < 0) {
1570            cb.onDetachedFromWindow();
1571        }
1572
1573        if (mWindow.mDecorContentParent != null) {
1574            mWindow.mDecorContentParent.dismissPopups();
1575        }
1576
1577        if (mPrimaryActionModePopup != null) {
1578            removeCallbacks(mShowPrimaryActionModePopup);
1579            if (mPrimaryActionModePopup.isShowing()) {
1580                mPrimaryActionModePopup.dismiss();
1581            }
1582            mPrimaryActionModePopup = null;
1583        }
1584        if (mFloatingToolbar != null) {
1585            mFloatingToolbar.dismiss();
1586            mFloatingToolbar = null;
1587        }
1588
1589        PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1590        if (st != null && st.menu != null && mFeatureId < 0) {
1591            st.menu.close();
1592        }
1593
1594        releaseThreadedRenderer();
1595
1596        if (mWindowResizeCallbacksAdded) {
1597            getViewRootImpl().removeWindowCallbacks(this);
1598            mWindowResizeCallbacksAdded = false;
1599        }
1600    }
1601
1602    @Override
1603    public void onCloseSystemDialogs(String reason) {
1604        if (mFeatureId >= 0) {
1605            mWindow.closeAllPanels();
1606        }
1607    }
1608
1609    public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1610        return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1611    }
1612
1613    public InputQueue.Callback willYouTakeTheInputQueue() {
1614        return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1615    }
1616
1617    public void setSurfaceType(int type) {
1618        mWindow.setType(type);
1619    }
1620
1621    public void setSurfaceFormat(int format) {
1622        mWindow.setFormat(format);
1623    }
1624
1625    public void setSurfaceKeepScreenOn(boolean keepOn) {
1626        if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1627        else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1628    }
1629
1630    @Override
1631    public void onRootViewScrollYChanged(int rootScrollY) {
1632        mRootScrollY = rootScrollY;
1633        updateColorViewTranslations();
1634    }
1635
1636    private ActionMode createActionMode(
1637            int type, ActionMode.Callback2 callback, View originatingView) {
1638        switch (type) {
1639            case ActionMode.TYPE_PRIMARY:
1640            default:
1641                return createStandaloneActionMode(callback);
1642            case ActionMode.TYPE_FLOATING:
1643                return createFloatingActionMode(originatingView, callback);
1644        }
1645    }
1646
1647    private void setHandledActionMode(ActionMode mode) {
1648        if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1649            setHandledPrimaryActionMode(mode);
1650        } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1651            setHandledFloatingActionMode(mode);
1652        }
1653    }
1654
1655    private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1656        endOnGoingFadeAnimation();
1657        cleanupPrimaryActionMode();
1658        // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1659        // instance at all, or if there is one, but it is detached from window. The latter case
1660        // might happen when app is resized in multi-window mode and decor view is preserved
1661        // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1662        // app memory leaks because killMode() is called when the dismiss animation ends and from
1663        // cleanupPrimaryActionMode() invocation above.
1664        if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
1665            if (mWindow.isFloating()) {
1666                // Use the action bar theme.
1667                final TypedValue outValue = new TypedValue();
1668                final Resources.Theme baseTheme = mContext.getTheme();
1669                baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1670
1671                final Context actionBarContext;
1672                if (outValue.resourceId != 0) {
1673                    final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1674                    actionBarTheme.setTo(baseTheme);
1675                    actionBarTheme.applyStyle(outValue.resourceId, true);
1676
1677                    actionBarContext = new ContextThemeWrapper(mContext, 0);
1678                    actionBarContext.getTheme().setTo(actionBarTheme);
1679                } else {
1680                    actionBarContext = mContext;
1681                }
1682
1683                mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1684                mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1685                        R.attr.actionModePopupWindowStyle);
1686                mPrimaryActionModePopup.setWindowLayoutType(
1687                        WindowManager.LayoutParams.TYPE_APPLICATION);
1688                mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1689                mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1690
1691                actionBarContext.getTheme().resolveAttribute(
1692                        R.attr.actionBarSize, outValue, true);
1693                final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1694                        actionBarContext.getResources().getDisplayMetrics());
1695                mPrimaryActionModeView.setContentHeight(height);
1696                mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1697                mShowPrimaryActionModePopup = new Runnable() {
1698                    public void run() {
1699                        mPrimaryActionModePopup.showAtLocation(
1700                                mPrimaryActionModeView.getApplicationWindowToken(),
1701                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1702                        endOnGoingFadeAnimation();
1703
1704                        if (shouldAnimatePrimaryActionModeView()) {
1705                            mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1706                                    0f, 1f);
1707                            mFadeAnim.addListener(new AnimatorListenerAdapter() {
1708                                @Override
1709                                public void onAnimationStart(Animator animation) {
1710                                    mPrimaryActionModeView.setVisibility(VISIBLE);
1711                                }
1712
1713                                @Override
1714                                public void onAnimationEnd(Animator animation) {
1715                                    mPrimaryActionModeView.setAlpha(1f);
1716                                    mFadeAnim = null;
1717                                }
1718                            });
1719                            mFadeAnim.start();
1720                        } else {
1721                            mPrimaryActionModeView.setAlpha(1f);
1722                            mPrimaryActionModeView.setVisibility(VISIBLE);
1723                        }
1724                    }
1725                };
1726            } else {
1727                ViewStub stub = findViewById(R.id.action_mode_bar_stub);
1728                if (stub != null) {
1729                    mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1730                    mPrimaryActionModePopup = null;
1731                }
1732            }
1733        }
1734        if (mPrimaryActionModeView != null) {
1735            mPrimaryActionModeView.killMode();
1736            ActionMode mode = new StandaloneActionMode(
1737                    mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1738                    callback, mPrimaryActionModePopup == null);
1739            return mode;
1740        }
1741        return null;
1742    }
1743
1744    private void endOnGoingFadeAnimation() {
1745        if (mFadeAnim != null) {
1746            mFadeAnim.end();
1747        }
1748    }
1749
1750    private void setHandledPrimaryActionMode(ActionMode mode) {
1751        endOnGoingFadeAnimation();
1752        mPrimaryActionMode = mode;
1753        mPrimaryActionMode.invalidate();
1754        mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1755        if (mPrimaryActionModePopup != null) {
1756            post(mShowPrimaryActionModePopup);
1757        } else {
1758            if (shouldAnimatePrimaryActionModeView()) {
1759                mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1760                mFadeAnim.addListener(new AnimatorListenerAdapter() {
1761                    @Override
1762                    public void onAnimationStart(Animator animation) {
1763                        mPrimaryActionModeView.setVisibility(View.VISIBLE);
1764                    }
1765
1766                    @Override
1767                    public void onAnimationEnd(Animator animation) {
1768                        mPrimaryActionModeView.setAlpha(1f);
1769                        mFadeAnim = null;
1770                    }
1771                });
1772                mFadeAnim.start();
1773            } else {
1774                mPrimaryActionModeView.setAlpha(1f);
1775                mPrimaryActionModeView.setVisibility(View.VISIBLE);
1776            }
1777        }
1778        mPrimaryActionModeView.sendAccessibilityEvent(
1779                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1780    }
1781
1782    boolean shouldAnimatePrimaryActionModeView() {
1783        // We only to animate the action mode in if the decor has already been laid out.
1784        // If it hasn't been laid out, it hasn't been drawn to screen yet.
1785        return isLaidOut();
1786    }
1787
1788    private ActionMode createFloatingActionMode(
1789            View originatingView, ActionMode.Callback2 callback) {
1790        if (mFloatingActionMode != null) {
1791            mFloatingActionMode.finish();
1792        }
1793        cleanupFloatingActionModeViews();
1794        mFloatingToolbar = new FloatingToolbar(mWindow);
1795        final FloatingActionMode mode =
1796                new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
1797        mFloatingActionModeOriginatingView = originatingView;
1798        mFloatingToolbarPreDrawListener =
1799            new ViewTreeObserver.OnPreDrawListener() {
1800                @Override
1801                public boolean onPreDraw() {
1802                    mode.updateViewLocationInWindow();
1803                    return true;
1804                }
1805            };
1806        return mode;
1807    }
1808
1809    private void setHandledFloatingActionMode(ActionMode mode) {
1810        mFloatingActionMode = mode;
1811        mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
1812        mFloatingActionModeOriginatingView.getViewTreeObserver()
1813            .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1814    }
1815
1816    /**
1817     * Informs the decor if the caption is attached and visible.
1818     * @param attachedAndVisible true when the decor is visible.
1819     * Note that this will even be called if there is no caption.
1820     **/
1821    void enableCaption(boolean attachedAndVisible) {
1822        if (mHasCaption != attachedAndVisible) {
1823            mHasCaption = attachedAndVisible;
1824            if (getForeground() != null) {
1825                drawableChanged();
1826            }
1827        }
1828    }
1829
1830    void setWindow(PhoneWindow phoneWindow) {
1831        mWindow = phoneWindow;
1832        Context context = getContext();
1833        if (context instanceof DecorContext) {
1834            DecorContext decorContext = (DecorContext) context;
1835            decorContext.setPhoneWindow(mWindow);
1836        }
1837    }
1838
1839    @Override
1840    public Resources getResources() {
1841        // Make sure the Resources object is propogated from the Context since it can be updated in
1842        // the Context object.
1843        return getContext().getResources();
1844    }
1845
1846    @Override
1847    protected void onConfigurationChanged(Configuration newConfig) {
1848        super.onConfigurationChanged(newConfig);
1849
1850        final boolean displayWindowDecor =
1851                newConfig.windowConfiguration.hasWindowDecorCaption();
1852        if (mDecorCaptionView == null && displayWindowDecor) {
1853            // Configuration now requires a caption.
1854            final LayoutInflater inflater = mWindow.getLayoutInflater();
1855            mDecorCaptionView = createDecorCaptionView(inflater);
1856            if (mDecorCaptionView != null) {
1857                if (mDecorCaptionView.getParent() == null) {
1858                    addView(mDecorCaptionView, 0,
1859                            new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1860                }
1861                removeView(mContentRoot);
1862                mDecorCaptionView.addView(mContentRoot,
1863                        new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1864            }
1865        } else if (mDecorCaptionView != null) {
1866            // We might have to change the kind of surface before we do anything else.
1867            mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
1868            enableCaption(displayWindowDecor);
1869        }
1870
1871        updateAvailableWidth();
1872        initializeElevation();
1873    }
1874
1875    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
1876        if (mBackdropFrameRenderer != null) {
1877            loadBackgroundDrawablesIfNeeded();
1878            mBackdropFrameRenderer.onResourcesLoaded(
1879                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
1880                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1881                    getCurrentColor(mNavigationColorViewState));
1882        }
1883
1884        mDecorCaptionView = createDecorCaptionView(inflater);
1885        final View root = inflater.inflate(layoutResource, null);
1886        if (mDecorCaptionView != null) {
1887            if (mDecorCaptionView.getParent() == null) {
1888                addView(mDecorCaptionView,
1889                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1890            }
1891            mDecorCaptionView.addView(root,
1892                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1893        } else {
1894
1895            // Put it below the color views.
1896            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1897        }
1898        mContentRoot = (ViewGroup) root;
1899        initializeElevation();
1900    }
1901
1902    private void loadBackgroundDrawablesIfNeeded() {
1903        if (mResizingBackgroundDrawable == null) {
1904            mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
1905                    mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource,
1906                    mWindow.isTranslucent() || mWindow.isShowingWallpaper());
1907            if (mResizingBackgroundDrawable == null) {
1908                // We shouldn't really get here as the background fallback should be always
1909                // available since it is defaulted by the system.
1910                Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
1911            }
1912        }
1913        if (mCaptionBackgroundDrawable == null) {
1914            mCaptionBackgroundDrawable = getContext().getDrawable(
1915                    R.drawable.decor_caption_title_focused);
1916        }
1917        if (mResizingBackgroundDrawable != null) {
1918            mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
1919            mResizingBackgroundDrawable.setCallback(null);
1920        }
1921    }
1922
1923    // Free floating overlapping windows require a caption.
1924    private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
1925        DecorCaptionView decorCaptionView = null;
1926        for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
1927            View view = getChildAt(i);
1928            if (view instanceof DecorCaptionView) {
1929                // The decor was most likely saved from a relaunch - so reuse it.
1930                decorCaptionView = (DecorCaptionView) view;
1931                removeViewAt(i);
1932            }
1933        }
1934        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
1935        final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
1936                attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
1937        final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1938        // Only a non floating application window on one of the allowed workspaces can get a caption
1939        if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
1940            // Dependent on the brightness of the used title we either use the
1941            // dark or the light button frame.
1942            if (decorCaptionView == null) {
1943                decorCaptionView = inflateDecorCaptionView(inflater);
1944            }
1945            decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
1946        } else {
1947            decorCaptionView = null;
1948        }
1949
1950        // Tell the decor if it has a visible caption.
1951        enableCaption(decorCaptionView != null);
1952        return decorCaptionView;
1953    }
1954
1955    private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1956        final Context context = getContext();
1957        // We make a copy of the inflater, so it has the right context associated with it.
1958        inflater = inflater.from(context);
1959        final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1960                null);
1961        setDecorCaptionShade(context, view);
1962        return view;
1963    }
1964
1965    private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1966        final int shade = mWindow.getDecorCaptionShade();
1967        switch (shade) {
1968            case DECOR_CAPTION_SHADE_LIGHT:
1969                setLightDecorCaptionShade(view);
1970                break;
1971            case DECOR_CAPTION_SHADE_DARK:
1972                setDarkDecorCaptionShade(view);
1973                break;
1974            default: {
1975                TypedValue value = new TypedValue();
1976                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1977                // We invert the shade depending on brightness of the theme. Dark shade for light
1978                // theme and vice versa. Thanks to this the buttons should be visible on the
1979                // background.
1980                if (Color.luminance(value.data) < 0.5) {
1981                    setLightDecorCaptionShade(view);
1982                } else {
1983                    setDarkDecorCaptionShade(view);
1984                }
1985                break;
1986            }
1987        }
1988    }
1989
1990    void updateDecorCaptionShade() {
1991        if (mDecorCaptionView != null) {
1992            setDecorCaptionShade(getContext(), mDecorCaptionView);
1993        }
1994    }
1995
1996    private void setLightDecorCaptionShade(DecorCaptionView view) {
1997        view.findViewById(R.id.maximize_window).setBackgroundResource(
1998                R.drawable.decor_maximize_button_light);
1999        view.findViewById(R.id.close_window).setBackgroundResource(
2000                R.drawable.decor_close_button_light);
2001    }
2002
2003    private void setDarkDecorCaptionShade(DecorCaptionView view) {
2004        view.findViewById(R.id.maximize_window).setBackgroundResource(
2005                R.drawable.decor_maximize_button_dark);
2006        view.findViewById(R.id.close_window).setBackgroundResource(
2007                R.drawable.decor_close_button_dark);
2008    }
2009
2010    /**
2011     * Returns the color used to fill areas the app has not rendered content to yet when the
2012     * user is resizing the window of an activity in multi-window mode.
2013     */
2014    public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
2015            int backgroundFallbackRes, boolean windowTranslucent) {
2016        if (backgroundRes != 0) {
2017            final Drawable drawable = context.getDrawable(backgroundRes);
2018            if (drawable != null) {
2019                return enforceNonTranslucentBackground(drawable, windowTranslucent);
2020            }
2021        }
2022
2023        if (backgroundFallbackRes != 0) {
2024            final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
2025            if (fallbackDrawable != null) {
2026                return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
2027            }
2028        }
2029        return new ColorDrawable(Color.BLACK);
2030    }
2031
2032    /**
2033     * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2034     * window is not translucent.
2035     */
2036    private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2037            boolean windowTranslucent) {
2038        if (!windowTranslucent && drawable instanceof ColorDrawable) {
2039            ColorDrawable colorDrawable = (ColorDrawable) drawable;
2040            int color = colorDrawable.getColor();
2041            if (Color.alpha(color) != 255) {
2042                ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2043                        .mutate();
2044                copy.setColor(
2045                        Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2046                return copy;
2047            }
2048        }
2049        return drawable;
2050    }
2051
2052    void clearContentView() {
2053        if (mDecorCaptionView != null) {
2054            mDecorCaptionView.removeContentView();
2055        } else {
2056            // This window doesn't have caption, so we need to remove everything except our views
2057            // we might have added.
2058            for (int i = getChildCount() - 1; i >= 0; i--) {
2059                View v = getChildAt(i);
2060                if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
2061                        && v != mStatusGuard) {
2062                    removeViewAt(i);
2063                }
2064            }
2065        }
2066    }
2067
2068    @Override
2069    public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2070            Rect stableInsets) {
2071        if (mBackdropFrameRenderer != null) {
2072            mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
2073        }
2074    }
2075
2076    @Override
2077    public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
2078            Rect stableInsets, int resizeMode) {
2079        if (mWindow.isDestroyed()) {
2080            // If the owner's window is gone, we should not be able to come here anymore.
2081            releaseThreadedRenderer();
2082            return;
2083        }
2084        if (mBackdropFrameRenderer != null) {
2085            return;
2086        }
2087        final ThreadedRenderer renderer = getThreadedRenderer();
2088        if (renderer != null) {
2089            loadBackgroundDrawablesIfNeeded();
2090            mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
2091                    initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2092                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2093                    getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
2094                    stableInsets, resizeMode);
2095
2096            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2097            // If we want to get the shadow shown while resizing, we would need to elevate a new
2098            // element which owns the caption and has the elevation.
2099            updateElevation();
2100
2101            updateColorViews(null /* insets */, false);
2102        }
2103        mResizeMode = resizeMode;
2104        getViewRootImpl().requestInvalidateRootRenderNode();
2105    }
2106
2107    @Override
2108    public void onWindowDragResizeEnd() {
2109        releaseThreadedRenderer();
2110        updateColorViews(null /* insets */, false);
2111        mResizeMode = RESIZE_MODE_INVALID;
2112        getViewRootImpl().requestInvalidateRootRenderNode();
2113    }
2114
2115    @Override
2116    public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2117        if (mBackdropFrameRenderer == null) {
2118            return false;
2119        }
2120        return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2121    }
2122
2123    @Override
2124    public void onRequestDraw(boolean reportNextDraw) {
2125        if (mBackdropFrameRenderer != null) {
2126            mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2127        } else if (reportNextDraw) {
2128            // If render thread is gone, just report immediately.
2129            if (isAttachedToWindow()) {
2130                getViewRootImpl().reportDrawFinish();
2131            }
2132        }
2133    }
2134
2135    @Override
2136    public void onPostDraw(DisplayListCanvas canvas) {
2137        drawResizingShadowIfNeeded(canvas);
2138    }
2139
2140    private void initResizingPaints() {
2141        final int startColor = mContext.getResources().getColor(
2142                R.color.resize_shadow_start_color, null);
2143        final int endColor = mContext.getResources().getColor(
2144                R.color.resize_shadow_end_color, null);
2145        final int middleColor = (startColor + endColor) / 2;
2146        mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2147                0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2148                new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2149        mVerticalResizeShadowPaint.setShader(new LinearGradient(
2150                0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2151                new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2152    }
2153
2154    private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
2155        if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2156                || mWindow.isTranslucent()
2157                || mWindow.isShowingWallpaper()) {
2158            return;
2159        }
2160        canvas.save();
2161        canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2162        canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2163        canvas.restore();
2164        canvas.save();
2165        canvas.translate(getWidth() - mFrameOffsets.right, 0);
2166        canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2167        canvas.restore();
2168    }
2169
2170    /** Release the renderer thread which is usually done when the user stops resizing. */
2171    private void releaseThreadedRenderer() {
2172        if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2173            mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2174            mLastBackgroundDrawableCb = null;
2175        }
2176
2177        if (mBackdropFrameRenderer != null) {
2178            mBackdropFrameRenderer.releaseRenderer();
2179            mBackdropFrameRenderer = null;
2180            // Bring the shadow back.
2181            updateElevation();
2182        }
2183    }
2184
2185    private boolean isResizing() {
2186        return mBackdropFrameRenderer != null;
2187    }
2188
2189    /**
2190     * The elevation gets set for the first time and the framework needs to be informed that
2191     * the surface layer gets created with the shadow size in mind.
2192     */
2193    private void initializeElevation() {
2194        // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2195        mAllowUpdateElevation = false;
2196        updateElevation();
2197    }
2198
2199    private void updateElevation() {
2200        float elevation = 0;
2201        final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2202        // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2203        // since the shadow is bound to the content size and not the target size.
2204        final int windowingMode =
2205                getResources().getConfiguration().windowConfiguration.getWindowingMode();
2206        if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
2207            elevation = hasWindowFocus() ?
2208                    DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
2209            // Add a maximum shadow height value to the top level view.
2210            // Note that pinned stack doesn't have focus
2211            // so maximum shadow height adjustment isn't needed.
2212            // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
2213            if (!mAllowUpdateElevation) {
2214                elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2215            }
2216            // Convert the DP elevation into physical pixels.
2217            elevation = dipToPx(elevation);
2218            mElevationAdjustedForStack = true;
2219        } else if (windowingMode == WINDOWING_MODE_PINNED) {
2220            elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
2221            mElevationAdjustedForStack = true;
2222        } else {
2223            mElevationAdjustedForStack = false;
2224        }
2225
2226        // Don't change the elevation if we didn't previously adjust it for the stack it was in
2227        // or it didn't change.
2228        if ((wasAdjustedForStack || mElevationAdjustedForStack)
2229                && getElevation() != elevation) {
2230            mWindow.setElevation(elevation);
2231        }
2232    }
2233
2234    boolean isShowingCaption() {
2235        return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
2236    }
2237
2238    int getCaptionHeight() {
2239        return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
2240    }
2241
2242    /**
2243     * Converts a DIP measure into physical pixels.
2244     * @param dip The dip value.
2245     * @return Returns the number of pixels.
2246     */
2247    private float dipToPx(float dip) {
2248        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2249                getResources().getDisplayMetrics());
2250    }
2251
2252    /**
2253     * Provide an override of the caption background drawable.
2254     */
2255    void setUserCaptionBackgroundDrawable(Drawable drawable) {
2256        mUserCaptionBackgroundDrawable = drawable;
2257        if (mBackdropFrameRenderer != null) {
2258            mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2259        }
2260    }
2261
2262    private static String getTitleSuffix(WindowManager.LayoutParams params) {
2263        if (params == null) {
2264            return "";
2265        }
2266        final String[] split = params.getTitle().toString().split("\\.");
2267        if (split.length > 0) {
2268            return split[split.length - 1];
2269        } else {
2270            return "";
2271        }
2272    }
2273
2274    void updateLogTag(WindowManager.LayoutParams params) {
2275        mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2276    }
2277
2278    private void updateAvailableWidth() {
2279        Resources res = getResources();
2280        mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2281                res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2282    }
2283
2284    /**
2285     * @hide
2286     */
2287    @Override
2288    public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2289        final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2290        final Menu menu = st != null ? st.menu : null;
2291        if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2292            mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
2293        }
2294    }
2295
2296    @Override
2297    public void dispatchPointerCaptureChanged(boolean hasCapture) {
2298        super.dispatchPointerCaptureChanged(hasCapture);
2299        if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2300            mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2301        }
2302    }
2303
2304    @Override
2305    public int getAccessibilityViewId() {
2306        return AccessibilityNodeInfo.ROOT_ITEM_ID;
2307    }
2308
2309    @Override
2310    public String toString() {
2311        return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2312                + getTitleSuffix(mWindow.getAttributes()) + "]";
2313    }
2314
2315    private static class ColorViewState {
2316        View view = null;
2317        int targetVisibility = View.INVISIBLE;
2318        boolean present = false;
2319        boolean visible;
2320        int color;
2321
2322        final ColorViewAttributes attributes;
2323
2324        ColorViewState(ColorViewAttributes attributes) {
2325            this.attributes = attributes;
2326        }
2327    }
2328
2329    public static class ColorViewAttributes {
2330
2331        final int id;
2332        final int systemUiHideFlag;
2333        final int translucentFlag;
2334        final int verticalGravity;
2335        final int horizontalGravity;
2336        final int seascapeGravity;
2337        final String transitionName;
2338        final int hideWindowFlag;
2339
2340        private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2341                int horizontalGravity, int seascapeGravity, String transitionName, int id,
2342                int hideWindowFlag) {
2343            this.id = id;
2344            this.systemUiHideFlag = systemUiHideFlag;
2345            this.translucentFlag = translucentFlag;
2346            this.verticalGravity = verticalGravity;
2347            this.horizontalGravity = horizontalGravity;
2348            this.seascapeGravity = seascapeGravity;
2349            this.transitionName = transitionName;
2350            this.hideWindowFlag = hideWindowFlag;
2351        }
2352
2353        public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2354            return (sysUiVis & systemUiHideFlag) == 0
2355                    && (windowFlags & hideWindowFlag) == 0
2356                    && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2357                    || force);
2358        }
2359
2360        public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2361            return present
2362                    && (color & Color.BLACK) != 0
2363                    && ((windowFlags & translucentFlag) == 0  || force);
2364        }
2365
2366        public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2367            final boolean present = isPresent(sysUiVis, windowFlags, force);
2368            return isVisible(present, color, windowFlags, force);
2369        }
2370    }
2371
2372    /**
2373     * Clears out internal references when the action mode is destroyed.
2374     */
2375    private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2376        private final ActionMode.Callback mWrapped;
2377
2378        public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2379            mWrapped = wrapped;
2380        }
2381
2382        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2383            return mWrapped.onCreateActionMode(mode, menu);
2384        }
2385
2386        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2387            requestFitSystemWindows();
2388            return mWrapped.onPrepareActionMode(mode, menu);
2389        }
2390
2391        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2392            return mWrapped.onActionItemClicked(mode, item);
2393        }
2394
2395        public void onDestroyActionMode(ActionMode mode) {
2396            mWrapped.onDestroyActionMode(mode);
2397            final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2398                    >= M;
2399            final boolean isPrimary;
2400            final boolean isFloating;
2401            if (isMncApp) {
2402                isPrimary = mode == mPrimaryActionMode;
2403                isFloating = mode == mFloatingActionMode;
2404                if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
2405                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
2406                            + mode + " was not the current primary action mode! Expected "
2407                            + mPrimaryActionMode);
2408                }
2409                if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
2410                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
2411                            + mode + " was not the current floating action mode! Expected "
2412                            + mFloatingActionMode);
2413                }
2414            } else {
2415                isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2416                isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2417            }
2418            if (isPrimary) {
2419                if (mPrimaryActionModePopup != null) {
2420                    removeCallbacks(mShowPrimaryActionModePopup);
2421                }
2422                if (mPrimaryActionModeView != null) {
2423                    endOnGoingFadeAnimation();
2424                    // Store action mode view reference, so we can access it safely when animation
2425                    // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2426                    // so no need to store reference to it in separate variable.
2427                    final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
2428                    mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2429                            1f, 0f);
2430                    mFadeAnim.addListener(new Animator.AnimatorListener() {
2431
2432                                @Override
2433                                public void onAnimationStart(Animator animation) {
2434
2435                                }
2436
2437                                @Override
2438                                public void onAnimationEnd(Animator animation) {
2439                                    // If mPrimaryActionModeView has changed - it means that we've
2440                                    // cleared the content while preserving decor view. We don't
2441                                    // want to change the state of new instances accidentally here.
2442                                    if (lastActionModeView == mPrimaryActionModeView) {
2443                                        lastActionModeView.setVisibility(GONE);
2444                                        if (mPrimaryActionModePopup != null) {
2445                                            mPrimaryActionModePopup.dismiss();
2446                                        }
2447                                        lastActionModeView.killMode();
2448                                        mFadeAnim = null;
2449                                    }
2450                                }
2451
2452                                @Override
2453                                public void onAnimationCancel(Animator animation) {
2454
2455                                }
2456
2457                                @Override
2458                                public void onAnimationRepeat(Animator animation) {
2459
2460                                }
2461                            });
2462                    mFadeAnim.start();
2463                }
2464
2465                mPrimaryActionMode = null;
2466            } else if (isFloating) {
2467                cleanupFloatingActionModeViews();
2468                mFloatingActionMode = null;
2469            }
2470            if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2471                try {
2472                    mWindow.getCallback().onActionModeFinished(mode);
2473                } catch (AbstractMethodError ame) {
2474                    // Older apps might not implement this callback method.
2475                }
2476            }
2477            requestFitSystemWindows();
2478        }
2479
2480        @Override
2481        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2482            if (mWrapped instanceof ActionMode.Callback2) {
2483                ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2484            } else {
2485                super.onGetContentRect(mode, view, outRect);
2486            }
2487        }
2488    }
2489}
2490