blob: 7c52a40d4494d4515fec8582581b405db4095ea5 [file] [log] [blame]
Wale Ogunwale8804af22015-11-17 09:18:15 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.internal.policy;
18
Robert Carr32bcb102018-01-29 15:03:23 -080019import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
Wale Ogunwale3382ab12017-07-27 08:55:03 -070020import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -080022import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
Jorim Jaggi8f5701b2016-04-04 18:36:02 -070023import static android.os.Build.VERSION_CODES.M;
24import static android.os.Build.VERSION_CODES.N;
Wale Ogunwale8804af22015-11-17 09:18:15 -080025import static android.view.View.MeasureSpec.AT_MOST;
26import static android.view.View.MeasureSpec.EXACTLY;
27import static android.view.View.MeasureSpec.getMode;
28import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
29import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -080030import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
31import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
Wale Ogunwale8804af22015-11-17 09:18:15 -080032import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
33import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
Jorim Jaggi9f6798a2016-02-10 22:16:06 -080034import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
Jorim Jaggi65bff3e2016-02-08 19:17:07 -080035import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
Wale Ogunwale8804af22015-11-17 09:18:15 -080036import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
37import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080038import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
39import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Chong Zhangfea963e2016-08-15 17:14:16 -070040import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
Adrian Roos4c864592019-04-10 14:47:57 +020041
Clara Bayarri75e09792015-07-29 16:20:40 +010042import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
Wale Ogunwale8804af22015-11-17 09:18:15 -080043
John Reck32f140aa62018-10-04 15:08:24 -070044import android.animation.Animator;
45import android.animation.AnimatorListenerAdapter;
46import android.animation.ObjectAnimator;
47import android.annotation.Nullable;
48import android.annotation.TestApi;
Andrei Onea15884392019-03-22 17:28:11 +000049import android.annotation.UnsupportedAppUsage;
John Reck32f140aa62018-10-04 15:08:24 -070050import android.app.WindowConfiguration;
51import android.content.Context;
52import android.content.res.Configuration;
53import android.content.res.Resources;
54import android.graphics.Canvas;
55import android.graphics.Color;
Jorim Jaggia6aabac2019-03-11 14:23:16 -070056import android.graphics.Insets;
John Reck32f140aa62018-10-04 15:08:24 -070057import android.graphics.LinearGradient;
58import android.graphics.Outline;
59import android.graphics.Paint;
60import android.graphics.PixelFormat;
61import android.graphics.RecordingCanvas;
62import android.graphics.Rect;
63import android.graphics.Region;
64import android.graphics.Shader;
65import android.graphics.drawable.ColorDrawable;
66import android.graphics.drawable.Drawable;
67import android.graphics.drawable.InsetDrawable;
68import android.graphics.drawable.LayerDrawable;
69import android.util.DisplayMetrics;
70import android.util.Log;
71import android.util.Pair;
72import android.util.TypedValue;
73import android.view.ActionMode;
74import android.view.ContextThemeWrapper;
75import android.view.Gravity;
76import android.view.InputQueue;
77import android.view.KeyEvent;
78import android.view.KeyboardShortcutGroup;
79import android.view.LayoutInflater;
80import android.view.Menu;
81import android.view.MenuItem;
82import android.view.MotionEvent;
83import android.view.ThreadedRenderer;
84import android.view.View;
85import android.view.ViewGroup;
86import android.view.ViewOutlineProvider;
Jorim Jaggi6482df52019-06-26 17:35:32 +020087import android.view.ViewRootImpl;
John Reck32f140aa62018-10-04 15:08:24 -070088import android.view.ViewStub;
89import android.view.ViewTreeObserver;
90import android.view.Window;
91import android.view.WindowCallbacks;
92import android.view.WindowInsets;
93import android.view.WindowManager;
94import android.view.accessibility.AccessibilityEvent;
95import android.view.accessibility.AccessibilityManager;
96import android.view.accessibility.AccessibilityNodeInfo;
97import android.view.animation.AnimationUtils;
98import android.view.animation.Interpolator;
99import android.widget.FrameLayout;
100import android.widget.PopupWindow;
101
102import com.android.internal.R;
103import com.android.internal.policy.PhoneWindow.PanelFeatureState;
104import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
105import com.android.internal.view.FloatingActionMode;
106import com.android.internal.view.RootViewSurfaceTaker;
107import com.android.internal.view.StandaloneActionMode;
108import com.android.internal.view.menu.ContextMenuBuilder;
109import com.android.internal.view.menu.MenuHelper;
110import com.android.internal.widget.ActionBarContextView;
111import com.android.internal.widget.BackgroundFallback;
112import com.android.internal.widget.DecorCaptionView;
113import com.android.internal.widget.FloatingToolbar;
114
115import java.util.List;
116
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800117/** @hide */
118public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800119 private static final String TAG = "DecorView";
120
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800121 private static final boolean DEBUG_MEASURE = false;
122
Wale Ogunwale8804af22015-11-17 09:18:15 -0800123 private static final boolean SWEEP_OPEN_MENU = false;
124
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800125 // The height of a window which has focus in DIP.
126 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
127 // The height of a window which has not in DIP.
128 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
129
Adrian Roosc3f77562019-05-15 19:38:49 +0200130 private static final int SCRIM_LIGHT = 0xe6ffffff; // 90% white
Adrian Roos4c864592019-04-10 14:47:57 +0200131
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200132 public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
133 new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
134 Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
135 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
136 com.android.internal.R.id.statusBarBackground,
137 FLAG_FULLSCREEN);
138
139 public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
140 new ColorViewAttributes(
141 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
142 Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
143 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
144 com.android.internal.R.id.navigationBarBackground,
145 0 /* hideWindowFlag */);
146
Winson Chung4d8681f2017-05-23 16:22:08 -0700147 // This is used to workaround an issue where the PiP shadow can be transparent if the window
148 // background is transparent
149 private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
150 @Override
151 public void getOutline(View view, Outline outline) {
152 outline.setRect(0, 0, view.getWidth(), view.getHeight());
153 outline.setAlpha(1f);
154 }
155 };
156
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800157 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
158 // size calculation takes the shadow size into account. We set the elevation currently
159 // to max until the first layout command has been executed.
160 private boolean mAllowUpdateElevation = false;
161
162 private boolean mElevationAdjustedForStack = false;
163
Winson Chung4d8681f2017-05-23 16:22:08 -0700164 // Keeps track of the picture-in-picture mode for the view shadow
165 private boolean mIsInPictureInPictureMode;
166
167 // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER
168 private ViewOutlineProvider mLastOutlineProvider;
169
Wale Ogunwale8804af22015-11-17 09:18:15 -0800170 int mDefaultOpacity = PixelFormat.OPAQUE;
171
172 /** The feature ID of the panel, or -1 if this is the application's DecorView */
173 private final int mFeatureId;
174
175 private final Rect mDrawingBounds = new Rect();
176
177 private final Rect mBackgroundPadding = new Rect();
178
179 private final Rect mFramePadding = new Rect();
180
181 private final Rect mFrameOffsets = new Rect();
182
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800183 private boolean mHasCaption = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800184
185 private boolean mChanging;
186
187 private Drawable mMenuBackground;
188 private boolean mWatchingForMenu;
189 private int mDownY;
190
191 ActionMode mPrimaryActionMode;
192 private ActionMode mFloatingActionMode;
193 private ActionBarContextView mPrimaryActionModeView;
194 private PopupWindow mPrimaryActionModePopup;
195 private Runnable mShowPrimaryActionModePopup;
196 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
197 private View mFloatingActionModeOriginatingView;
198 private FloatingToolbar mFloatingToolbar;
199 private ObjectAnimator mFadeAnim;
200
201 // View added at runtime to draw under the status bar area
202 private View mStatusGuard;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800203
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200204 private final ColorViewState mStatusColorViewState =
205 new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
206 private final ColorViewState mNavigationColorViewState =
207 new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800208
209 private final Interpolator mShowInterpolator;
210 private final Interpolator mHideInterpolator;
211 private final int mBarEnterExitDuration;
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700212 final boolean mForceWindowDrawsBarBackgrounds;
213 private final int mSemiTransparentBarColor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800214
215 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
216
217 private int mLastTopInset = 0;
Andrei Onea15884392019-03-22 17:28:11 +0000218 @UnsupportedAppUsage
Wale Ogunwale8804af22015-11-17 09:18:15 -0800219 private int mLastBottomInset = 0;
Andrei Onea15884392019-03-22 17:28:11 +0000220 @UnsupportedAppUsage
Wale Ogunwale8804af22015-11-17 09:18:15 -0800221 private int mLastRightInset = 0;
Andrei Onea15884392019-03-22 17:28:11 +0000222 @UnsupportedAppUsage
Adrian Roos85d202b2016-06-02 16:27:47 -0700223 private int mLastLeftInset = 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800224 private boolean mLastHasTopStableInset = false;
225 private boolean mLastHasBottomStableInset = false;
226 private boolean mLastHasRightStableInset = false;
Adrian Roos85d202b2016-06-02 16:27:47 -0700227 private boolean mLastHasLeftStableInset = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800228 private int mLastWindowFlags = 0;
Brad Stenninge0573692019-03-11 13:52:46 -0700229 private boolean mLastShouldAlwaysConsumeSystemBars = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800230
231 private int mRootScrollY = 0;
232
Andrei Onea15884392019-03-22 17:28:11 +0000233 @UnsupportedAppUsage
Wale Ogunwale8804af22015-11-17 09:18:15 -0800234 private PhoneWindow mWindow;
235
236 ViewGroup mContentRoot;
237
238 private Rect mTempRect;
239 private Rect mOutsets = new Rect();
240
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800241 // This is the caption view for the window, containing the caption and window control
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800242 // buttons. The visibility of this decor depends on the workspace and the window type.
243 // If the window type does not require such a view, this member might be null.
Garfield Tan3b9613c2018-12-26 17:08:51 -0800244 private DecorCaptionView mDecorCaptionView;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800245
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800246 private boolean mWindowResizeCallbacksAdded = false;
Chong Zhangd3fd96c2016-02-08 18:25:24 -0800247 private Drawable.Callback mLastBackgroundDrawableCb = null;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800248 private BackdropFrameRenderer mBackdropFrameRenderer = null;
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700249 private Drawable mOriginalBackgroundDrawable;
250 private Drawable mLastOriginalBackgroundDrawable;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800251 private Drawable mResizingBackgroundDrawable;
252 private Drawable mCaptionBackgroundDrawable;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800253 private Drawable mUserCaptionBackgroundDrawable;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800254
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800255 private float mAvailableWidth;
256
257 String mLogTag = TAG;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800258 private final Rect mFloatingInsets = new Rect();
259 private boolean mApplyFloatingVerticalInsets = false;
260 private boolean mApplyFloatingHorizontalInsets = false;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800261
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700262 private int mResizeMode = RESIZE_MODE_INVALID;
263 private final int mResizeShadowSize;
264 private final Paint mVerticalResizeShadowPaint = new Paint();
265 private final Paint mHorizontalResizeShadowPaint = new Paint();
Jorim Jaggi86d30ff2019-06-11 17:54:07 +0200266 private final Paint mLegacyNavigationBarBackgroundPaint = new Paint();
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700267 private Insets mBackgroundInsets = Insets.NONE;
268 private Insets mLastBackgroundInsets = Insets.NONE;
Jorim Jaggi86d30ff2019-06-11 17:54:07 +0200269 private boolean mDrawLegacyNavigationBarBackground;
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700270
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800271 DecorView(Context context, int featureId, PhoneWindow window,
272 WindowManager.LayoutParams params) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800273 super(context);
274 mFeatureId = featureId;
275
276 mShowInterpolator = AnimationUtils.loadInterpolator(context,
277 android.R.interpolator.linear_out_slow_in);
278 mHideInterpolator = AnimationUtils.loadInterpolator(context,
279 android.R.interpolator.fast_out_linear_in);
280
281 mBarEnterExitDuration = context.getResources().getInteger(
282 R.integer.dock_enter_exit_duration);
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700283 mForceWindowDrawsBarBackgrounds = context.getResources().getBoolean(
Jorim Jaggi8f5701b2016-04-04 18:36:02 -0700284 R.bool.config_forceWindowDrawsStatusBarBackground)
285 && context.getApplicationInfo().targetSdkVersion >= N;
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700286 mSemiTransparentBarColor = context.getResources().getColor(
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800287 R.color.system_bar_background_semi_transparent, null /* theme */);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800288
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800289 updateAvailableWidth();
290
Wale Ogunwale8804af22015-11-17 09:18:15 -0800291 setWindow(window);
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800292
293 updateLogTag(params);
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700294
295 mResizeShadowSize = context.getResources().getDimensionPixelSize(
296 R.dimen.resize_shadow_size);
297 initResizingPaints();
Jorim Jaggi86d30ff2019-06-11 17:54:07 +0200298
299 mLegacyNavigationBarBackgroundPaint.setColor(Color.BLACK);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800300 }
301
Nader Jawad56c68bc2018-04-24 17:08:51 -0700302 void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
303 mBackgroundFallback.setDrawable(fallbackDrawable);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800304 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
305 }
306
Nader Jawad56c68bc2018-04-24 17:08:51 -0700307 @TestApi
308 public @Nullable Drawable getBackgroundFallback() {
309 return mBackgroundFallback.getDrawable();
310 }
311
Wale Ogunwale8804af22015-11-17 09:18:15 -0800312 @Override
Chris Craik867b8122016-05-05 16:19:22 -0700313 public boolean gatherTransparentRegion(Region region) {
314 boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
315 boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
316 boolean decorOpaque = super.gatherTransparentRegion(region);
317
318 // combine bools after computation, so each method above always executes
319 return statusOpaque || navOpaque || decorOpaque;
320 }
321
322 boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
323 if (colorViewState.view != null && colorViewState.visible && isResizing()) {
324 // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
325 // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
326 // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
327 return colorViewState.view.gatherTransparentRegion(region);
328 }
329 return false; // no opaque area added
330 }
331
332 @Override
Wale Ogunwale8804af22015-11-17 09:18:15 -0800333 public void onDraw(Canvas c) {
334 super.onDraw(c);
Jorim Jaggi853d17d2017-05-19 14:53:55 +0200335
Adrian Roos786ea782018-04-30 16:05:00 +0200336 mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
337 mStatusColorViewState.view, mNavigationColorViewState.view);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800338 }
339
340 @Override
341 public boolean dispatchKeyEvent(KeyEvent event) {
342 final int keyCode = event.getKeyCode();
343 final int action = event.getAction();
344 final boolean isDown = action == KeyEvent.ACTION_DOWN;
345
346 if (isDown && (event.getRepeatCount() == 0)) {
347 // First handle chording of panel key: if a panel key is held
348 // but not released, try to execute a shortcut in it.
349 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
350 boolean handled = dispatchKeyShortcutEvent(event);
351 if (handled) {
352 return true;
353 }
354 }
355
356 // If a panel is open, perform a shortcut on it without the
357 // chorded panel key
358 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
359 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
360 return true;
361 }
362 }
363 }
364
365 if (!mWindow.isDestroyed()) {
366 final Window.Callback cb = mWindow.getCallback();
367 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
368 : super.dispatchKeyEvent(event);
369 if (handled) {
370 return true;
371 }
372 }
373
374 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
375 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
376 }
377
378 @Override
379 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
380 // If the panel is already prepared, then perform the shortcut using it.
381 boolean handled;
382 if (mWindow.mPreparedPanel != null) {
383 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
384 Menu.FLAG_PERFORM_NO_CLOSE);
385 if (handled) {
386 if (mWindow.mPreparedPanel != null) {
387 mWindow.mPreparedPanel.isHandled = true;
388 }
389 return true;
390 }
391 }
392
393 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
394 final Window.Callback cb = mWindow.getCallback();
395 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
396 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
397 if (handled) {
398 return true;
399 }
400
401 // If the panel is not prepared, then we may be trying to handle a shortcut key
402 // combination such as Control+C. Temporarily prepare the panel then mark it
403 // unprepared again when finished to ensure that the panel will again be prepared
404 // the next time it is shown for real.
405 PhoneWindow.PanelFeatureState st =
406 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
407 if (st != null && mWindow.mPreparedPanel == null) {
408 mWindow.preparePanel(st, ev);
409 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
410 Menu.FLAG_PERFORM_NO_CLOSE);
411 st.isPrepared = false;
412 if (handled) {
413 return true;
414 }
415 }
416 return false;
417 }
418
419 @Override
420 public boolean dispatchTouchEvent(MotionEvent ev) {
421 final Window.Callback cb = mWindow.getCallback();
422 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
423 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
424 }
425
426 @Override
427 public boolean dispatchTrackballEvent(MotionEvent ev) {
428 final Window.Callback cb = mWindow.getCallback();
429 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
430 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
431 }
432
433 @Override
434 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
435 final Window.Callback cb = mWindow.getCallback();
436 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
437 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
438 }
439
440 public boolean superDispatchKeyEvent(KeyEvent event) {
441 // Give priority to closing action modes if applicable.
442 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
443 final int action = event.getAction();
444 // Back cancels action modes first.
445 if (mPrimaryActionMode != null) {
446 if (action == KeyEvent.ACTION_UP) {
447 mPrimaryActionMode.finish();
448 }
449 return true;
450 }
451 }
452
Evan Rosky5e29c072017-06-02 17:31:22 -0700453 if (super.dispatchKeyEvent(event)) {
454 return true;
455 }
456
Evan Roskycd80e612018-05-17 17:46:09 -0700457 return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800458 }
459
460 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
461 return super.dispatchKeyShortcutEvent(event);
462 }
463
464 public boolean superDispatchTouchEvent(MotionEvent event) {
465 return super.dispatchTouchEvent(event);
466 }
467
468 public boolean superDispatchTrackballEvent(MotionEvent event) {
469 return super.dispatchTrackballEvent(event);
470 }
471
472 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
473 return super.dispatchGenericMotionEvent(event);
474 }
475
476 @Override
477 public boolean onTouchEvent(MotionEvent event) {
478 return onInterceptTouchEvent(event);
479 }
480
481 private boolean isOutOfInnerBounds(int x, int y) {
482 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
483 }
484
485 private boolean isOutOfBounds(int x, int y) {
486 return x < -5 || y < -5 || x > (getWidth() + 5)
487 || y > (getHeight() + 5);
488 }
489
490 @Override
491 public boolean onInterceptTouchEvent(MotionEvent event) {
492 int action = event.getAction();
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800493 if (mHasCaption && isShowingCaption()) {
494 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
495 // was (starting) outside the window. Window resizing events should be handled by
496 // WindowManager.
Wale Ogunwale8804af22015-11-17 09:18:15 -0800497 // TODO: Investigate how to handle the outside touch in window manager
498 // without generating these events.
499 // Currently we receive these because we need to enlarge the window's
500 // touch region so that the monitor channel receives the events
501 // in the outside touch area.
502 if (action == MotionEvent.ACTION_DOWN) {
503 final int x = (int) event.getX();
504 final int y = (int) event.getY();
505 if (isOutOfInnerBounds(x, y)) {
506 return true;
507 }
508 }
509 }
510
511 if (mFeatureId >= 0) {
512 if (action == MotionEvent.ACTION_DOWN) {
513 int x = (int)event.getX();
514 int y = (int)event.getY();
515 if (isOutOfBounds(x, y)) {
516 mWindow.closePanel(mFeatureId);
517 return true;
518 }
519 }
520 }
521
522 if (!SWEEP_OPEN_MENU) {
523 return false;
524 }
525
526 if (mFeatureId >= 0) {
527 if (action == MotionEvent.ACTION_DOWN) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800528 Log.i(mLogTag, "Watchiing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800529 mWatchingForMenu = true;
530 mDownY = (int) event.getY();
531 return false;
532 }
533
534 if (!mWatchingForMenu) {
535 return false;
536 }
537
538 int y = (int)event.getY();
539 if (action == MotionEvent.ACTION_MOVE) {
540 if (y > (mDownY+30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800541 Log.i(mLogTag, "Closing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800542 mWindow.closePanel(mFeatureId);
543 mWatchingForMenu = false;
544 return true;
545 }
546 } else if (action == MotionEvent.ACTION_UP) {
547 mWatchingForMenu = false;
548 }
549
550 return false;
551 }
552
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800553 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
Wale Ogunwale8804af22015-11-17 09:18:15 -0800554 // + " (in " + getHeight() + ")");
555
556 if (action == MotionEvent.ACTION_DOWN) {
557 int y = (int)event.getY();
558 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800559 Log.i(mLogTag, "Watching!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800560 mWatchingForMenu = true;
561 }
562 return false;
563 }
564
565 if (!mWatchingForMenu) {
566 return false;
567 }
568
569 int y = (int)event.getY();
570 if (action == MotionEvent.ACTION_MOVE) {
571 if (y < (getHeight()-30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800572 Log.i(mLogTag, "Opening!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800573 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
574 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
575 mWatchingForMenu = false;
576 return true;
577 }
578 } else if (action == MotionEvent.ACTION_UP) {
579 mWatchingForMenu = false;
580 }
581
582 return false;
583 }
584
585 @Override
586 public void sendAccessibilityEvent(int eventType) {
587 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
588 return;
589 }
590
591 // if we are showing a feature that should be announced and one child
592 // make this child the event source since this is the feature itself
593 // otherwise the callback will take over and announce its client
594 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
595 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
596 mFeatureId == Window.FEATURE_PROGRESS ||
597 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
598 && getChildCount() == 1) {
599 getChildAt(0).sendAccessibilityEvent(eventType);
600 } else {
601 super.sendAccessibilityEvent(eventType);
602 }
603 }
604
605 @Override
606 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
607 final Window.Callback cb = mWindow.getCallback();
608 if (cb != null && !mWindow.isDestroyed()) {
609 if (cb.dispatchPopulateAccessibilityEvent(event)) {
610 return true;
611 }
612 }
613 return super.dispatchPopulateAccessibilityEventInternal(event);
614 }
615
616 @Override
617 protected boolean setFrame(int l, int t, int r, int b) {
618 boolean changed = super.setFrame(l, t, r, b);
619 if (changed) {
620 final Rect drawingBounds = mDrawingBounds;
621 getDrawingRect(drawingBounds);
622
623 Drawable fg = getForeground();
624 if (fg != null) {
625 final Rect frameOffsets = mFrameOffsets;
626 drawingBounds.left += frameOffsets.left;
627 drawingBounds.top += frameOffsets.top;
628 drawingBounds.right -= frameOffsets.right;
629 drawingBounds.bottom -= frameOffsets.bottom;
630 fg.setBounds(drawingBounds);
631 final Rect framePadding = mFramePadding;
632 drawingBounds.left += framePadding.left - frameOffsets.left;
633 drawingBounds.top += framePadding.top - frameOffsets.top;
634 drawingBounds.right -= framePadding.right - frameOffsets.right;
635 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
636 }
637
Jorim Jaggi24cf50f2019-04-09 15:16:23 +0200638 // Need to call super here as we pretend to be having the original background.
639 Drawable bg = super.getBackground();
Wale Ogunwale8804af22015-11-17 09:18:15 -0800640 if (bg != null) {
641 bg.setBounds(drawingBounds);
642 }
643
644 if (SWEEP_OPEN_MENU) {
645 if (mMenuBackground == null && mFeatureId < 0
646 && mWindow.getAttributes().height
647 == WindowManager.LayoutParams.MATCH_PARENT) {
648 mMenuBackground = getContext().getDrawable(
649 R.drawable.menu_background);
650 }
651 if (mMenuBackground != null) {
652 mMenuBackground.setBounds(drawingBounds.left,
653 drawingBounds.bottom-6, drawingBounds.right,
654 drawingBounds.bottom+20);
655 }
656 }
657 }
658 return changed;
659 }
660
661 @Override
662 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
663 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800664 final boolean isPortrait =
665 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800666
667 final int widthMode = getMode(widthMeasureSpec);
668 final int heightMode = getMode(heightMeasureSpec);
669
670 boolean fixedWidth = false;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800671 mApplyFloatingHorizontalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800672 if (widthMode == AT_MOST) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800673 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800674 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
675 final int w;
676 if (tvw.type == TypedValue.TYPE_DIMENSION) {
677 w = (int) tvw.getDimension(metrics);
678 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
679 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
680 } else {
681 w = 0;
682 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800683 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
Jorim Jaggiffd04902016-02-23 19:20:59 -0500684 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800685 if (w > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800686 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
687 Math.min(w, widthSize), EXACTLY);
688 fixedWidth = true;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800689 } else {
690 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
Jorim Jaggiffd04902016-02-23 19:20:59 -0500691 widthSize - mFloatingInsets.left - mFloatingInsets.right,
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800692 AT_MOST);
693 mApplyFloatingHorizontalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800694 }
695 }
696 }
697
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800698 mApplyFloatingVerticalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800699 if (heightMode == AT_MOST) {
700 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
701 : mWindow.mFixedHeightMinor;
702 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
703 final int h;
704 if (tvh.type == TypedValue.TYPE_DIMENSION) {
705 h = (int) tvh.getDimension(metrics);
706 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
707 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
708 } else {
709 h = 0;
710 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800711 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800712 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800713 if (h > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800714 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
715 Math.min(h, heightSize), EXACTLY);
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800716 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800717 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
718 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
719 mApplyFloatingVerticalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800720 }
721 }
722 }
723
724 getOutsets(mOutsets);
725 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
726 int mode = MeasureSpec.getMode(heightMeasureSpec);
727 if (mode != MeasureSpec.UNSPECIFIED) {
728 int height = MeasureSpec.getSize(heightMeasureSpec);
729 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
730 height + mOutsets.top + mOutsets.bottom, mode);
731 }
732 }
733 if (mOutsets.left > 0 || mOutsets.right > 0) {
734 int mode = MeasureSpec.getMode(widthMeasureSpec);
735 if (mode != MeasureSpec.UNSPECIFIED) {
736 int width = MeasureSpec.getSize(widthMeasureSpec);
737 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
738 width + mOutsets.left + mOutsets.right, mode);
739 }
740 }
741
742 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
743
744 int width = getMeasuredWidth();
745 boolean measure = false;
746
747 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
748
749 if (!fixedWidth && widthMode == AT_MOST) {
750 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
751 if (tv.type != TypedValue.TYPE_NULL) {
752 final int min;
753 if (tv.type == TypedValue.TYPE_DIMENSION) {
754 min = (int)tv.getDimension(metrics);
755 } else if (tv.type == TypedValue.TYPE_FRACTION) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800756 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800757 } else {
758 min = 0;
759 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800760 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
761 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800762
763 if (width < min) {
764 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
765 measure = true;
766 }
767 }
768 }
769
770 // TODO: Support height?
771
772 if (measure) {
773 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
774 }
775 }
776
777 @Override
778 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
779 super.onLayout(changed, left, top, right, bottom);
780 getOutsets(mOutsets);
781 if (mOutsets.left > 0) {
782 offsetLeftAndRight(-mOutsets.left);
783 }
784 if (mOutsets.top > 0) {
785 offsetTopAndBottom(-mOutsets.top);
786 }
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800787 if (mApplyFloatingVerticalInsets) {
788 offsetTopAndBottom(mFloatingInsets.top);
789 }
790 if (mApplyFloatingHorizontalInsets) {
791 offsetLeftAndRight(mFloatingInsets.left);
792 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800793
794 // If the application changed its SystemUI metrics, we might also have to adapt
795 // our shadow elevation.
796 updateElevation();
797 mAllowUpdateElevation = true;
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700798
799 if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
800 getViewRootImpl().requestInvalidateRootRenderNode();
801 }
Wale Ogunwale8804af22015-11-17 09:18:15 -0800802 }
803
804 @Override
805 public void draw(Canvas canvas) {
806 super.draw(canvas);
807
808 if (mMenuBackground != null) {
809 mMenuBackground.draw(canvas);
810 }
811 }
812
813 @Override
814 public boolean showContextMenuForChild(View originalView) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700815 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800816 }
817
818 @Override
819 public boolean showContextMenuForChild(View originalView, float x, float y) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700820 return showContextMenuForChildInternal(originalView, x, y);
Alan Viverette021627e2015-11-25 14:22:00 -0500821 }
822
823 private boolean showContextMenuForChildInternal(View originalView,
Oren Blasberg23e282d2016-04-20 13:43:45 -0700824 float x, float y) {
Alan Viverette021627e2015-11-25 14:22:00 -0500825 // Only allow one context menu at a time.
826 if (mWindow.mContextMenuHelper != null) {
827 mWindow.mContextMenuHelper.dismiss();
828 mWindow.mContextMenuHelper = null;
829 }
830
831 // Reuse the context menu builder.
Alan Viverette77fb85e2015-12-14 11:42:44 -0500832 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800833 if (mWindow.mContextMenu == null) {
834 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
Alan Viverette77fb85e2015-12-14 11:42:44 -0500835 mWindow.mContextMenu.setCallback(callback);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800836 } else {
837 mWindow.mContextMenu.clearAll();
838 }
839
Alan Viverette021627e2015-11-25 14:22:00 -0500840 final MenuHelper helper;
Oren Blasberg23e282d2016-04-20 13:43:45 -0700841 final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
Alan Viverette021627e2015-11-25 14:22:00 -0500842 if (isPopup) {
843 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
844 } else {
845 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
Wale Ogunwale8804af22015-11-17 09:18:15 -0800846 }
Alan Viverette021627e2015-11-25 14:22:00 -0500847
Alan Viverette9084d222015-12-16 09:56:37 -0500848 if (helper != null) {
849 // If it's a dialog, the callback needs to handle showing
850 // sub-menus. Either way, the callback is required for propagating
851 // selection to Context.onContextMenuItemSelected().
852 callback.setShowDialogForSubmenu(!isPopup);
853 helper.setPresenterCallback(callback);
854 }
Alan Viverette021627e2015-11-25 14:22:00 -0500855
856 mWindow.mContextMenuHelper = helper;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800857 return helper != null;
858 }
859
860 @Override
861 public ActionMode startActionModeForChild(View originalView,
862 ActionMode.Callback callback) {
863 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
864 }
865
866 @Override
867 public ActionMode startActionModeForChild(
868 View child, ActionMode.Callback callback, int type) {
869 return startActionMode(child, callback, type);
870 }
871
872 @Override
873 public ActionMode startActionMode(ActionMode.Callback callback) {
874 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
875 }
876
877 @Override
878 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
879 return startActionMode(this, callback, type);
880 }
881
882 private ActionMode startActionMode(
883 View originatingView, ActionMode.Callback callback, int type) {
884 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
885 ActionMode mode = null;
886 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
887 try {
888 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
889 } catch (AbstractMethodError ame) {
890 // Older apps might not implement the typed version of this method.
891 if (type == ActionMode.TYPE_PRIMARY) {
892 try {
893 mode = mWindow.getCallback().onWindowStartingActionMode(
894 wrappedCallback);
895 } catch (AbstractMethodError ame2) {
896 // Older apps might not implement this callback method at all.
897 }
898 }
899 }
900 }
901 if (mode != null) {
902 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
903 cleanupPrimaryActionMode();
904 mPrimaryActionMode = mode;
905 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
906 if (mFloatingActionMode != null) {
907 mFloatingActionMode.finish();
908 }
909 mFloatingActionMode = mode;
910 }
911 } else {
912 mode = createActionMode(type, wrappedCallback, originatingView);
913 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
914 setHandledActionMode(mode);
915 } else {
916 mode = null;
917 }
918 }
919 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
920 try {
921 mWindow.getCallback().onActionModeStarted(mode);
922 } catch (AbstractMethodError ame) {
923 // Older apps might not implement this callback method.
924 }
925 }
926 return mode;
927 }
928
929 private void cleanupPrimaryActionMode() {
930 if (mPrimaryActionMode != null) {
931 mPrimaryActionMode.finish();
932 mPrimaryActionMode = null;
933 }
934 if (mPrimaryActionModeView != null) {
935 mPrimaryActionModeView.killMode();
936 }
937 }
938
939 private void cleanupFloatingActionModeViews() {
940 if (mFloatingToolbar != null) {
941 mFloatingToolbar.dismiss();
942 mFloatingToolbar = null;
943 }
944 if (mFloatingActionModeOriginatingView != null) {
945 if (mFloatingToolbarPreDrawListener != null) {
946 mFloatingActionModeOriginatingView.getViewTreeObserver()
947 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
948 mFloatingToolbarPreDrawListener = null;
949 }
950 mFloatingActionModeOriginatingView = null;
951 }
952 }
953
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800954 void startChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800955 mChanging = true;
956 }
957
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800958 void finishChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800959 mChanging = false;
960 drawableChanged();
961 }
962
963 public void setWindowBackground(Drawable drawable) {
Jorim Jaggia6aabac2019-03-11 14:23:16 -0700964 if (mOriginalBackgroundDrawable != drawable) {
965 mOriginalBackgroundDrawable = drawable;
966 updateBackgroundDrawable();
Wale Ogunwale8804af22015-11-17 09:18:15 -0800967 if (drawable != null) {
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700968 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
969 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700970 } else {
971 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
Nader Jawad56c68bc2018-04-24 17:08:51 -0700972 mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable,
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700973 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700974 }
975 if (mResizingBackgroundDrawable != null) {
976 mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800977 } else {
978 mBackgroundPadding.setEmpty();
979 }
980 drawableChanged();
981 }
982 }
983
Jorim Jaggi24cf50f2019-04-09 15:16:23 +0200984 @Override
985 public void setBackgroundDrawable(Drawable background) {
Jorim Jaggi24cf50f2019-04-09 15:16:23 +0200986 // TODO: This should route through setWindowBackground, but late in the release to make this
987 // change.
988 if (mOriginalBackgroundDrawable != background) {
989 mOriginalBackgroundDrawable = background;
990 updateBackgroundDrawable();
Vadim Caen64aa6e12019-07-24 16:24:33 +0200991 if (!View.sBrokenWindowBackground) {
992 drawableChanged();
993 }
Jorim Jaggi24cf50f2019-04-09 15:16:23 +0200994 }
995 }
996
Wale Ogunwale8804af22015-11-17 09:18:15 -0800997 public void setWindowFrame(Drawable drawable) {
998 if (getForeground() != drawable) {
999 setForeground(drawable);
1000 if (drawable != null) {
1001 drawable.getPadding(mFramePadding);
1002 } else {
1003 mFramePadding.setEmpty();
1004 }
1005 drawableChanged();
1006 }
1007 }
1008
1009 @Override
1010 public void onWindowSystemUiVisibilityChanged(int visible) {
1011 updateColorViews(null /* insets */, true /* animate */);
Garfield Tan3b9613c2018-12-26 17:08:51 -08001012 updateDecorCaptionStatus(getResources().getConfiguration());
Adrian Roos27db0ca2019-05-29 16:12:09 +02001013
1014 if (mStatusGuard != null && mStatusGuard.getVisibility() == VISIBLE) {
1015 updateStatusGuardColor();
1016 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001017 }
1018
1019 @Override
1020 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001021 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
1022 mFloatingInsets.setEmpty();
Jorim Jaggi65bff3e2016-02-08 19:17:07 -08001023 if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001024 // For dialog windows we want to make sure they don't go over the status bar or nav bar.
1025 // We consume the system insets and we will reuse them later during the measure phase.
Jorim Jaggi65bff3e2016-02-08 19:17:07 -08001026 // We allow the app to ignore this and handle insets itself by using
1027 // FLAG_LAYOUT_IN_SCREEN.
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001028 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
1029 mFloatingInsets.top = insets.getSystemWindowInsetTop();
1030 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +01001031 insets = insets.inset(0, insets.getSystemWindowInsetTop(),
1032 0, insets.getSystemWindowInsetBottom());
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001033 }
1034 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
1035 mFloatingInsets.left = insets.getSystemWindowInsetTop();
1036 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +01001037 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
1038 insets.getSystemWindowInsetRight(), 0);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001039 }
1040 }
Adrian Roos60f59292018-08-24 16:29:06 +02001041 mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001042 insets = updateColorViews(insets, true /* animate */);
1043 insets = updateStatusGuard(insets);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001044 if (getForeground() != null) {
1045 drawableChanged();
1046 }
1047 return insets;
1048 }
1049
1050 @Override
1051 public boolean isTransitionGroup() {
1052 return false;
1053 }
1054
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001055 public static int getColorViewTopInset(int stableTop, int systemTop) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001056 return Math.min(stableTop, systemTop);
1057 }
1058
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001059 public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001060 return Math.min(stableBottom, systemBottom);
1061 }
1062
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001063 public static int getColorViewRightInset(int stableRight, int systemRight) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001064 return Math.min(stableRight, systemRight);
1065 }
1066
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001067 public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001068 return Math.min(stableLeft, systemLeft);
1069 }
1070
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001071 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001072 return bottomInset == 0 && rightInset > 0;
1073 }
1074
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001075 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001076 return bottomInset == 0 && leftInset > 0;
1077 }
1078
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001079 public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001080 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1081 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001082 }
1083
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001084 public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
Winson Chungffde2ea2019-06-17 17:19:13 -07001085 Rect contentInsets, Rect outRect, float scale) {
1086 final int bottomInset =
1087 (int) (getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom) * scale);
1088 final int leftInset =
1089 (int) (getColorViewLeftInset(stableInsets.left, contentInsets.left) * scale);
1090 final int rightInset =
1091 (int) (getColorViewLeftInset(stableInsets.right, contentInsets.right) * scale);
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001092 final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1093 if (isNavBarToRightEdge(bottomInset, rightInset)) {
1094 outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1095 } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1096 outRect.set(0, 0, size, canvasHeight);
1097 } else {
1098 outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1099 }
1100 }
1101
Wale Ogunwale8804af22015-11-17 09:18:15 -08001102 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1103 WindowManager.LayoutParams attrs = mWindow.getAttributes();
1104 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1105
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001106 // IME is an exceptional floating window that requires color view.
1107 final boolean isImeWindow =
1108 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1109 if (!mWindow.mIsFloating || isImeWindow) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001110 boolean disallowAnimate = !isLaidOut();
1111 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1112 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1113 mLastWindowFlags = attrs.flags;
1114
1115 if (insets != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001116 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001117 insets.getSystemWindowInsetTop());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001118 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001119 insets.getSystemWindowInsetBottom());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001120 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001121 insets.getSystemWindowInsetRight());
Adrian Roos85d202b2016-06-02 16:27:47 -07001122 mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
1123 insets.getSystemWindowInsetLeft());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001124
1125 // Don't animate if the presence of stable insets has changed, because that
1126 // indicates that the window was either just added and received them for the
1127 // first time, or the window size or position has changed.
1128 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
1129 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1130 mLastHasTopStableInset = hasTopStableInset;
1131
1132 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
1133 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1134 mLastHasBottomStableInset = hasBottomStableInset;
1135
1136 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
1137 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1138 mLastHasRightStableInset = hasRightStableInset;
Adrian Roos85d202b2016-06-02 16:27:47 -07001139
1140 boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
1141 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1142 mLastHasLeftStableInset = hasLeftStableInset;
1143
Brad Stenninge0573692019-03-11 13:52:46 -07001144 mLastShouldAlwaysConsumeSystemBars = insets.shouldAlwaysConsumeSystemBars();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001145 }
1146
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001147 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
Adrian Roos85d202b2016-06-02 16:27:47 -07001148 boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1149 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001150 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001151 calculateNavigationBarColor(), mWindow.mNavigationBarDividerColor, navBarSize,
Jason Monkea506c62017-09-01 12:40:06 -04001152 navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001153 0 /* sideInset */, animate && !disallowAnimate,
1154 mForceWindowDrawsBarBackgrounds);
Jorim Jaggi6482df52019-06-26 17:35:32 +02001155 boolean oldDrawLegacy = mDrawLegacyNavigationBarBackground;
Jorim Jaggi86d30ff2019-06-11 17:54:07 +02001156 mDrawLegacyNavigationBarBackground = mNavigationColorViewState.visible
1157 && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0;
Jorim Jaggi6482df52019-06-26 17:35:32 +02001158 if (oldDrawLegacy != mDrawLegacyNavigationBarBackground) {
1159 ViewRootImpl vri = getViewRootImpl();
1160 if (vri != null) {
1161 vri.requestInvalidateRootRenderNode();
1162 }
1163 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001164
1165 boolean statusBarNeedsRightInset = navBarToRightEdge
1166 && mNavigationColorViewState.present;
Adrian Roos85d202b2016-06-02 16:27:47 -07001167 boolean statusBarNeedsLeftInset = navBarToLeftEdge
1168 && mNavigationColorViewState.present;
1169 int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1170 : statusBarNeedsLeftInset ? mLastLeftInset : 0;
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001171 updateColorViewInt(mStatusColorViewState, sysUiVisibility,
Jason Monkea506c62017-09-01 12:40:06 -04001172 calculateStatusBarColor(), 0, mLastTopInset,
Adrian Roos85d202b2016-06-02 16:27:47 -07001173 false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1174 animate && !disallowAnimate,
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001175 mForceWindowDrawsBarBackgrounds);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001176 }
1177
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001178 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS or
1179 // mForceWindowDrawsBarBackgrounds, we still need to ensure that the rest of the view
1180 // hierarchy doesn't notice it, unless they've explicitly asked for it.
1181 //
1182 // Note: We don't need to check for IN_SCREEN or INSET_DECOR because unlike the status bar,
1183 // these flags wouldn't make the window draw behind the navigation bar, unless
1184 // LAYOUT_HIDE_NAVIGATION was set.
Adrian Roosfaba4062019-05-14 15:27:17 +02001185 boolean hideNavigation = (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) != 0;
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001186 boolean forceConsumingNavBar = (mForceWindowDrawsBarBackgrounds
1187 && (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
Wale Ogunwale8804af22015-11-17 09:18:15 -08001188 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
Adrian Roosfaba4062019-05-14 15:27:17 +02001189 && !hideNavigation)
1190 || (mLastShouldAlwaysConsumeSystemBars && hideNavigation);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001191
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001192 boolean consumingNavBar =
1193 ((attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1194 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
Adrian Roosfaba4062019-05-14 15:27:17 +02001195 && !hideNavigation)
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001196 || forceConsumingNavBar;
1197
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001198 // If we didn't request fullscreen layout, but we still got it because of the
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001199 // mForceWindowDrawsBarBackgrounds flag, also consume top inset.
Jorim Jaggi01d60d02019-05-07 17:10:42 +02001200 // If we should always consume system bars, only consume that if the app wanted to go to
1201 // fullscreen, as othrewise we can expect the app to handle it.
1202 boolean fullscreen = (sysUiVisibility & SYSTEM_UI_FLAG_FULLSCREEN) != 0
1203 || (attrs.flags & FLAG_FULLSCREEN) != 0;
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001204 boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001205 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1206 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001207 && mForceWindowDrawsBarBackgrounds
Brad Stenninge0573692019-03-11 13:52:46 -07001208 && mLastTopInset != 0
Jorim Jaggi01d60d02019-05-07 17:10:42 +02001209 || (mLastShouldAlwaysConsumeSystemBars && fullscreen);
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001210
1211 int consumedTop = consumingStatusBar ? mLastTopInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001212 int consumedRight = consumingNavBar ? mLastRightInset : 0;
1213 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
Adrian Roos85d202b2016-06-02 16:27:47 -07001214 int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001215
1216 if (mContentRoot != null
1217 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1218 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001219 if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
Adrian Roos85d202b2016-06-02 16:27:47 -07001220 || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001221 lp.topMargin = consumedTop;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001222 lp.rightMargin = consumedRight;
1223 lp.bottomMargin = consumedBottom;
Adrian Roos85d202b2016-06-02 16:27:47 -07001224 lp.leftMargin = consumedLeft;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001225 mContentRoot.setLayoutParams(lp);
1226
1227 if (insets == null) {
1228 // The insets have changed, but we're not currently in the process
1229 // of dispatching them.
1230 requestApplyInsets();
1231 }
1232 }
1233 if (insets != null) {
Adrian Roosf7b74262017-11-22 14:21:01 +01001234 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001235 }
1236 }
1237
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001238 if (forceConsumingNavBar) {
1239 mBackgroundInsets = Insets.of(mLastLeftInset, 0, mLastRightInset, mLastBottomInset);
1240 } else {
1241 mBackgroundInsets = Insets.NONE;
1242 }
1243 updateBackgroundDrawable();
1244
Wale Ogunwale8804af22015-11-17 09:18:15 -08001245 if (insets != null) {
1246 insets = insets.consumeStableInsets();
1247 }
1248 return insets;
1249 }
1250
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001251 /**
1252 * Updates the background drawable, applying padding to it in case we {@link #mBackgroundInsets}
1253 * are set.
1254 */
1255 private void updateBackgroundDrawable() {
Jorim Jaggid798fd32019-05-08 23:08:02 +02001256 // Background insets can be null if super constructor calls setBackgroundDrawable.
1257 if (mBackgroundInsets == null) {
1258 mBackgroundInsets = Insets.NONE;
1259 }
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001260 if (mBackgroundInsets.equals(mLastBackgroundInsets)
1261 && mLastOriginalBackgroundDrawable == mOriginalBackgroundDrawable) {
1262 return;
1263 }
Jorim Jaggi1ed68892019-04-08 14:51:42 +02001264 if (mOriginalBackgroundDrawable == null || mBackgroundInsets.equals(Insets.NONE)) {
Jorim Jaggi24cf50f2019-04-09 15:16:23 +02001265
1266 // Call super since we are intercepting setBackground on this class.
1267 super.setBackgroundDrawable(mOriginalBackgroundDrawable);
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001268 } else {
Jorim Jaggi24cf50f2019-04-09 15:16:23 +02001269
1270 // Call super since we are intercepting setBackground on this class.
1271 super.setBackgroundDrawable(new InsetDrawable(mOriginalBackgroundDrawable,
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001272 mBackgroundInsets.left, mBackgroundInsets.top,
1273 mBackgroundInsets.right, mBackgroundInsets.bottom) {
1274
1275 /**
1276 * Return inner padding so we don't apply the padding again in
1277 * {@link DecorView#drawableChanged()}
1278 */
1279 @Override
1280 public boolean getPadding(Rect padding) {
1281 return getDrawable().getPadding(padding);
1282 }
1283 });
1284 }
1285 mLastBackgroundInsets = mBackgroundInsets;
1286 mLastOriginalBackgroundDrawable = mOriginalBackgroundDrawable;
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001287 }
1288
Jorim Jaggi24cf50f2019-04-09 15:16:23 +02001289 @Override
1290 public Drawable getBackground() {
1291 return mOriginalBackgroundDrawable;
1292 }
1293
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001294 private int calculateStatusBarColor() {
1295 return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_STATUS,
Adrian Roos4c864592019-04-10 14:47:57 +02001296 mSemiTransparentBarColor, mWindow.mStatusBarColor,
1297 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_STATUS_BAR,
1298 mWindow.mEnsureStatusBarContrastWhenTransparent);
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001299 }
1300
1301 private int calculateNavigationBarColor() {
1302 return calculateBarColor(mWindow.getAttributes().flags, FLAG_TRANSLUCENT_NAVIGATION,
Adrian Roos4c864592019-04-10 14:47:57 +02001303 mSemiTransparentBarColor, mWindow.mNavigationBarColor,
1304 getWindowSystemUiVisibility(), SYSTEM_UI_FLAG_LIGHT_NAVIGATION_BAR,
1305 mWindow.mEnsureNavigationBarContrastWhenTransparent
1306 && getContext().getResources().getBoolean(R.bool.config_navBarNeedsScrim));
Jorim Jaggia6aabac2019-03-11 14:23:16 -07001307 }
1308
1309 public static int calculateBarColor(int flags, int translucentFlag, int semiTransparentBarColor,
Adrian Roos4c864592019-04-10 14:47:57 +02001310 int barColor, int sysuiVis, int lightSysuiFlag, boolean scrimTransparent) {
1311 if ((flags & translucentFlag) != 0) {
1312 return semiTransparentBarColor;
1313 } else if ((flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0) {
1314 return Color.BLACK;
Winson Chungffc70a62019-07-01 15:32:43 -07001315 } else if (scrimTransparent && Color.alpha(barColor) == 0) {
Adrian Roos4c864592019-04-10 14:47:57 +02001316 boolean light = (sysuiVis & lightSysuiFlag) != 0;
1317 return light ? SCRIM_LIGHT : semiTransparentBarColor;
1318 } else {
1319 return barColor;
1320 }
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001321 }
1322
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001323 private int getCurrentColor(ColorViewState state) {
1324 if (state.visible) {
1325 return state.color;
1326 } else {
1327 return 0;
1328 }
1329 }
1330
Wale Ogunwale8804af22015-11-17 09:18:15 -08001331 /**
1332 * Update a color view
1333 *
1334 * @param state the color view to update.
1335 * @param sysUiVis the current systemUiVisibility to apply.
1336 * @param color the current color to apply.
Jason Monkea506c62017-09-01 12:40:06 -04001337 * @param dividerColor the current divider color to apply.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001338 * @param size the current size in the non-parent-matching dimension.
1339 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1340 * horizontal edge,
Adrian Roos85d202b2016-06-02 16:27:47 -07001341 * @param sideMargin sideMargin for the color view.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001342 * @param animate if true, the change will be animated.
1343 */
1344 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
Jason Monkea506c62017-09-01 12:40:06 -04001345 int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
Adrian Roos85d202b2016-06-02 16:27:47 -07001346 boolean animate, boolean force) {
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001347 state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
1348 boolean show = state.attributes.isVisible(state.present, color,
1349 mWindow.getAttributes().flags, force);
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001350 boolean showView = show && !isResizing() && size > 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001351
1352 boolean visibilityChanged = false;
1353 View view = state.view;
1354
1355 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1356 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
Adrian Roos85d202b2016-06-02 16:27:47 -07001357 int resolvedGravity = verticalBar
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001358 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1359 : state.attributes.verticalGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001360
1361 if (view == null) {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001362 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001363 state.view = view = new View(mContext);
Jason Monkea506c62017-09-01 12:40:06 -04001364 setColor(view, color, dividerColor, verticalBar, seascape);
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001365 view.setTransitionName(state.attributes.transitionName);
1366 view.setId(state.attributes.id);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001367 visibilityChanged = true;
1368 view.setVisibility(INVISIBLE);
1369 state.targetVisibility = VISIBLE;
1370
1371 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1372 resolvedGravity);
Adrian Roos85d202b2016-06-02 16:27:47 -07001373 if (seascape) {
1374 lp.leftMargin = sideMargin;
1375 } else {
1376 lp.rightMargin = sideMargin;
1377 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001378 addView(view, lp);
1379 updateColorViewTranslations();
1380 }
1381 } else {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001382 int vis = showView ? VISIBLE : INVISIBLE;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001383 visibilityChanged = state.targetVisibility != vis;
1384 state.targetVisibility = vis;
1385 LayoutParams lp = (LayoutParams) view.getLayoutParams();
Adrian Roos85d202b2016-06-02 16:27:47 -07001386 int rightMargin = seascape ? 0 : sideMargin;
1387 int leftMargin = seascape ? sideMargin : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001388 if (lp.height != resolvedHeight || lp.width != resolvedWidth
Adrian Roos85d202b2016-06-02 16:27:47 -07001389 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1390 || lp.leftMargin != leftMargin) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001391 lp.height = resolvedHeight;
1392 lp.width = resolvedWidth;
1393 lp.gravity = resolvedGravity;
1394 lp.rightMargin = rightMargin;
Adrian Roos85d202b2016-06-02 16:27:47 -07001395 lp.leftMargin = leftMargin;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001396 view.setLayoutParams(lp);
1397 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001398 if (showView) {
Jason Monkea506c62017-09-01 12:40:06 -04001399 setColor(view, color, dividerColor, verticalBar, seascape);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001400 }
1401 }
1402 if (visibilityChanged) {
1403 view.animate().cancel();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001404 if (animate && !isResizing()) {
1405 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001406 if (view.getVisibility() != VISIBLE) {
1407 view.setVisibility(VISIBLE);
1408 view.setAlpha(0.0f);
1409 }
1410 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1411 setDuration(mBarEnterExitDuration);
1412 } else {
1413 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1414 .setDuration(mBarEnterExitDuration)
1415 .withEndAction(new Runnable() {
1416 @Override
1417 public void run() {
1418 state.view.setAlpha(1.0f);
1419 state.view.setVisibility(INVISIBLE);
1420 }
1421 });
1422 }
1423 } else {
1424 view.setAlpha(1.0f);
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001425 view.setVisibility(showView ? VISIBLE : INVISIBLE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001426 }
1427 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001428 state.visible = show;
1429 state.color = color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001430 }
1431
Jason Monkea506c62017-09-01 12:40:06 -04001432 private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1433 boolean seascape) {
1434 if (dividerColor != 0) {
1435 final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1436 if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1437 final int size = Math.round(
1438 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1439 v.getContext().getResources().getDisplayMetrics()));
1440 // Use an inset to make the divider line on the side that faces the app.
1441 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1442 verticalBar && !seascape ? size : 0,
1443 !verticalBar ? size : 0,
1444 verticalBar && seascape ? size : 0, 0);
1445 v.setBackground(new LayerDrawable(new Drawable[] {
1446 new ColorDrawable(dividerColor), d }));
1447 v.setTag(new Pair<>(verticalBar, seascape));
1448 } else {
1449 final LayerDrawable d = (LayerDrawable) v.getBackground();
1450 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1451 ((ColorDrawable) inset.getDrawable()).setColor(color);
1452 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1453 }
1454 } else {
1455 v.setTag(null);
1456 v.setBackgroundColor(color);
1457 }
1458 }
1459
Wale Ogunwale8804af22015-11-17 09:18:15 -08001460 private void updateColorViewTranslations() {
1461 // Put the color views back in place when they get moved off the screen
1462 // due to the the ViewRootImpl panning.
1463 int rootScrollY = mRootScrollY;
1464 if (mStatusColorViewState.view != null) {
1465 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1466 }
1467 if (mNavigationColorViewState.view != null) {
1468 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1469 }
1470 }
1471
1472 private WindowInsets updateStatusGuard(WindowInsets insets) {
1473 boolean showStatusGuard = false;
1474 // Show the status guard when the non-overlay contextual action bar is showing
1475 if (mPrimaryActionModeView != null) {
1476 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1477 // Insets are magic!
1478 final MarginLayoutParams mlp = (MarginLayoutParams)
1479 mPrimaryActionModeView.getLayoutParams();
1480 boolean mlpChanged = false;
1481 if (mPrimaryActionModeView.isShown()) {
1482 if (mTempRect == null) {
1483 mTempRect = new Rect();
1484 }
1485 final Rect rect = mTempRect;
1486
Adrian Roos27db0ca2019-05-29 16:12:09 +02001487 // Apply the insets that have not been applied by the contentParent yet.
1488 WindowInsets innerInsets =
1489 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1490 int newTopMargin = innerInsets.getSystemWindowInsetTop();
1491 int newLeftMargin = innerInsets.getSystemWindowInsetLeft();
1492 int newRightMargin = innerInsets.getSystemWindowInsetRight();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001493
Adrian Roos27db0ca2019-05-29 16:12:09 +02001494 // Must use root window insets for the guard, because the color views consume
1495 // the navigation bar inset if the window does not request LAYOUT_HIDE_NAV - but
1496 // the status guard is attached at the root.
1497 WindowInsets rootInsets = getRootWindowInsets();
1498 int newGuardLeftMargin = rootInsets.getSystemWindowInsetLeft();
1499 int newGuardRightMargin = rootInsets.getSystemWindowInsetRight();
1500
1501 if (mlp.topMargin != newTopMargin || mlp.leftMargin != newLeftMargin
1502 || mlp.rightMargin != newRightMargin) {
1503 mlpChanged = true;
1504 mlp.topMargin = newTopMargin;
1505 mlp.leftMargin = newLeftMargin;
1506 mlp.rightMargin = newRightMargin;
1507 }
1508
1509 if (newTopMargin > 0 && mStatusGuard == null) {
1510 mStatusGuard = new View(mContext);
1511 mStatusGuard.setVisibility(GONE);
1512 final LayoutParams lp = new LayoutParams(MATCH_PARENT,
1513 mlp.topMargin, Gravity.LEFT | Gravity.TOP);
1514 lp.leftMargin = newGuardLeftMargin;
1515 lp.rightMargin = newGuardRightMargin;
1516 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view), lp);
1517 } else if (mStatusGuard != null) {
1518 final LayoutParams lp = (LayoutParams)
1519 mStatusGuard.getLayoutParams();
1520 if (lp.height != mlp.topMargin || lp.leftMargin != newGuardLeftMargin
1521 || lp.rightMargin != newGuardRightMargin) {
1522 lp.height = mlp.topMargin;
1523 lp.leftMargin = newGuardLeftMargin;
1524 lp.rightMargin = newGuardRightMargin;
1525 mStatusGuard.setLayoutParams(lp);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001526 }
1527 }
1528
1529 // The action mode's theme may differ from the app, so
1530 // always show the status guard above it if we have one.
1531 showStatusGuard = mStatusGuard != null;
1532
Adrian Roos27db0ca2019-05-29 16:12:09 +02001533 if (showStatusGuard && mStatusGuard.getVisibility() != VISIBLE) {
1534 // If it wasn't previously shown, the color may be stale
1535 updateStatusGuardColor();
1536 }
1537
Wale Ogunwale8804af22015-11-17 09:18:15 -08001538 // We only need to consume the insets if the action
1539 // mode is overlaid on the app content (e.g. it's
1540 // sitting in a FrameLayout, see
1541 // screen_simple_overlay_action_mode.xml).
1542 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1543 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
Adrian Roosf7b74262017-11-22 14:21:01 +01001544 if (nonOverlay && showStatusGuard) {
1545 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1546 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001547 } else {
1548 // reset top margin
Adrian Roos27db0ca2019-05-29 16:12:09 +02001549 if (mlp.topMargin != 0 || mlp.leftMargin != 0 || mlp.rightMargin != 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001550 mlpChanged = true;
1551 mlp.topMargin = 0;
1552 }
1553 }
1554 if (mlpChanged) {
1555 mPrimaryActionModeView.setLayoutParams(mlp);
1556 }
1557 }
1558 }
1559 if (mStatusGuard != null) {
Adrian Roos27db0ca2019-05-29 16:12:09 +02001560 mStatusGuard.setVisibility(showStatusGuard ? VISIBLE : GONE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001561 }
1562 return insets;
1563 }
1564
Adrian Roos27db0ca2019-05-29 16:12:09 +02001565 private void updateStatusGuardColor() {
1566 boolean lightStatusBar =
1567 (getWindowSystemUiVisibility() & SYSTEM_UI_FLAG_LIGHT_STATUS_BAR) != 0;
1568 mStatusGuard.setBackgroundColor(lightStatusBar
1569 ? mContext.getColor(R.color.decor_view_status_guard_light)
1570 : mContext.getColor(R.color.decor_view_status_guard));
1571 }
1572
Winson Chung4d8681f2017-05-23 16:22:08 -07001573 /**
1574 * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1575 * an opaque shadow even if the window background is completely transparent. This only applies
1576 * to activities that are currently the task root.
1577 */
1578 public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1579 if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1580 return;
1581 }
1582
1583 if (isInPictureInPictureMode) {
1584 final Window.WindowControllerCallback callback =
1585 mWindow.getWindowControllerCallback();
1586 if (callback != null && callback.isTaskRoot()) {
1587 // Call super implementation directly as we don't want to save the PIP outline
1588 // provider to be restored
1589 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1590 }
1591 } else {
1592 // Restore the previous outline provider
1593 if (getOutlineProvider() != mLastOutlineProvider) {
1594 setOutlineProvider(mLastOutlineProvider);
1595 }
1596 }
1597 mIsInPictureInPictureMode = isInPictureInPictureMode;
1598 }
1599
1600 @Override
1601 public void setOutlineProvider(ViewOutlineProvider provider) {
1602 super.setOutlineProvider(provider);
1603
1604 // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1605 mLastOutlineProvider = provider;
1606 }
1607
Wale Ogunwale8804af22015-11-17 09:18:15 -08001608 private void drawableChanged() {
1609 if (mChanging) {
1610 return;
1611 }
1612
Jorim Jaggid798fd32019-05-08 23:08:02 +02001613 // Fields can be null if super constructor calls setBackgroundDrawable.
1614 Rect framePadding = mFramePadding != null ? mFramePadding : new Rect();
1615 Rect backgroundPadding = mBackgroundPadding != null ? mBackgroundPadding : new Rect();
1616
1617 setPadding(framePadding.left + backgroundPadding.left,
1618 framePadding.top + backgroundPadding.top,
1619 framePadding.right + backgroundPadding.right,
1620 framePadding.bottom + backgroundPadding.bottom);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001621 requestLayout();
1622 invalidate();
1623
1624 int opacity = PixelFormat.OPAQUE;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001625 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1626 if (winConfig.hasWindowShadow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001627 // If the window has a shadow, it must be translucent.
1628 opacity = PixelFormat.TRANSLUCENT;
1629 } else{
1630 // Note: If there is no background, we will assume opaque. The
1631 // common case seems to be that an application sets there to be
1632 // no background so it can draw everything itself. For that,
1633 // we would like to assume OPAQUE and let the app force it to
1634 // the slower TRANSLUCENT mode if that is really what it wants.
1635 Drawable bg = getBackground();
1636 Drawable fg = getForeground();
1637 if (bg != null) {
1638 if (fg == null) {
1639 opacity = bg.getOpacity();
Jorim Jaggid798fd32019-05-08 23:08:02 +02001640 } else if (framePadding.left <= 0 && framePadding.top <= 0
1641 && framePadding.right <= 0 && framePadding.bottom <= 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001642 // If the frame padding is zero, then we can be opaque
1643 // if either the frame -or- the background is opaque.
1644 int fop = fg.getOpacity();
1645 int bop = bg.getOpacity();
1646 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001647 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001648 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1649 opacity = PixelFormat.OPAQUE;
1650 } else if (fop == PixelFormat.UNKNOWN) {
1651 opacity = bop;
1652 } else if (bop == PixelFormat.UNKNOWN) {
1653 opacity = fop;
1654 } else {
1655 opacity = Drawable.resolveOpacity(fop, bop);
1656 }
1657 } else {
1658 // For now we have to assume translucent if there is a
1659 // frame with padding... there is no way to tell if the
1660 // frame and background together will draw all pixels.
1661 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001662 Log.v(mLogTag, "Padding: " + mFramePadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001663 opacity = PixelFormat.TRANSLUCENT;
1664 }
1665 }
1666 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001667 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001668 }
1669
1670 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001671 Log.v(mLogTag, "Selected default opacity: " + opacity);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001672
1673 mDefaultOpacity = opacity;
1674 if (mFeatureId < 0) {
1675 mWindow.setDefaultWindowFormat(opacity);
1676 }
1677 }
1678
1679 @Override
1680 public void onWindowFocusChanged(boolean hasWindowFocus) {
1681 super.onWindowFocusChanged(hasWindowFocus);
1682
1683 // If the user is chording a menu shortcut, release the chord since
1684 // this window lost focus
1685 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1686 && mWindow.mPanelChordingKey != 0) {
1687 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1688 }
1689
1690 final Window.Callback cb = mWindow.getCallback();
1691 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1692 cb.onWindowFocusChanged(hasWindowFocus);
1693 }
1694
1695 if (mPrimaryActionMode != null) {
1696 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1697 }
1698 if (mFloatingActionMode != null) {
1699 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1700 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001701
1702 updateElevation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001703 }
1704
1705 @Override
1706 protected void onAttachedToWindow() {
1707 super.onAttachedToWindow();
1708
1709 final Window.Callback cb = mWindow.getCallback();
1710 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1711 cb.onAttachedToWindow();
1712 }
1713
1714 if (mFeatureId == -1) {
1715 /*
1716 * The main window has been attached, try to restore any panels
1717 * that may have been open before. This is called in cases where
1718 * an activity is being killed for configuration change and the
1719 * menu was open. When the activity is recreated, the menu
1720 * should be shown again.
1721 */
1722 mWindow.openPanelsAfterRestore();
1723 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001724
1725 if (!mWindowResizeCallbacksAdded) {
1726 // If there is no window callback installed there was no window set before. Set it now.
1727 // Note that our ViewRootImpl object will not change.
1728 getViewRootImpl().addWindowCallbacks(this);
1729 mWindowResizeCallbacksAdded = true;
1730 } else if (mBackdropFrameRenderer != null) {
1731 // We are resizing and this call happened due to a configuration change. Tell the
1732 // renderer about it.
1733 mBackdropFrameRenderer.onConfigurationChange();
1734 }
Andrii Kulian51c1b672017-04-07 18:39:32 -07001735 mWindow.onViewRootImplSet(getViewRootImpl());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001736 }
1737
1738 @Override
1739 protected void onDetachedFromWindow() {
1740 super.onDetachedFromWindow();
1741
1742 final Window.Callback cb = mWindow.getCallback();
1743 if (cb != null && mFeatureId < 0) {
1744 cb.onDetachedFromWindow();
1745 }
1746
1747 if (mWindow.mDecorContentParent != null) {
1748 mWindow.mDecorContentParent.dismissPopups();
1749 }
1750
1751 if (mPrimaryActionModePopup != null) {
1752 removeCallbacks(mShowPrimaryActionModePopup);
1753 if (mPrimaryActionModePopup.isShowing()) {
1754 mPrimaryActionModePopup.dismiss();
1755 }
1756 mPrimaryActionModePopup = null;
1757 }
1758 if (mFloatingToolbar != null) {
1759 mFloatingToolbar.dismiss();
1760 mFloatingToolbar = null;
1761 }
1762
1763 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1764 if (st != null && st.menu != null && mFeatureId < 0) {
1765 st.menu.close();
1766 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001767
Jorim Jaggib2005a02016-04-08 14:13:30 -07001768 releaseThreadedRenderer();
1769
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001770 if (mWindowResizeCallbacksAdded) {
1771 getViewRootImpl().removeWindowCallbacks(this);
1772 mWindowResizeCallbacksAdded = false;
1773 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001774 }
1775
1776 @Override
1777 public void onCloseSystemDialogs(String reason) {
1778 if (mFeatureId >= 0) {
1779 mWindow.closeAllPanels();
1780 }
1781 }
1782
1783 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1784 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1785 }
1786
1787 public InputQueue.Callback willYouTakeTheInputQueue() {
1788 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1789 }
1790
1791 public void setSurfaceType(int type) {
1792 mWindow.setType(type);
1793 }
1794
1795 public void setSurfaceFormat(int format) {
1796 mWindow.setFormat(format);
1797 }
1798
1799 public void setSurfaceKeepScreenOn(boolean keepOn) {
1800 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1801 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1802 }
1803
1804 @Override
1805 public void onRootViewScrollYChanged(int rootScrollY) {
1806 mRootScrollY = rootScrollY;
Evan Rosky0d654cb2019-02-26 10:59:10 -08001807 if (mDecorCaptionView != null) {
1808 mDecorCaptionView.onRootViewScrollYChanged(rootScrollY);
1809 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001810 updateColorViewTranslations();
1811 }
1812
1813 private ActionMode createActionMode(
1814 int type, ActionMode.Callback2 callback, View originatingView) {
1815 switch (type) {
1816 case ActionMode.TYPE_PRIMARY:
1817 default:
1818 return createStandaloneActionMode(callback);
1819 case ActionMode.TYPE_FLOATING:
1820 return createFloatingActionMode(originatingView, callback);
1821 }
1822 }
1823
1824 private void setHandledActionMode(ActionMode mode) {
1825 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1826 setHandledPrimaryActionMode(mode);
1827 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1828 setHandledFloatingActionMode(mode);
1829 }
1830 }
1831
1832 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1833 endOnGoingFadeAnimation();
1834 cleanupPrimaryActionMode();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001835 // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1836 // instance at all, or if there is one, but it is detached from window. The latter case
1837 // might happen when app is resized in multi-window mode and decor view is preserved
1838 // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1839 // app memory leaks because killMode() is called when the dismiss animation ends and from
1840 // cleanupPrimaryActionMode() invocation above.
1841 if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001842 if (mWindow.isFloating()) {
1843 // Use the action bar theme.
1844 final TypedValue outValue = new TypedValue();
1845 final Resources.Theme baseTheme = mContext.getTheme();
1846 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1847
1848 final Context actionBarContext;
1849 if (outValue.resourceId != 0) {
1850 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1851 actionBarTheme.setTo(baseTheme);
1852 actionBarTheme.applyStyle(outValue.resourceId, true);
1853
1854 actionBarContext = new ContextThemeWrapper(mContext, 0);
1855 actionBarContext.getTheme().setTo(actionBarTheme);
1856 } else {
1857 actionBarContext = mContext;
1858 }
1859
1860 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1861 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1862 R.attr.actionModePopupWindowStyle);
1863 mPrimaryActionModePopup.setWindowLayoutType(
1864 WindowManager.LayoutParams.TYPE_APPLICATION);
1865 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1866 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1867
1868 actionBarContext.getTheme().resolveAttribute(
1869 R.attr.actionBarSize, outValue, true);
1870 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1871 actionBarContext.getResources().getDisplayMetrics());
1872 mPrimaryActionModeView.setContentHeight(height);
1873 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1874 mShowPrimaryActionModePopup = new Runnable() {
1875 public void run() {
1876 mPrimaryActionModePopup.showAtLocation(
1877 mPrimaryActionModeView.getApplicationWindowToken(),
1878 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1879 endOnGoingFadeAnimation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001880
Chris Banese65b3fb2016-06-01 11:39:54 +01001881 if (shouldAnimatePrimaryActionModeView()) {
1882 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1883 0f, 1f);
1884 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1885 @Override
1886 public void onAnimationStart(Animator animation) {
1887 mPrimaryActionModeView.setVisibility(VISIBLE);
1888 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001889
Chris Banese65b3fb2016-06-01 11:39:54 +01001890 @Override
1891 public void onAnimationEnd(Animator animation) {
1892 mPrimaryActionModeView.setAlpha(1f);
1893 mFadeAnim = null;
1894 }
1895 });
1896 mFadeAnim.start();
1897 } else {
1898 mPrimaryActionModeView.setAlpha(1f);
1899 mPrimaryActionModeView.setVisibility(VISIBLE);
1900 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001901 }
1902 };
1903 } else {
Alan Viverette51efddb2017-04-05 10:00:01 -04001904 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001905 if (stub != null) {
1906 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001907 mPrimaryActionModePopup = null;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001908 }
1909 }
1910 }
1911 if (mPrimaryActionModeView != null) {
1912 mPrimaryActionModeView.killMode();
1913 ActionMode mode = new StandaloneActionMode(
1914 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1915 callback, mPrimaryActionModePopup == null);
1916 return mode;
1917 }
1918 return null;
1919 }
1920
1921 private void endOnGoingFadeAnimation() {
1922 if (mFadeAnim != null) {
1923 mFadeAnim.end();
1924 }
1925 }
1926
1927 private void setHandledPrimaryActionMode(ActionMode mode) {
1928 endOnGoingFadeAnimation();
1929 mPrimaryActionMode = mode;
1930 mPrimaryActionMode.invalidate();
1931 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1932 if (mPrimaryActionModePopup != null) {
1933 post(mShowPrimaryActionModePopup);
1934 } else {
Chris Banese65b3fb2016-06-01 11:39:54 +01001935 if (shouldAnimatePrimaryActionModeView()) {
1936 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1937 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1938 @Override
1939 public void onAnimationStart(Animator animation) {
1940 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1941 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001942
Chris Banese65b3fb2016-06-01 11:39:54 +01001943 @Override
1944 public void onAnimationEnd(Animator animation) {
1945 mPrimaryActionModeView.setAlpha(1f);
1946 mFadeAnim = null;
1947 }
1948 });
1949 mFadeAnim.start();
1950 } else {
1951 mPrimaryActionModeView.setAlpha(1f);
1952 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1953 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001954 }
1955 mPrimaryActionModeView.sendAccessibilityEvent(
1956 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1957 }
1958
Chris Banese65b3fb2016-06-01 11:39:54 +01001959 boolean shouldAnimatePrimaryActionModeView() {
1960 // We only to animate the action mode in if the decor has already been laid out.
1961 // If it hasn't been laid out, it hasn't been drawn to screen yet.
1962 return isLaidOut();
1963 }
1964
Wale Ogunwale8804af22015-11-17 09:18:15 -08001965 private ActionMode createFloatingActionMode(
1966 View originatingView, ActionMode.Callback2 callback) {
1967 if (mFloatingActionMode != null) {
1968 mFloatingActionMode.finish();
1969 }
1970 cleanupFloatingActionModeViews();
Tarandeep Singhc9c83a92017-08-29 14:39:22 -07001971 mFloatingToolbar = new FloatingToolbar(mWindow);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001972 final FloatingActionMode mode =
Abodunrinwa Toki17293cc2017-05-22 14:16:04 +01001973 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001974 mFloatingActionModeOriginatingView = originatingView;
1975 mFloatingToolbarPreDrawListener =
1976 new ViewTreeObserver.OnPreDrawListener() {
1977 @Override
1978 public boolean onPreDraw() {
1979 mode.updateViewLocationInWindow();
1980 return true;
1981 }
1982 };
1983 return mode;
1984 }
1985
1986 private void setHandledFloatingActionMode(ActionMode mode) {
1987 mFloatingActionMode = mode;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001988 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1989 mFloatingActionModeOriginatingView.getViewTreeObserver()
1990 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1991 }
1992
1993 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001994 * Informs the decor if the caption is attached and visible.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001995 * @param attachedAndVisible true when the decor is visible.
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001996 * Note that this will even be called if there is no caption.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001997 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001998 void enableCaption(boolean attachedAndVisible) {
1999 if (mHasCaption != attachedAndVisible) {
2000 mHasCaption = attachedAndVisible;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002001 if (getForeground() != null) {
2002 drawableChanged();
2003 }
2004 }
2005 }
2006
Wale Ogunwale8804af22015-11-17 09:18:15 -08002007 void setWindow(PhoneWindow phoneWindow) {
2008 mWindow = phoneWindow;
2009 Context context = getContext();
2010 if (context instanceof DecorContext) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002011 DecorContext decorContext = (DecorContext) context;
2012 decorContext.setPhoneWindow(mWindow);
2013 }
2014 }
2015
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002016 @Override
chaviwa213d302018-04-23 13:34:36 -07002017 public Resources getResources() {
2018 // Make sure the Resources object is propogated from the Context since it can be updated in
2019 // the Context object.
2020 return getContext().getResources();
2021 }
2022
2023 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002024 protected void onConfigurationChanged(Configuration newConfig) {
2025 super.onConfigurationChanged(newConfig);
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002026
Garfield Tan3b9613c2018-12-26 17:08:51 -08002027 updateDecorCaptionStatus(newConfig);
2028
2029 updateAvailableWidth();
2030 initializeElevation();
2031 }
2032
sanryhuang2ed879f2018-10-08 23:10:22 +08002033 @Override
2034 public void onMovedToDisplay(int displayId, Configuration config) {
2035 super.onMovedToDisplay(displayId, config);
2036 // Have to explicitly update displayId because it may use DecorContext
2037 getContext().updateDisplay(displayId);
2038 }
2039
Garfield Tan3b9613c2018-12-26 17:08:51 -08002040 /**
2041 * Determines if the workspace is entirely covered by the window.
2042 * @return {@code true} when the window is filling the entire screen/workspace.
2043 **/
2044 private boolean isFillingScreen(Configuration config) {
2045 final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
2046 == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
2047 return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
Yunfan Chen1831e8e2019-04-24 15:54:49 +09002048 & View.SYSTEM_UI_FLAG_FULLSCREEN));
Garfield Tan3b9613c2018-12-26 17:08:51 -08002049 }
2050
2051 private void updateDecorCaptionStatus(Configuration config) {
2052 final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
2053 && !isFillingScreen(config);
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002054 if (mDecorCaptionView == null && displayWindowDecor) {
2055 // Configuration now requires a caption.
2056 final LayoutInflater inflater = mWindow.getLayoutInflater();
2057 mDecorCaptionView = createDecorCaptionView(inflater);
2058 if (mDecorCaptionView != null) {
2059 if (mDecorCaptionView.getParent() == null) {
2060 addView(mDecorCaptionView, 0,
2061 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08002062 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002063 removeView(mContentRoot);
2064 mDecorCaptionView.addView(mContentRoot,
2065 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002066 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002067 } else if (mDecorCaptionView != null) {
2068 // We might have to change the kind of surface before we do anything else.
2069 mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
2070 enableCaption(displayWindowDecor);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002071 }
2072 }
2073
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002074 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002075 if (mBackdropFrameRenderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002076 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002077 mBackdropFrameRenderer.onResourcesLoaded(
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002078 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002079 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2080 getCurrentColor(mNavigationColorViewState));
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002081 }
2082
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002083 mDecorCaptionView = createDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002084 final View root = inflater.inflate(layoutResource, null);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002085 if (mDecorCaptionView != null) {
2086 if (mDecorCaptionView.getParent() == null) {
2087 addView(mDecorCaptionView,
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002088 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
2089 }
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002090 mDecorCaptionView.addView(root,
Filip Gruszczynski63250652015-11-18 14:43:01 -08002091 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002092 } else {
Jorim Jaggi0a13bfd2016-02-04 18:34:50 -08002093
2094 // Put it below the color views.
2095 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002096 }
2097 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002098 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002099 }
2100
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002101 private void loadBackgroundDrawablesIfNeeded() {
2102 if (mResizingBackgroundDrawable == null) {
Nader Jawad56c68bc2018-04-24 17:08:51 -07002103 mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
2104 mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
2105 || mWindow.isShowingWallpaper());
Winson Chung1af8eda2016-02-05 17:55:56 +00002106 if (mResizingBackgroundDrawable == null) {
2107 // We shouldn't really get here as the background fallback should be always
2108 // available since it is defaulted by the system.
2109 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
2110 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002111 }
2112 if (mCaptionBackgroundDrawable == null) {
2113 mCaptionBackgroundDrawable = getContext().getDrawable(
2114 R.drawable.decor_caption_title_focused);
2115 }
Chong Zhang0df63d52016-02-24 15:39:53 -08002116 if (mResizingBackgroundDrawable != null) {
2117 mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
2118 mResizingBackgroundDrawable.setCallback(null);
2119 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002120 }
2121
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002122 // Free floating overlapping windows require a caption.
2123 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002124 DecorCaptionView decorCaptionView = null;
2125 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002126 View view = getChildAt(i);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002127 if (view instanceof DecorCaptionView) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002128 // The decor was most likely saved from a relaunch - so reuse it.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002129 decorCaptionView = (DecorCaptionView) view;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002130 removeViewAt(i);
2131 }
2132 }
2133 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002134 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Chong Zhangfea963e2016-08-15 17:14:16 -07002135 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002136 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002137 // Only a non floating application window on one of the allowed workspaces can get a caption
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002138 if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002139 // Dependent on the brightness of the used title we either use the
2140 // dark or the light button frame.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002141 if (decorCaptionView == null) {
2142 decorCaptionView = inflateDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002143 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002144 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002145 } else {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002146 decorCaptionView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002147 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002148
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002149 // Tell the decor if it has a visible caption.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002150 enableCaption(decorCaptionView != null);
2151 return decorCaptionView;
2152 }
2153
2154 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
2155 final Context context = getContext();
2156 // We make a copy of the inflater, so it has the right context associated with it.
2157 inflater = inflater.from(context);
2158 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
2159 null);
2160 setDecorCaptionShade(context, view);
2161 return view;
2162 }
2163
2164 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
2165 final int shade = mWindow.getDecorCaptionShade();
2166 switch (shade) {
2167 case DECOR_CAPTION_SHADE_LIGHT:
2168 setLightDecorCaptionShade(view);
2169 break;
2170 case DECOR_CAPTION_SHADE_DARK:
2171 setDarkDecorCaptionShade(view);
2172 break;
2173 default: {
2174 TypedValue value = new TypedValue();
2175 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
2176 // We invert the shade depending on brightness of the theme. Dark shade for light
2177 // theme and vice versa. Thanks to this the buttons should be visible on the
2178 // background.
2179 if (Color.luminance(value.data) < 0.5) {
2180 setLightDecorCaptionShade(view);
2181 } else {
2182 setDarkDecorCaptionShade(view);
2183 }
2184 break;
2185 }
2186 }
2187 }
2188
2189 void updateDecorCaptionShade() {
2190 if (mDecorCaptionView != null) {
2191 setDecorCaptionShade(getContext(), mDecorCaptionView);
2192 }
2193 }
2194
2195 private void setLightDecorCaptionShade(DecorCaptionView view) {
2196 view.findViewById(R.id.maximize_window).setBackgroundResource(
2197 R.drawable.decor_maximize_button_light);
2198 view.findViewById(R.id.close_window).setBackgroundResource(
2199 R.drawable.decor_close_button_light);
2200 }
2201
2202 private void setDarkDecorCaptionShade(DecorCaptionView view) {
2203 view.findViewById(R.id.maximize_window).setBackgroundResource(
2204 R.drawable.decor_maximize_button_dark);
2205 view.findViewById(R.id.close_window).setBackgroundResource(
2206 R.drawable.decor_close_button_dark);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002207 }
2208
2209 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002210 * Returns the color used to fill areas the app has not rendered content to yet when the
2211 * user is resizing the window of an activity in multi-window mode.
2212 */
Nader Jawad56c68bc2018-04-24 17:08:51 -07002213 public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
2214 @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
2215 if (backgroundDrawable != null) {
2216 return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002217 }
2218
Nader Jawad56c68bc2018-04-24 17:08:51 -07002219 if (fallbackDrawable != null) {
2220 return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002221 }
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002222 return new ColorDrawable(Color.BLACK);
2223 }
2224
2225 /**
2226 * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2227 * window is not translucent.
2228 */
2229 private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2230 boolean windowTranslucent) {
2231 if (!windowTranslucent && drawable instanceof ColorDrawable) {
2232 ColorDrawable colorDrawable = (ColorDrawable) drawable;
2233 int color = colorDrawable.getColor();
2234 if (Color.alpha(color) != 255) {
2235 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2236 .mutate();
2237 copy.setColor(
2238 Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2239 return copy;
2240 }
2241 }
2242 return drawable;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002243 }
2244
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002245 void clearContentView() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002246 if (mDecorCaptionView != null) {
Filip Gruszczynski63250652015-11-18 14:43:01 -08002247 mDecorCaptionView.removeContentView();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002248 } else {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002249 // This window doesn't have caption, so we need to remove everything except our views
2250 // we might have added.
2251 for (int i = getChildCount() - 1; i >= 0; i--) {
2252 View v = getChildAt(i);
2253 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
Yohei Yukawa8f162c62018-01-10 13:18:09 -08002254 && v != mStatusGuard) {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002255 removeViewAt(i);
2256 }
2257 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002258 }
2259 }
2260
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002261 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002262 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2263 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002264 if (mBackdropFrameRenderer != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002265 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002266 }
2267 }
2268
2269 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002270 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002271 Rect stableInsets, int resizeMode) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002272 if (mWindow.isDestroyed()) {
2273 // If the owner's window is gone, we should not be able to come here anymore.
2274 releaseThreadedRenderer();
2275 return;
2276 }
2277 if (mBackdropFrameRenderer != null) {
2278 return;
2279 }
Stan Iliev45faba52016-06-28 13:33:15 -04002280 final ThreadedRenderer renderer = getThreadedRenderer();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002281 if (renderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002282 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002283 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002284 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002285 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2286 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
Garfield Tanfbd8ea62018-10-16 17:09:49 -07002287 stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002288
2289 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2290 // If we want to get the shadow shown while resizing, we would need to elevate a new
2291 // element which owns the caption and has the elevation.
2292 updateElevation();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002293
2294 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002295 }
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002296 mResizeMode = resizeMode;
2297 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002298 }
2299
2300 @Override
2301 public void onWindowDragResizeEnd() {
2302 releaseThreadedRenderer();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002303 updateColorViews(null /* insets */, false);
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002304 mResizeMode = RESIZE_MODE_INVALID;
2305 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002306 }
2307
2308 @Override
2309 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2310 if (mBackdropFrameRenderer == null) {
2311 return false;
2312 }
2313 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2314 }
2315
2316 @Override
2317 public void onRequestDraw(boolean reportNextDraw) {
2318 if (mBackdropFrameRenderer != null) {
2319 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2320 } else if (reportNextDraw) {
2321 // If render thread is gone, just report immediately.
2322 if (isAttachedToWindow()) {
2323 getViewRootImpl().reportDrawFinish();
2324 }
2325 }
2326 }
2327
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002328 @Override
John Reck32f140aa62018-10-04 15:08:24 -07002329 public void onPostDraw(RecordingCanvas canvas) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002330 drawResizingShadowIfNeeded(canvas);
Jorim Jaggi86d30ff2019-06-11 17:54:07 +02002331 drawLegacyNavigationBarBackground(canvas);
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002332 }
2333
2334 private void initResizingPaints() {
2335 final int startColor = mContext.getResources().getColor(
2336 R.color.resize_shadow_start_color, null);
2337 final int endColor = mContext.getResources().getColor(
2338 R.color.resize_shadow_end_color, null);
2339 final int middleColor = (startColor + endColor) / 2;
2340 mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2341 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2342 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2343 mVerticalResizeShadowPaint.setShader(new LinearGradient(
2344 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2345 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2346 }
2347
John Reck32f140aa62018-10-04 15:08:24 -07002348 private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002349 if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2350 || mWindow.isTranslucent()
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002351 || mWindow.isShowingWallpaper()) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002352 return;
2353 }
2354 canvas.save();
2355 canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2356 canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2357 canvas.restore();
2358 canvas.save();
2359 canvas.translate(getWidth() - mFrameOffsets.right, 0);
2360 canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2361 canvas.restore();
2362 }
2363
Jorim Jaggi86d30ff2019-06-11 17:54:07 +02002364 private void drawLegacyNavigationBarBackground(RecordingCanvas canvas) {
2365 if (!mDrawLegacyNavigationBarBackground) {
2366 return;
2367 }
2368 View v = mNavigationColorViewState.view;
2369 if (v == null) {
2370 return;
2371 }
2372 canvas.drawRect(v.getLeft(), v.getTop(), v.getRight(), v.getBottom(),
2373 mLegacyNavigationBarBackgroundPaint);
2374 }
2375
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002376 /** Release the renderer thread which is usually done when the user stops resizing. */
2377 private void releaseThreadedRenderer() {
Chong Zhangd3fd96c2016-02-08 18:25:24 -08002378 if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2379 mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2380 mLastBackgroundDrawableCb = null;
2381 }
2382
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002383 if (mBackdropFrameRenderer != null) {
2384 mBackdropFrameRenderer.releaseRenderer();
2385 mBackdropFrameRenderer = null;
2386 // Bring the shadow back.
2387 updateElevation();
2388 }
2389 }
2390
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002391 private boolean isResizing() {
2392 return mBackdropFrameRenderer != null;
2393 }
2394
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002395 /**
2396 * The elevation gets set for the first time and the framework needs to be informed that
2397 * the surface layer gets created with the shadow size in mind.
2398 */
2399 private void initializeElevation() {
2400 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2401 mAllowUpdateElevation = false;
2402 updateElevation();
2403 }
2404
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002405 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002406 float elevation = 0;
2407 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2408 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2409 // since the shadow is bound to the content size and not the target size.
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002410 final int windowingMode =
2411 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2412 if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002413 elevation = hasWindowFocus() ?
2414 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
Jaewan Kim880eff62016-04-27 10:36:35 +09002415 // Add a maximum shadow height value to the top level view.
2416 // Note that pinned stack doesn't have focus
2417 // so maximum shadow height adjustment isn't needed.
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002418 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
Robert Carr232b5f82017-04-17 15:11:35 -07002419 if (!mAllowUpdateElevation) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002420 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2421 }
2422 // Convert the DP elevation into physical pixels.
2423 elevation = dipToPx(elevation);
2424 mElevationAdjustedForStack = true;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002425 } else if (windowingMode == WINDOWING_MODE_PINNED) {
Robert Carr32bcb102018-01-29 15:03:23 -08002426 elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
Robert Carr232b5f82017-04-17 15:11:35 -07002427 mElevationAdjustedForStack = true;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002428 } else {
2429 mElevationAdjustedForStack = false;
2430 }
2431
2432 // Don't change the elevation if we didn't previously adjust it for the stack it was in
2433 // or it didn't change.
2434 if ((wasAdjustedForStack || mElevationAdjustedForStack)
2435 && getElevation() != elevation) {
Garfield Tanfbd8ea62018-10-16 17:09:49 -07002436 if (!isResizing()) {
2437 mWindow.setElevation(elevation);
2438 } else {
2439 // Just suppress the shadow when resizing, don't adjust surface insets because it'll
2440 // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
2441 // will compensate the offset when passing to BackdropFrameRenderer.
2442 setElevation(elevation);
2443 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002444 }
2445 }
2446
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002447 boolean isShowingCaption() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002448 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002449 }
2450
2451 int getCaptionHeight() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002452 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002453 }
2454
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002455 /**
2456 * Converts a DIP measure into physical pixels.
2457 * @param dip The dip value.
2458 * @return Returns the number of pixels.
2459 */
2460 private float dipToPx(float dip) {
2461 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2462 getResources().getDisplayMetrics());
2463 }
2464
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002465 /**
2466 * Provide an override of the caption background drawable.
2467 */
2468 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2469 mUserCaptionBackgroundDrawable = drawable;
2470 if (mBackdropFrameRenderer != null) {
2471 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2472 }
2473 }
2474
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002475 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2476 if (params == null) {
2477 return "";
2478 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002479 final String[] split = params.getTitle().toString().split("\\.");
2480 if (split.length > 0) {
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002481 return split[split.length - 1];
2482 } else {
2483 return "";
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002484 }
2485 }
2486
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002487 void updateLogTag(WindowManager.LayoutParams params) {
2488 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2489 }
2490
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002491 private void updateAvailableWidth() {
2492 Resources res = getResources();
2493 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2494 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2495 }
2496
Clara Bayarri75e09792015-07-29 16:20:40 +01002497 /**
2498 * @hide
2499 */
2500 @Override
Clara Bayarrifcd7e802016-03-10 12:58:18 +00002501 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
Clara Bayarri75e09792015-07-29 16:20:40 +01002502 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
Michael Wright936f27c2017-04-11 23:23:42 +01002503 final Menu menu = st != null ? st.menu : null;
2504 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2505 mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
Clara Bayarri75e09792015-07-29 16:20:40 +01002506 }
2507 }
2508
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002509 @Override
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -08002510 public void dispatchPointerCaptureChanged(boolean hasCapture) {
2511 super.dispatchPointerCaptureChanged(hasCapture);
2512 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2513 mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2514 }
2515 }
2516
2517 @Override
Phil Weaverf00cd142017-03-03 13:44:00 -08002518 public int getAccessibilityViewId() {
2519 return AccessibilityNodeInfo.ROOT_ITEM_ID;
2520 }
2521
2522 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002523 public String toString() {
2524 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2525 + getTitleSuffix(mWindow.getAttributes()) + "]";
2526 }
2527
Wale Ogunwale8804af22015-11-17 09:18:15 -08002528 private static class ColorViewState {
2529 View view = null;
2530 int targetVisibility = View.INVISIBLE;
2531 boolean present = false;
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002532 boolean visible;
2533 int color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002534
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002535 final ColorViewAttributes attributes;
2536
2537 ColorViewState(ColorViewAttributes attributes) {
2538 this.attributes = attributes;
2539 }
2540 }
2541
2542 public static class ColorViewAttributes {
2543
Wale Ogunwale8804af22015-11-17 09:18:15 -08002544 final int id;
2545 final int systemUiHideFlag;
2546 final int translucentFlag;
2547 final int verticalGravity;
2548 final int horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002549 final int seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002550 final String transitionName;
2551 final int hideWindowFlag;
2552
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002553 private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2554 int horizontalGravity, int seascapeGravity, String transitionName, int id,
2555 int hideWindowFlag) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08002556 this.id = id;
2557 this.systemUiHideFlag = systemUiHideFlag;
2558 this.translucentFlag = translucentFlag;
2559 this.verticalGravity = verticalGravity;
2560 this.horizontalGravity = horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002561 this.seascapeGravity = seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002562 this.transitionName = transitionName;
2563 this.hideWindowFlag = hideWindowFlag;
2564 }
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002565
2566 public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2567 return (sysUiVis & systemUiHideFlag) == 0
2568 && (windowFlags & hideWindowFlag) == 0
2569 && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2570 || force);
2571 }
2572
2573 public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2574 return present
2575 && (color & Color.BLACK) != 0
2576 && ((windowFlags & translucentFlag) == 0 || force);
2577 }
2578
2579 public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2580 final boolean present = isPresent(sysUiVis, windowFlags, force);
2581 return isVisible(present, color, windowFlags, force);
2582 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002583 }
2584
2585 /**
2586 * Clears out internal references when the action mode is destroyed.
2587 */
2588 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2589 private final ActionMode.Callback mWrapped;
2590
2591 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2592 mWrapped = wrapped;
2593 }
2594
2595 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2596 return mWrapped.onCreateActionMode(mode, menu);
2597 }
2598
2599 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2600 requestFitSystemWindows();
2601 return mWrapped.onPrepareActionMode(mode, menu);
2602 }
2603
2604 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2605 return mWrapped.onActionItemClicked(mode, item);
2606 }
2607
2608 public void onDestroyActionMode(ActionMode mode) {
2609 mWrapped.onDestroyActionMode(mode);
2610 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
Jorim Jaggi8f5701b2016-04-04 18:36:02 -07002611 >= M;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002612 final boolean isPrimary;
2613 final boolean isFloating;
2614 if (isMncApp) {
2615 isPrimary = mode == mPrimaryActionMode;
2616 isFloating = mode == mFloatingActionMode;
2617 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002618 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002619 + mode + " was not the current primary action mode! Expected "
2620 + mPrimaryActionMode);
2621 }
2622 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002623 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002624 + mode + " was not the current floating action mode! Expected "
2625 + mFloatingActionMode);
2626 }
2627 } else {
2628 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2629 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2630 }
2631 if (isPrimary) {
2632 if (mPrimaryActionModePopup != null) {
2633 removeCallbacks(mShowPrimaryActionModePopup);
2634 }
2635 if (mPrimaryActionModeView != null) {
2636 endOnGoingFadeAnimation();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002637 // Store action mode view reference, so we can access it safely when animation
2638 // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2639 // so no need to store reference to it in separate variable.
2640 final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002641 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2642 1f, 0f);
2643 mFadeAnim.addListener(new Animator.AnimatorListener() {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002644
Wale Ogunwale8804af22015-11-17 09:18:15 -08002645 @Override
2646 public void onAnimationStart(Animator animation) {
2647
2648 }
2649
2650 @Override
2651 public void onAnimationEnd(Animator animation) {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002652 // If mPrimaryActionModeView has changed - it means that we've
2653 // cleared the content while preserving decor view. We don't
2654 // want to change the state of new instances accidentally here.
2655 if (lastActionModeView == mPrimaryActionModeView) {
2656 lastActionModeView.setVisibility(GONE);
2657 if (mPrimaryActionModePopup != null) {
2658 mPrimaryActionModePopup.dismiss();
2659 }
2660 lastActionModeView.killMode();
2661 mFadeAnim = null;
Adrian Roos27db0ca2019-05-29 16:12:09 +02002662 requestApplyInsets();
Wale Ogunwale8804af22015-11-17 09:18:15 -08002663 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002664 }
2665
2666 @Override
2667 public void onAnimationCancel(Animator animation) {
2668
2669 }
2670
2671 @Override
2672 public void onAnimationRepeat(Animator animation) {
2673
2674 }
2675 });
2676 mFadeAnim.start();
2677 }
2678
2679 mPrimaryActionMode = null;
2680 } else if (isFloating) {
2681 cleanupFloatingActionModeViews();
2682 mFloatingActionMode = null;
2683 }
2684 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2685 try {
2686 mWindow.getCallback().onActionModeFinished(mode);
2687 } catch (AbstractMethodError ame) {
2688 // Older apps might not implement this callback method.
2689 }
2690 }
2691 requestFitSystemWindows();
2692 }
2693
2694 @Override
2695 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2696 if (mWrapped instanceof ActionMode.Callback2) {
2697 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2698 } else {
2699 super.onGetContentRect(mode, view, outRect);
2700 }
2701 }
2702 }
2703}