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