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