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