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