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