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