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