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