DecorView.java revision c2ec7bf965b00d7cbac699c25aa372a9b5f9fcc3
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.onBeforeAttachedToWindow();
1478            cb.onAttachedToWindow();
1479        }
1480
1481        if (mFeatureId == -1) {
1482            /*
1483             * The main window has been attached, try to restore any panels
1484             * that may have been open before. This is called in cases where
1485             * an activity is being killed for configuration change and the
1486             * menu was open. When the activity is recreated, the menu
1487             * should be shown again.
1488             */
1489            mWindow.openPanelsAfterRestore();
1490        }
1491
1492        if (!mWindowResizeCallbacksAdded) {
1493            // If there is no window callback installed there was no window set before. Set it now.
1494            // Note that our ViewRootImpl object will not change.
1495            getViewRootImpl().addWindowCallbacks(this);
1496            mWindowResizeCallbacksAdded = true;
1497        } else if (mBackdropFrameRenderer != null) {
1498            // We are resizing and this call happened due to a configuration change. Tell the
1499            // renderer about it.
1500            mBackdropFrameRenderer.onConfigurationChange();
1501        }
1502        mWindow.onViewRootImplSet(getViewRootImpl());
1503    }
1504
1505    @Override
1506    protected void onDetachedFromWindow() {
1507        super.onDetachedFromWindow();
1508
1509        final Window.Callback cb = mWindow.getCallback();
1510        if (cb != null && mFeatureId < 0) {
1511            cb.onDetachedFromWindow();
1512        }
1513
1514        if (mWindow.mDecorContentParent != null) {
1515            mWindow.mDecorContentParent.dismissPopups();
1516        }
1517
1518        if (mPrimaryActionModePopup != null) {
1519            removeCallbacks(mShowPrimaryActionModePopup);
1520            if (mPrimaryActionModePopup.isShowing()) {
1521                mPrimaryActionModePopup.dismiss();
1522            }
1523            mPrimaryActionModePopup = null;
1524        }
1525        if (mFloatingToolbar != null) {
1526            mFloatingToolbar.dismiss();
1527            mFloatingToolbar = null;
1528        }
1529
1530        PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1531        if (st != null && st.menu != null && mFeatureId < 0) {
1532            st.menu.close();
1533        }
1534
1535        releaseThreadedRenderer();
1536
1537        if (mWindowResizeCallbacksAdded) {
1538            getViewRootImpl().removeWindowCallbacks(this);
1539            mWindowResizeCallbacksAdded = false;
1540        }
1541    }
1542
1543    @Override
1544    public void onCloseSystemDialogs(String reason) {
1545        if (mFeatureId >= 0) {
1546            mWindow.closeAllPanels();
1547        }
1548    }
1549
1550    public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1551        return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1552    }
1553
1554    public InputQueue.Callback willYouTakeTheInputQueue() {
1555        return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1556    }
1557
1558    public void setSurfaceType(int type) {
1559        mWindow.setType(type);
1560    }
1561
1562    public void setSurfaceFormat(int format) {
1563        mWindow.setFormat(format);
1564    }
1565
1566    public void setSurfaceKeepScreenOn(boolean keepOn) {
1567        if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1568        else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1569    }
1570
1571    @Override
1572    public void onRootViewScrollYChanged(int rootScrollY) {
1573        mRootScrollY = rootScrollY;
1574        updateColorViewTranslations();
1575    }
1576
1577    private ActionMode createActionMode(
1578            int type, ActionMode.Callback2 callback, View originatingView) {
1579        switch (type) {
1580            case ActionMode.TYPE_PRIMARY:
1581            default:
1582                return createStandaloneActionMode(callback);
1583            case ActionMode.TYPE_FLOATING:
1584                return createFloatingActionMode(originatingView, callback);
1585        }
1586    }
1587
1588    private void setHandledActionMode(ActionMode mode) {
1589        if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1590            setHandledPrimaryActionMode(mode);
1591        } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1592            setHandledFloatingActionMode(mode);
1593        }
1594    }
1595
1596    private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1597        endOnGoingFadeAnimation();
1598        cleanupPrimaryActionMode();
1599        // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1600        // instance at all, or if there is one, but it is detached from window. The latter case
1601        // might happen when app is resized in multi-window mode and decor view is preserved
1602        // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1603        // app memory leaks because killMode() is called when the dismiss animation ends and from
1604        // cleanupPrimaryActionMode() invocation above.
1605        if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
1606            if (mWindow.isFloating()) {
1607                // Use the action bar theme.
1608                final TypedValue outValue = new TypedValue();
1609                final Resources.Theme baseTheme = mContext.getTheme();
1610                baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1611
1612                final Context actionBarContext;
1613                if (outValue.resourceId != 0) {
1614                    final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1615                    actionBarTheme.setTo(baseTheme);
1616                    actionBarTheme.applyStyle(outValue.resourceId, true);
1617
1618                    actionBarContext = new ContextThemeWrapper(mContext, 0);
1619                    actionBarContext.getTheme().setTo(actionBarTheme);
1620                } else {
1621                    actionBarContext = mContext;
1622                }
1623
1624                mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1625                mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1626                        R.attr.actionModePopupWindowStyle);
1627                mPrimaryActionModePopup.setWindowLayoutType(
1628                        WindowManager.LayoutParams.TYPE_APPLICATION);
1629                mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1630                mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1631
1632                actionBarContext.getTheme().resolveAttribute(
1633                        R.attr.actionBarSize, outValue, true);
1634                final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1635                        actionBarContext.getResources().getDisplayMetrics());
1636                mPrimaryActionModeView.setContentHeight(height);
1637                mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1638                mShowPrimaryActionModePopup = new Runnable() {
1639                    public void run() {
1640                        mPrimaryActionModePopup.showAtLocation(
1641                                mPrimaryActionModeView.getApplicationWindowToken(),
1642                                Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1643                        endOnGoingFadeAnimation();
1644
1645                        if (shouldAnimatePrimaryActionModeView()) {
1646                            mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1647                                    0f, 1f);
1648                            mFadeAnim.addListener(new AnimatorListenerAdapter() {
1649                                @Override
1650                                public void onAnimationStart(Animator animation) {
1651                                    mPrimaryActionModeView.setVisibility(VISIBLE);
1652                                }
1653
1654                                @Override
1655                                public void onAnimationEnd(Animator animation) {
1656                                    mPrimaryActionModeView.setAlpha(1f);
1657                                    mFadeAnim = null;
1658                                }
1659                            });
1660                            mFadeAnim.start();
1661                        } else {
1662                            mPrimaryActionModeView.setAlpha(1f);
1663                            mPrimaryActionModeView.setVisibility(VISIBLE);
1664                        }
1665                    }
1666                };
1667            } else {
1668                ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
1669                if (stub != null) {
1670                    mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1671                    mPrimaryActionModePopup = null;
1672                }
1673            }
1674        }
1675        if (mPrimaryActionModeView != null) {
1676            mPrimaryActionModeView.killMode();
1677            ActionMode mode = new StandaloneActionMode(
1678                    mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1679                    callback, mPrimaryActionModePopup == null);
1680            return mode;
1681        }
1682        return null;
1683    }
1684
1685    private void endOnGoingFadeAnimation() {
1686        if (mFadeAnim != null) {
1687            mFadeAnim.end();
1688        }
1689    }
1690
1691    private void setHandledPrimaryActionMode(ActionMode mode) {
1692        endOnGoingFadeAnimation();
1693        mPrimaryActionMode = mode;
1694        mPrimaryActionMode.invalidate();
1695        mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1696        if (mPrimaryActionModePopup != null) {
1697            post(mShowPrimaryActionModePopup);
1698        } else {
1699            if (shouldAnimatePrimaryActionModeView()) {
1700                mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1701                mFadeAnim.addListener(new AnimatorListenerAdapter() {
1702                    @Override
1703                    public void onAnimationStart(Animator animation) {
1704                        mPrimaryActionModeView.setVisibility(View.VISIBLE);
1705                    }
1706
1707                    @Override
1708                    public void onAnimationEnd(Animator animation) {
1709                        mPrimaryActionModeView.setAlpha(1f);
1710                        mFadeAnim = null;
1711                    }
1712                });
1713                mFadeAnim.start();
1714            } else {
1715                mPrimaryActionModeView.setAlpha(1f);
1716                mPrimaryActionModeView.setVisibility(View.VISIBLE);
1717            }
1718        }
1719        mPrimaryActionModeView.sendAccessibilityEvent(
1720                AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1721    }
1722
1723    boolean shouldAnimatePrimaryActionModeView() {
1724        // We only to animate the action mode in if the decor has already been laid out.
1725        // If it hasn't been laid out, it hasn't been drawn to screen yet.
1726        return isLaidOut();
1727    }
1728
1729    private ActionMode createFloatingActionMode(
1730            View originatingView, ActionMode.Callback2 callback) {
1731        if (mFloatingActionMode != null) {
1732            mFloatingActionMode.finish();
1733        }
1734        cleanupFloatingActionModeViews();
1735        final FloatingActionMode mode =
1736                new FloatingActionMode(mContext, callback, originatingView);
1737        mFloatingActionModeOriginatingView = originatingView;
1738        mFloatingToolbarPreDrawListener =
1739            new ViewTreeObserver.OnPreDrawListener() {
1740                @Override
1741                public boolean onPreDraw() {
1742                    mode.updateViewLocationInWindow();
1743                    return true;
1744                }
1745            };
1746        return mode;
1747    }
1748
1749    private void setHandledFloatingActionMode(ActionMode mode) {
1750        mFloatingActionMode = mode;
1751        mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
1752        ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
1753        mFloatingActionMode.invalidate();  // Will show the floating toolbar if necessary.
1754        mFloatingActionModeOriginatingView.getViewTreeObserver()
1755            .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1756    }
1757
1758    /**
1759     * Informs the decor if the caption is attached and visible.
1760     * @param attachedAndVisible true when the decor is visible.
1761     * Note that this will even be called if there is no caption.
1762     **/
1763    void enableCaption(boolean attachedAndVisible) {
1764        if (mHasCaption != attachedAndVisible) {
1765            mHasCaption = attachedAndVisible;
1766            if (getForeground() != null) {
1767                drawableChanged();
1768            }
1769        }
1770    }
1771
1772    void setWindow(PhoneWindow phoneWindow) {
1773        mWindow = phoneWindow;
1774        Context context = getContext();
1775        if (context instanceof DecorContext) {
1776            DecorContext decorContext = (DecorContext) context;
1777            decorContext.setPhoneWindow(mWindow);
1778        }
1779    }
1780
1781    @Override
1782    protected void onConfigurationChanged(Configuration newConfig) {
1783        super.onConfigurationChanged(newConfig);
1784        int workspaceId = getStackId();
1785        if (mStackId != workspaceId) {
1786            mStackId = workspaceId;
1787            if (mDecorCaptionView == null && StackId.hasWindowDecor(mStackId)) {
1788                // Configuration now requires a caption.
1789                final LayoutInflater inflater = mWindow.getLayoutInflater();
1790                mDecorCaptionView = createDecorCaptionView(inflater);
1791                if (mDecorCaptionView != null) {
1792                    if (mDecorCaptionView.getParent() == null) {
1793                        addView(mDecorCaptionView, 0,
1794                                new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1795                    }
1796                    removeView(mContentRoot);
1797                    mDecorCaptionView.addView(mContentRoot,
1798                            new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1799                }
1800            } else if (mDecorCaptionView != null) {
1801                // We might have to change the kind of surface before we do anything else.
1802                mDecorCaptionView.onConfigurationChanged(StackId.hasWindowDecor(mStackId));
1803                enableCaption(StackId.hasWindowDecor(workspaceId));
1804            }
1805        }
1806        updateAvailableWidth();
1807        initializeElevation();
1808    }
1809
1810    void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
1811        mStackId = getStackId();
1812
1813        if (mBackdropFrameRenderer != null) {
1814            loadBackgroundDrawablesIfNeeded();
1815            mBackdropFrameRenderer.onResourcesLoaded(
1816                    this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
1817                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1818                    getCurrentColor(mNavigationColorViewState));
1819        }
1820
1821        mDecorCaptionView = createDecorCaptionView(inflater);
1822        final View root = inflater.inflate(layoutResource, null);
1823        if (mDecorCaptionView != null) {
1824            if (mDecorCaptionView.getParent() == null) {
1825                addView(mDecorCaptionView,
1826                        new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1827            }
1828            mDecorCaptionView.addView(root,
1829                    new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1830        } else {
1831
1832            // Put it below the color views.
1833            addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1834        }
1835        mContentRoot = (ViewGroup) root;
1836        initializeElevation();
1837    }
1838
1839    private void loadBackgroundDrawablesIfNeeded() {
1840        if (mResizingBackgroundDrawable == null) {
1841            mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
1842                    mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource,
1843                    mWindow.isTranslucent() || mWindow.isShowingWallpaper());
1844            if (mResizingBackgroundDrawable == null) {
1845                // We shouldn't really get here as the background fallback should be always
1846                // available since it is defaulted by the system.
1847                Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
1848            }
1849        }
1850        if (mCaptionBackgroundDrawable == null) {
1851            mCaptionBackgroundDrawable = getContext().getDrawable(
1852                    R.drawable.decor_caption_title_focused);
1853        }
1854        if (mResizingBackgroundDrawable != null) {
1855            mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
1856            mResizingBackgroundDrawable.setCallback(null);
1857        }
1858    }
1859
1860    // Free floating overlapping windows require a caption.
1861    private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
1862        DecorCaptionView decorCaptionView = null;
1863        for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
1864            View view = getChildAt(i);
1865            if (view instanceof DecorCaptionView) {
1866                // The decor was most likely saved from a relaunch - so reuse it.
1867                decorCaptionView = (DecorCaptionView) view;
1868                removeViewAt(i);
1869            }
1870        }
1871        final WindowManager.LayoutParams attrs = mWindow.getAttributes();
1872        final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
1873                attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
1874        // Only a non floating application window on one of the allowed workspaces can get a caption
1875        if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) {
1876            // Dependent on the brightness of the used title we either use the
1877            // dark or the light button frame.
1878            if (decorCaptionView == null) {
1879                decorCaptionView = inflateDecorCaptionView(inflater);
1880            }
1881            decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
1882        } else {
1883            decorCaptionView = null;
1884        }
1885
1886        // Tell the decor if it has a visible caption.
1887        enableCaption(decorCaptionView != null);
1888        return decorCaptionView;
1889    }
1890
1891    private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1892        final Context context = getContext();
1893        // We make a copy of the inflater, so it has the right context associated with it.
1894        inflater = inflater.from(context);
1895        final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1896                null);
1897        setDecorCaptionShade(context, view);
1898        return view;
1899    }
1900
1901    private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1902        final int shade = mWindow.getDecorCaptionShade();
1903        switch (shade) {
1904            case DECOR_CAPTION_SHADE_LIGHT:
1905                setLightDecorCaptionShade(view);
1906                break;
1907            case DECOR_CAPTION_SHADE_DARK:
1908                setDarkDecorCaptionShade(view);
1909                break;
1910            default: {
1911                TypedValue value = new TypedValue();
1912                context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1913                // We invert the shade depending on brightness of the theme. Dark shade for light
1914                // theme and vice versa. Thanks to this the buttons should be visible on the
1915                // background.
1916                if (Color.luminance(value.data) < 0.5) {
1917                    setLightDecorCaptionShade(view);
1918                } else {
1919                    setDarkDecorCaptionShade(view);
1920                }
1921                break;
1922            }
1923        }
1924    }
1925
1926    void updateDecorCaptionShade() {
1927        if (mDecorCaptionView != null) {
1928            setDecorCaptionShade(getContext(), mDecorCaptionView);
1929        }
1930    }
1931
1932    private void setLightDecorCaptionShade(DecorCaptionView view) {
1933        view.findViewById(R.id.maximize_window).setBackgroundResource(
1934                R.drawable.decor_maximize_button_light);
1935        view.findViewById(R.id.close_window).setBackgroundResource(
1936                R.drawable.decor_close_button_light);
1937    }
1938
1939    private void setDarkDecorCaptionShade(DecorCaptionView view) {
1940        view.findViewById(R.id.maximize_window).setBackgroundResource(
1941                R.drawable.decor_maximize_button_dark);
1942        view.findViewById(R.id.close_window).setBackgroundResource(
1943                R.drawable.decor_close_button_dark);
1944    }
1945
1946    /**
1947     * Returns the color used to fill areas the app has not rendered content to yet when the
1948     * user is resizing the window of an activity in multi-window mode.
1949     */
1950    public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
1951            int backgroundFallbackRes, boolean windowTranslucent) {
1952        if (backgroundRes != 0) {
1953            final Drawable drawable = context.getDrawable(backgroundRes);
1954            if (drawable != null) {
1955                return enforceNonTranslucentBackground(drawable, windowTranslucent);
1956            }
1957        }
1958
1959        if (backgroundFallbackRes != 0) {
1960            final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
1961            if (fallbackDrawable != null) {
1962                return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
1963            }
1964        }
1965        return new ColorDrawable(Color.BLACK);
1966    }
1967
1968    /**
1969     * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
1970     * window is not translucent.
1971     */
1972    private static Drawable enforceNonTranslucentBackground(Drawable drawable,
1973            boolean windowTranslucent) {
1974        if (!windowTranslucent && drawable instanceof ColorDrawable) {
1975            ColorDrawable colorDrawable = (ColorDrawable) drawable;
1976            int color = colorDrawable.getColor();
1977            if (Color.alpha(color) != 255) {
1978                ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
1979                        .mutate();
1980                copy.setColor(
1981                        Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
1982                return copy;
1983            }
1984        }
1985        return drawable;
1986    }
1987
1988    /**
1989     * Returns the Id of the stack which contains this window.
1990     * Note that if no stack can be determined - which usually means that it was not
1991     * created for an activity - the fullscreen stack ID will be returned.
1992     * @return Returns the stack id which contains this window.
1993     **/
1994    private int getStackId() {
1995        int workspaceId = INVALID_STACK_ID;
1996        final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
1997        if (callback != null) {
1998            try {
1999                workspaceId = callback.getWindowStackId();
2000            } catch (RemoteException ex) {
2001                Log.e(mLogTag, "Failed to get the workspace ID of a PhoneWindow.");
2002            }
2003        }
2004        if (workspaceId == INVALID_STACK_ID) {
2005            return FULLSCREEN_WORKSPACE_STACK_ID;
2006        }
2007        return workspaceId;
2008    }
2009
2010    void clearContentView() {
2011        if (mDecorCaptionView != null) {
2012            mDecorCaptionView.removeContentView();
2013        } else {
2014            // This window doesn't have caption, so we need to remove everything except our views
2015            // we might have added.
2016            for (int i = getChildCount() - 1; i >= 0; i--) {
2017                View v = getChildAt(i);
2018                if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
2019                        && v != mStatusGuard && v != mNavigationGuard) {
2020                    removeViewAt(i);
2021                }
2022            }
2023        }
2024    }
2025
2026    @Override
2027    public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2028            Rect stableInsets) {
2029        if (mBackdropFrameRenderer != null) {
2030            mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
2031        }
2032    }
2033
2034    @Override
2035    public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
2036            Rect stableInsets, int resizeMode) {
2037        if (mWindow.isDestroyed()) {
2038            // If the owner's window is gone, we should not be able to come here anymore.
2039            releaseThreadedRenderer();
2040            return;
2041        }
2042        if (mBackdropFrameRenderer != null) {
2043            return;
2044        }
2045        final ThreadedRenderer renderer = getThreadedRenderer();
2046        if (renderer != null) {
2047            loadBackgroundDrawablesIfNeeded();
2048            mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
2049                    initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
2050                    mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2051                    getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
2052                    stableInsets, resizeMode);
2053
2054            // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2055            // If we want to get the shadow shown while resizing, we would need to elevate a new
2056            // element which owns the caption and has the elevation.
2057            updateElevation();
2058
2059            updateColorViews(null /* insets */, false);
2060        }
2061        mResizeMode = resizeMode;
2062        getViewRootImpl().requestInvalidateRootRenderNode();
2063    }
2064
2065    @Override
2066    public void onWindowDragResizeEnd() {
2067        releaseThreadedRenderer();
2068        updateColorViews(null /* insets */, false);
2069        mResizeMode = RESIZE_MODE_INVALID;
2070        getViewRootImpl().requestInvalidateRootRenderNode();
2071    }
2072
2073    @Override
2074    public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2075        if (mBackdropFrameRenderer == null) {
2076            return false;
2077        }
2078        return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2079    }
2080
2081    @Override
2082    public void onRequestDraw(boolean reportNextDraw) {
2083        if (mBackdropFrameRenderer != null) {
2084            mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2085        } else if (reportNextDraw) {
2086            // If render thread is gone, just report immediately.
2087            if (isAttachedToWindow()) {
2088                getViewRootImpl().reportDrawFinish();
2089            }
2090        }
2091    }
2092
2093    @Override
2094    public void onPostDraw(DisplayListCanvas canvas) {
2095        drawResizingShadowIfNeeded(canvas);
2096    }
2097
2098    private void initResizingPaints() {
2099        final int startColor = mContext.getResources().getColor(
2100                R.color.resize_shadow_start_color, null);
2101        final int endColor = mContext.getResources().getColor(
2102                R.color.resize_shadow_end_color, null);
2103        final int middleColor = (startColor + endColor) / 2;
2104        mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2105                0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2106                new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2107        mVerticalResizeShadowPaint.setShader(new LinearGradient(
2108                0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2109                new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2110    }
2111
2112    private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
2113        if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2114                || mWindow.isTranslucent()
2115                || mWindow.isShowingWallpaper()) {
2116            return;
2117        }
2118        canvas.save();
2119        canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2120        canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2121        canvas.restore();
2122        canvas.save();
2123        canvas.translate(getWidth() - mFrameOffsets.right, 0);
2124        canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2125        canvas.restore();
2126    }
2127
2128    /** Release the renderer thread which is usually done when the user stops resizing. */
2129    private void releaseThreadedRenderer() {
2130        if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2131            mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2132            mLastBackgroundDrawableCb = null;
2133        }
2134
2135        if (mBackdropFrameRenderer != null) {
2136            mBackdropFrameRenderer.releaseRenderer();
2137            mBackdropFrameRenderer = null;
2138            // Bring the shadow back.
2139            updateElevation();
2140        }
2141    }
2142
2143    private boolean isResizing() {
2144        return mBackdropFrameRenderer != null;
2145    }
2146
2147    /**
2148     * The elevation gets set for the first time and the framework needs to be informed that
2149     * the surface layer gets created with the shadow size in mind.
2150     */
2151    private void initializeElevation() {
2152        // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2153        mAllowUpdateElevation = false;
2154        updateElevation();
2155    }
2156
2157    private void updateElevation() {
2158        float elevation = 0;
2159        final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2160        // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2161        // since the shadow is bound to the content size and not the target size.
2162        if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
2163            elevation = hasWindowFocus() ?
2164                    DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
2165            // Add a maximum shadow height value to the top level view.
2166            // Note that pinned stack doesn't have focus
2167            // so maximum shadow height adjustment isn't needed.
2168            // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
2169            if (!mAllowUpdateElevation && mStackId != PINNED_STACK_ID) {
2170                elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2171            }
2172            // Convert the DP elevation into physical pixels.
2173            elevation = dipToPx(elevation);
2174            mElevationAdjustedForStack = true;
2175        } else {
2176            mElevationAdjustedForStack = false;
2177        }
2178
2179        // Don't change the elevation if we didn't previously adjust it for the stack it was in
2180        // or it didn't change.
2181        if ((wasAdjustedForStack || mElevationAdjustedForStack)
2182                && getElevation() != elevation) {
2183            mWindow.setElevation(elevation);
2184        }
2185    }
2186
2187    boolean isShowingCaption() {
2188        return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
2189    }
2190
2191    int getCaptionHeight() {
2192        return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
2193    }
2194
2195    /**
2196     * Converts a DIP measure into physical pixels.
2197     * @param dip The dip value.
2198     * @return Returns the number of pixels.
2199     */
2200    private float dipToPx(float dip) {
2201        return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2202                getResources().getDisplayMetrics());
2203    }
2204
2205    /**
2206     * Provide an override of the caption background drawable.
2207     */
2208    void setUserCaptionBackgroundDrawable(Drawable drawable) {
2209        mUserCaptionBackgroundDrawable = drawable;
2210        if (mBackdropFrameRenderer != null) {
2211            mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2212        }
2213    }
2214
2215    private static String getTitleSuffix(WindowManager.LayoutParams params) {
2216        if (params == null) {
2217            return "";
2218        }
2219        final String[] split = params.getTitle().toString().split("\\.");
2220        if (split.length > 0) {
2221            return split[split.length - 1];
2222        } else {
2223            return "";
2224        }
2225    }
2226
2227    void updateLogTag(WindowManager.LayoutParams params) {
2228        mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2229    }
2230
2231    private void updateAvailableWidth() {
2232        Resources res = getResources();
2233        mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2234                res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2235    }
2236
2237    /**
2238     * @hide
2239     */
2240    @Override
2241    public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
2242        final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2243        if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
2244            mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu, deviceId);
2245        }
2246    }
2247
2248    @Override
2249    public void dispatchPointerCaptureChanged(boolean hasCapture) {
2250        super.dispatchPointerCaptureChanged(hasCapture);
2251        if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2252            mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2253        }
2254    }
2255
2256    @Override
2257    public int getAccessibilityViewId() {
2258        return AccessibilityNodeInfo.ROOT_ITEM_ID;
2259    }
2260
2261    @Override
2262    public String toString() {
2263        return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2264                + getTitleSuffix(mWindow.getAttributes()) + "]";
2265    }
2266
2267    private static class ColorViewState {
2268        View view = null;
2269        int targetVisibility = View.INVISIBLE;
2270        boolean present = false;
2271        boolean visible;
2272        int color;
2273
2274        final int id;
2275        final int systemUiHideFlag;
2276        final int translucentFlag;
2277        final int verticalGravity;
2278        final int horizontalGravity;
2279        final int seascapeGravity;
2280        final String transitionName;
2281        final int hideWindowFlag;
2282
2283        ColorViewState(int systemUiHideFlag,
2284                int translucentFlag, int verticalGravity, int horizontalGravity,
2285                int seascapeGravity, String transitionName, int id, int hideWindowFlag) {
2286            this.id = id;
2287            this.systemUiHideFlag = systemUiHideFlag;
2288            this.translucentFlag = translucentFlag;
2289            this.verticalGravity = verticalGravity;
2290            this.horizontalGravity = horizontalGravity;
2291            this.seascapeGravity = seascapeGravity;
2292            this.transitionName = transitionName;
2293            this.hideWindowFlag = hideWindowFlag;
2294        }
2295    }
2296
2297    /**
2298     * Clears out internal references when the action mode is destroyed.
2299     */
2300    private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2301        private final ActionMode.Callback mWrapped;
2302
2303        public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2304            mWrapped = wrapped;
2305        }
2306
2307        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2308            return mWrapped.onCreateActionMode(mode, menu);
2309        }
2310
2311        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2312            requestFitSystemWindows();
2313            return mWrapped.onPrepareActionMode(mode, menu);
2314        }
2315
2316        public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2317            return mWrapped.onActionItemClicked(mode, item);
2318        }
2319
2320        public void onDestroyActionMode(ActionMode mode) {
2321            mWrapped.onDestroyActionMode(mode);
2322            final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2323                    >= M;
2324            final boolean isPrimary;
2325            final boolean isFloating;
2326            if (isMncApp) {
2327                isPrimary = mode == mPrimaryActionMode;
2328                isFloating = mode == mFloatingActionMode;
2329                if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
2330                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
2331                            + mode + " was not the current primary action mode! Expected "
2332                            + mPrimaryActionMode);
2333                }
2334                if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
2335                    Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
2336                            + mode + " was not the current floating action mode! Expected "
2337                            + mFloatingActionMode);
2338                }
2339            } else {
2340                isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2341                isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2342            }
2343            if (isPrimary) {
2344                if (mPrimaryActionModePopup != null) {
2345                    removeCallbacks(mShowPrimaryActionModePopup);
2346                }
2347                if (mPrimaryActionModeView != null) {
2348                    endOnGoingFadeAnimation();
2349                    // Store action mode view reference, so we can access it safely when animation
2350                    // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2351                    // so no need to store reference to it in separate variable.
2352                    final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
2353                    mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2354                            1f, 0f);
2355                    mFadeAnim.addListener(new Animator.AnimatorListener() {
2356
2357                                @Override
2358                                public void onAnimationStart(Animator animation) {
2359
2360                                }
2361
2362                                @Override
2363                                public void onAnimationEnd(Animator animation) {
2364                                    // If mPrimaryActionModeView has changed - it means that we've
2365                                    // cleared the content while preserving decor view. We don't
2366                                    // want to change the state of new instances accidentally here.
2367                                    if (lastActionModeView == mPrimaryActionModeView) {
2368                                        lastActionModeView.setVisibility(GONE);
2369                                        if (mPrimaryActionModePopup != null) {
2370                                            mPrimaryActionModePopup.dismiss();
2371                                        }
2372                                        lastActionModeView.killMode();
2373                                        mFadeAnim = null;
2374                                    }
2375                                }
2376
2377                                @Override
2378                                public void onAnimationCancel(Animator animation) {
2379
2380                                }
2381
2382                                @Override
2383                                public void onAnimationRepeat(Animator animation) {
2384
2385                                }
2386                            });
2387                    mFadeAnim.start();
2388                }
2389
2390                mPrimaryActionMode = null;
2391            } else if (isFloating) {
2392                cleanupFloatingActionModeViews();
2393                mFloatingActionMode = null;
2394            }
2395            if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2396                try {
2397                    mWindow.getCallback().onActionModeFinished(mode);
2398                } catch (AbstractMethodError ame) {
2399                    // Older apps might not implement this callback method.
2400                }
2401            }
2402            requestFitSystemWindows();
2403        }
2404
2405        @Override
2406        public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2407            if (mWrapped instanceof ActionMode.Callback2) {
2408                ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2409            } else {
2410                super.onGetContentRect(mode, view, outRect);
2411            }
2412        }
2413    }
2414}
2415