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