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