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