• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DecorView(Context context, int featureId, PhoneWindow window, WindowManager.LayoutParams params)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 
setBackgroundFallback(int resId)261     void setBackgroundFallback(int resId) {
262         mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
263         setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
264     }
265 
266     @Override
gatherTransparentRegion(Region region)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 
gatherTransparentRegion(ColorViewState colorViewState, Region region)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
onDraw(Canvas c)287     public void onDraw(Canvas c) {
288         super.onDraw(c);
289         mBackgroundFallback.draw(mContentRoot, c, mWindow.mContentParent);
290     }
291 
292     @Override
dispatchKeyEvent(KeyEvent event)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
onLayout(boolean changed, int left, int top, int right, int bottom)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
draw(Canvas canvas)752     public void draw(Canvas canvas) {
753         super.draw(canvas);
754 
755         if (mMenuBackground != null) {
756             mMenuBackground.draw(canvas);
757         }
758     }
759 
760     @Override
showContextMenuForChild(View originalView)761     public boolean showContextMenuForChild(View originalView) {
762         return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
763     }
764 
765     @Override
showContextMenuForChild(View originalView, float x, float y)766     public boolean showContextMenuForChild(View originalView, float x, float y) {
767         return showContextMenuForChildInternal(originalView, x, y);
768     }
769 
showContextMenuForChildInternal(View originalView, float x, float y)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
startActionModeForChild(View originalView, ActionMode.Callback callback)808     public ActionMode startActionModeForChild(View originalView,
809             ActionMode.Callback callback) {
810         return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
811     }
812 
813     @Override
startActionModeForChild( View child, ActionMode.Callback callback, int type)814     public ActionMode startActionModeForChild(
815             View child, ActionMode.Callback callback, int type) {
816         return startActionMode(child, callback, type);
817     }
818 
819     @Override
startActionMode(ActionMode.Callback callback)820     public ActionMode startActionMode(ActionMode.Callback callback) {
821         return startActionMode(callback, ActionMode.TYPE_PRIMARY);
822     }
823 
824     @Override
startActionMode(ActionMode.Callback callback, int type)825     public ActionMode startActionMode(ActionMode.Callback callback, int type) {
826         return startActionMode(this, callback, type);
827     }
828 
startActionMode( View originatingView, ActionMode.Callback callback, int type)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 
cleanupPrimaryActionMode()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 
cleanupFloatingActionModeViews()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 
startChanging()901     void startChanging() {
902         mChanging = true;
903     }
904 
finishChanging()905     void finishChanging() {
906         mChanging = false;
907         drawableChanged();
908     }
909 
setWindowBackground(Drawable drawable)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 
setWindowFrame(Drawable drawable)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
onWindowSystemUiVisibilityChanged(int visible)943     public void onWindowSystemUiVisibilityChanged(int visible) {
944         updateColorViews(null /* insets */, true /* animate */);
945     }
946 
947     @Override
onApplyWindowInsets(WindowInsets insets)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
isTransitionGroup()980     public boolean isTransitionGroup() {
981         return false;
982     }
983 
getColorViewTopInset(int stableTop, int systemTop)984     static int getColorViewTopInset(int stableTop, int systemTop) {
985         return Math.min(stableTop, systemTop);
986     }
987 
getColorViewBottomInset(int stableBottom, int systemBottom)988     static int getColorViewBottomInset(int stableBottom, int systemBottom) {
989         return Math.min(stableBottom, systemBottom);
990     }
991 
getColorViewRightInset(int stableRight, int systemRight)992     static int getColorViewRightInset(int stableRight, int systemRight) {
993         return Math.min(stableRight, systemRight);
994     }
995 
getColorViewLeftInset(int stableLeft, int systemLeft)996     static int getColorViewLeftInset(int stableLeft, int systemLeft) {
997         return Math.min(stableLeft, systemLeft);
998     }
999 
isNavBarToRightEdge(int bottomInset, int rightInset)1000     static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
1001         return bottomInset == 0 && rightInset > 0;
1002     }
1003 
isNavBarToLeftEdge(int bottomInset, int leftInset)1004     static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
1005         return bottomInset == 0 && leftInset > 0;
1006     }
1007 
getNavBarSize(int bottomInset, int rightInset, int leftInset)1008     static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
1009         return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1010                 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
1011     }
1012 
updateColorViews(WindowInsets insets, boolean animate)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 
calculateStatusBarColor()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 
getCurrentColor(ColorViewState state)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      */
updateColorViewInt(final ColorViewState state, int sysUiVis, int color, int size, boolean verticalBar, boolean seascape, int sideMargin, boolean animate, boolean force)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 
updateColorViewTranslations()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