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