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