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