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