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