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