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