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