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