blob: 465957d7cfd061dfcafaa6610e66aa14691ef678 [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
Wale Ogunwale3382ab12017-07-27 08:55:03 -070019import android.app.WindowConfiguration;
Winson Chung4d8681f2017-05-23 16:22:08 -070020import android.graphics.Outline;
Jason Monkea506c62017-09-01 12:40:06 -040021import android.graphics.drawable.InsetDrawable;
22import android.graphics.drawable.LayerDrawable;
23import android.util.Pair;
Winson Chung4d8681f2017-05-23 16:22:08 -070024import android.view.ViewOutlineProvider;
Phil Weaverf00cd142017-03-03 13:44:00 -080025import android.view.accessibility.AccessibilityNodeInfo;
Wale Ogunwale8804af22015-11-17 09:18:15 -080026import com.android.internal.R;
Clara Bayarri75e09792015-07-29 16:20:40 +010027import com.android.internal.policy.PhoneWindow.PanelFeatureState;
Alan Viverette77fb85e2015-12-14 11:42:44 -050028import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -080029import com.android.internal.view.FloatingActionMode;
30import com.android.internal.view.RootViewSurfaceTaker;
31import com.android.internal.view.StandaloneActionMode;
32import com.android.internal.view.menu.ContextMenuBuilder;
Alan Viverette021627e2015-11-25 14:22:00 -050033import com.android.internal.view.menu.MenuHelper;
Wale Ogunwale8804af22015-11-17 09:18:15 -080034import com.android.internal.widget.ActionBarContextView;
35import com.android.internal.widget.BackgroundFallback;
Wale Ogunwale62a91d62015-11-18 11:44:10 -080036import com.android.internal.widget.DecorCaptionView;
Wale Ogunwale8804af22015-11-17 09:18:15 -080037import com.android.internal.widget.FloatingToolbar;
38
Clara Bayarri75e09792015-07-29 16:20:40 +010039import java.util.List;
40
Wale Ogunwale8804af22015-11-17 09:18:15 -080041import android.animation.Animator;
Chris Banese65b3fb2016-06-01 11:39:54 +010042import android.animation.AnimatorListenerAdapter;
Wale Ogunwale8804af22015-11-17 09:18:15 -080043import android.animation.ObjectAnimator;
Wale Ogunwale8804af22015-11-17 09:18:15 -080044import android.content.Context;
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -080045import android.content.res.Configuration;
Wale Ogunwale8804af22015-11-17 09:18:15 -080046import android.content.res.Resources;
47import android.graphics.Canvas;
48import android.graphics.Color;
Jorim Jaggic39c7b02016-03-24 10:47:07 -070049import android.graphics.LinearGradient;
50import android.graphics.Paint;
Wale Ogunwale8804af22015-11-17 09:18:15 -080051import android.graphics.PixelFormat;
52import android.graphics.Rect;
Chris Craik867b8122016-05-05 16:19:22 -070053import android.graphics.Region;
Jorim Jaggic39c7b02016-03-24 10:47:07 -070054import android.graphics.Shader;
Jorim Jaggi8e879f12016-05-25 16:41:49 -070055import android.graphics.drawable.ColorDrawable;
Wale Ogunwale8804af22015-11-17 09:18:15 -080056import android.graphics.drawable.Drawable;
Wale Ogunwale8804af22015-11-17 09:18:15 -080057import android.util.DisplayMetrics;
58import android.util.Log;
59import android.util.TypedValue;
60import android.view.ActionMode;
61import android.view.ContextThemeWrapper;
Jorim Jaggic39c7b02016-03-24 10:47:07 -070062import android.view.DisplayListCanvas;
Wale Ogunwale8804af22015-11-17 09:18:15 -080063import android.view.Gravity;
64import android.view.InputQueue;
65import android.view.KeyEvent;
Clara Bayarri75e09792015-07-29 16:20:40 +010066import android.view.KeyboardShortcutGroup;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080067import android.view.LayoutInflater;
Wale Ogunwale8804af22015-11-17 09:18:15 -080068import android.view.Menu;
69import android.view.MenuItem;
70import android.view.MotionEvent;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080071import android.view.ThreadedRenderer;
Wale Ogunwale8804af22015-11-17 09:18:15 -080072import android.view.View;
73import android.view.ViewGroup;
74import android.view.ViewStub;
75import android.view.ViewTreeObserver;
76import android.view.Window;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080077import android.view.WindowCallbacks;
Wale Ogunwale8804af22015-11-17 09:18:15 -080078import android.view.WindowInsets;
79import android.view.WindowManager;
80import android.view.accessibility.AccessibilityEvent;
81import android.view.accessibility.AccessibilityManager;
82import android.view.animation.AnimationUtils;
83import android.view.animation.Interpolator;
84import android.widget.FrameLayout;
85import android.widget.PopupWindow;
86
Robert Carr32bcb102018-01-29 15:03:23 -080087import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
Wale Ogunwale3382ab12017-07-27 08:55:03 -070088import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
89import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -080090import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
Jorim Jaggi8f5701b2016-04-04 18:36:02 -070091import static android.os.Build.VERSION_CODES.M;
92import static android.os.Build.VERSION_CODES.N;
Wale Ogunwale8804af22015-11-17 09:18:15 -080093import static android.view.View.MeasureSpec.AT_MOST;
94import static android.view.View.MeasureSpec.EXACTLY;
95import static android.view.View.MeasureSpec.getMode;
96import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
97import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -080098import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
99import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800100import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
101import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
Jorim Jaggi9f6798a2016-02-10 22:16:06 -0800102import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800103import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800104import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
105import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800106import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
107import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Chong Zhangfea963e2016-08-15 17:14:16 -0700108import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
Clara Bayarri75e09792015-07-29 16:20:40 +0100109import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800110
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800111/** @hide */
112public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800113 private static final String TAG = "DecorView";
114
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800115 private static final boolean DEBUG_MEASURE = false;
116
Wale Ogunwale8804af22015-11-17 09:18:15 -0800117 private static final boolean SWEEP_OPEN_MENU = false;
118
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800119 // The height of a window which has focus in DIP.
120 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
121 // The height of a window which has not in DIP.
122 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
123
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200124 public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
125 new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
126 Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
127 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
128 com.android.internal.R.id.statusBarBackground,
129 FLAG_FULLSCREEN);
130
131 public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
132 new ColorViewAttributes(
133 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
134 Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
135 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
136 com.android.internal.R.id.navigationBarBackground,
137 0 /* hideWindowFlag */);
138
Winson Chung4d8681f2017-05-23 16:22:08 -0700139 // This is used to workaround an issue where the PiP shadow can be transparent if the window
140 // background is transparent
141 private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
142 @Override
143 public void getOutline(View view, Outline outline) {
144 outline.setRect(0, 0, view.getWidth(), view.getHeight());
145 outline.setAlpha(1f);
146 }
147 };
148
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800149 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
150 // size calculation takes the shadow size into account. We set the elevation currently
151 // to max until the first layout command has been executed.
152 private boolean mAllowUpdateElevation = false;
153
154 private boolean mElevationAdjustedForStack = false;
155
Winson Chung4d8681f2017-05-23 16:22:08 -0700156 // Keeps track of the picture-in-picture mode for the view shadow
157 private boolean mIsInPictureInPictureMode;
158
159 // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER
160 private ViewOutlineProvider mLastOutlineProvider;
161
Wale Ogunwale8804af22015-11-17 09:18:15 -0800162 int mDefaultOpacity = PixelFormat.OPAQUE;
163
164 /** The feature ID of the panel, or -1 if this is the application's DecorView */
165 private final int mFeatureId;
166
167 private final Rect mDrawingBounds = new Rect();
168
169 private final Rect mBackgroundPadding = new Rect();
170
171 private final Rect mFramePadding = new Rect();
172
173 private final Rect mFrameOffsets = new Rect();
174
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800175 private boolean mHasCaption = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800176
177 private boolean mChanging;
178
179 private Drawable mMenuBackground;
180 private boolean mWatchingForMenu;
181 private int mDownY;
182
183 ActionMode mPrimaryActionMode;
184 private ActionMode mFloatingActionMode;
185 private ActionBarContextView mPrimaryActionModeView;
186 private PopupWindow mPrimaryActionModePopup;
187 private Runnable mShowPrimaryActionModePopup;
188 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
189 private View mFloatingActionModeOriginatingView;
190 private FloatingToolbar mFloatingToolbar;
191 private ObjectAnimator mFadeAnim;
192
193 // View added at runtime to draw under the status bar area
194 private View mStatusGuard;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800195
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200196 private final ColorViewState mStatusColorViewState =
197 new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
198 private final ColorViewState mNavigationColorViewState =
199 new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800200
201 private final Interpolator mShowInterpolator;
202 private final Interpolator mHideInterpolator;
203 private final int mBarEnterExitDuration;
Jorim Jaggi8f5701b2016-04-04 18:36:02 -0700204 final boolean mForceWindowDrawsStatusBarBackground;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800205 private final int mSemiTransparentStatusBarColor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800206
207 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
208
209 private int mLastTopInset = 0;
210 private int mLastBottomInset = 0;
211 private int mLastRightInset = 0;
Adrian Roos85d202b2016-06-02 16:27:47 -0700212 private int mLastLeftInset = 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800213 private boolean mLastHasTopStableInset = false;
214 private boolean mLastHasBottomStableInset = false;
215 private boolean mLastHasRightStableInset = false;
Adrian Roos85d202b2016-06-02 16:27:47 -0700216 private boolean mLastHasLeftStableInset = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800217 private int mLastWindowFlags = 0;
Jorim Jaggie5638a62016-03-25 22:57:01 -0700218 private boolean mLastShouldAlwaysConsumeNavBar = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800219
220 private int mRootScrollY = 0;
221
222 private PhoneWindow mWindow;
223
224 ViewGroup mContentRoot;
225
226 private Rect mTempRect;
227 private Rect mOutsets = new Rect();
228
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800229 // This is the caption view for the window, containing the caption and window control
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800230 // buttons. The visibility of this decor depends on the workspace and the window type.
231 // If the window type does not require such a view, this member might be null.
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800232 DecorCaptionView mDecorCaptionView;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800233
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800234 private boolean mWindowResizeCallbacksAdded = false;
Chong Zhangd3fd96c2016-02-08 18:25:24 -0800235 private Drawable.Callback mLastBackgroundDrawableCb = null;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800236 private BackdropFrameRenderer mBackdropFrameRenderer = null;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800237 private Drawable mResizingBackgroundDrawable;
238 private Drawable mCaptionBackgroundDrawable;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800239 private Drawable mUserCaptionBackgroundDrawable;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800240
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800241 private float mAvailableWidth;
242
243 String mLogTag = TAG;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800244 private final Rect mFloatingInsets = new Rect();
245 private boolean mApplyFloatingVerticalInsets = false;
246 private boolean mApplyFloatingHorizontalInsets = false;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800247
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700248 private int mResizeMode = RESIZE_MODE_INVALID;
249 private final int mResizeShadowSize;
250 private final Paint mVerticalResizeShadowPaint = new Paint();
251 private final Paint mHorizontalResizeShadowPaint = new Paint();
252
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800253 DecorView(Context context, int featureId, PhoneWindow window,
254 WindowManager.LayoutParams params) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800255 super(context);
256 mFeatureId = featureId;
257
258 mShowInterpolator = AnimationUtils.loadInterpolator(context,
259 android.R.interpolator.linear_out_slow_in);
260 mHideInterpolator = AnimationUtils.loadInterpolator(context,
261 android.R.interpolator.fast_out_linear_in);
262
263 mBarEnterExitDuration = context.getResources().getInteger(
264 R.integer.dock_enter_exit_duration);
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800265 mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean(
Jorim Jaggi8f5701b2016-04-04 18:36:02 -0700266 R.bool.config_forceWindowDrawsStatusBarBackground)
267 && context.getApplicationInfo().targetSdkVersion >= N;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800268 mSemiTransparentStatusBarColor = context.getResources().getColor(
269 R.color.system_bar_background_semi_transparent, null /* theme */);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800270
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800271 updateAvailableWidth();
272
Wale Ogunwale8804af22015-11-17 09:18:15 -0800273 setWindow(window);
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800274
275 updateLogTag(params);
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700276
277 mResizeShadowSize = context.getResources().getDimensionPixelSize(
278 R.dimen.resize_shadow_size);
279 initResizingPaints();
Wale Ogunwale8804af22015-11-17 09:18:15 -0800280 }
281
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800282 void setBackgroundFallback(int resId) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800283 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
284 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
285 }
286
287 @Override
Chris Craik867b8122016-05-05 16:19:22 -0700288 public boolean gatherTransparentRegion(Region region) {
289 boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
290 boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
291 boolean decorOpaque = super.gatherTransparentRegion(region);
292
293 // combine bools after computation, so each method above always executes
294 return statusOpaque || navOpaque || decorOpaque;
295 }
296
297 boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
298 if (colorViewState.view != null && colorViewState.visible && isResizing()) {
299 // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
300 // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
301 // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
302 return colorViewState.view.gatherTransparentRegion(region);
303 }
304 return false; // no opaque area added
305 }
306
307 @Override
Wale Ogunwale8804af22015-11-17 09:18:15 -0800308 public void onDraw(Canvas c) {
309 super.onDraw(c);
Jorim Jaggi853d17d2017-05-19 14:53:55 +0200310
Adrian Roos786ea782018-04-30 16:05:00 +0200311 mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
312 mStatusColorViewState.view, mNavigationColorViewState.view);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800313 }
314
315 @Override
316 public boolean dispatchKeyEvent(KeyEvent event) {
317 final int keyCode = event.getKeyCode();
318 final int action = event.getAction();
319 final boolean isDown = action == KeyEvent.ACTION_DOWN;
320
321 if (isDown && (event.getRepeatCount() == 0)) {
322 // First handle chording of panel key: if a panel key is held
323 // but not released, try to execute a shortcut in it.
324 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
325 boolean handled = dispatchKeyShortcutEvent(event);
326 if (handled) {
327 return true;
328 }
329 }
330
331 // If a panel is open, perform a shortcut on it without the
332 // chorded panel key
333 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
334 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
335 return true;
336 }
337 }
338 }
339
340 if (!mWindow.isDestroyed()) {
341 final Window.Callback cb = mWindow.getCallback();
342 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
343 : super.dispatchKeyEvent(event);
344 if (handled) {
345 return true;
346 }
347 }
348
349 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
350 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
351 }
352
353 @Override
354 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
355 // If the panel is already prepared, then perform the shortcut using it.
356 boolean handled;
357 if (mWindow.mPreparedPanel != null) {
358 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
359 Menu.FLAG_PERFORM_NO_CLOSE);
360 if (handled) {
361 if (mWindow.mPreparedPanel != null) {
362 mWindow.mPreparedPanel.isHandled = true;
363 }
364 return true;
365 }
366 }
367
368 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
369 final Window.Callback cb = mWindow.getCallback();
370 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
371 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
372 if (handled) {
373 return true;
374 }
375
376 // If the panel is not prepared, then we may be trying to handle a shortcut key
377 // combination such as Control+C. Temporarily prepare the panel then mark it
378 // unprepared again when finished to ensure that the panel will again be prepared
379 // the next time it is shown for real.
380 PhoneWindow.PanelFeatureState st =
381 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
382 if (st != null && mWindow.mPreparedPanel == null) {
383 mWindow.preparePanel(st, ev);
384 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
385 Menu.FLAG_PERFORM_NO_CLOSE);
386 st.isPrepared = false;
387 if (handled) {
388 return true;
389 }
390 }
391 return false;
392 }
393
394 @Override
395 public boolean dispatchTouchEvent(MotionEvent ev) {
396 final Window.Callback cb = mWindow.getCallback();
397 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
398 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
399 }
400
401 @Override
402 public boolean dispatchTrackballEvent(MotionEvent ev) {
403 final Window.Callback cb = mWindow.getCallback();
404 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
405 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
406 }
407
408 @Override
409 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
410 final Window.Callback cb = mWindow.getCallback();
411 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
412 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
413 }
414
415 public boolean superDispatchKeyEvent(KeyEvent event) {
416 // Give priority to closing action modes if applicable.
417 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
418 final int action = event.getAction();
419 // Back cancels action modes first.
420 if (mPrimaryActionMode != null) {
421 if (action == KeyEvent.ACTION_UP) {
422 mPrimaryActionMode.finish();
423 }
424 return true;
425 }
426 }
427
Evan Rosky5e29c072017-06-02 17:31:22 -0700428 if (super.dispatchKeyEvent(event)) {
429 return true;
430 }
431
432 return (getViewRootImpl() != null) && getViewRootImpl().dispatchKeyFallbackEvent(event);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800433 }
434
435 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
436 return super.dispatchKeyShortcutEvent(event);
437 }
438
439 public boolean superDispatchTouchEvent(MotionEvent event) {
440 return super.dispatchTouchEvent(event);
441 }
442
443 public boolean superDispatchTrackballEvent(MotionEvent event) {
444 return super.dispatchTrackballEvent(event);
445 }
446
447 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
448 return super.dispatchGenericMotionEvent(event);
449 }
450
451 @Override
452 public boolean onTouchEvent(MotionEvent event) {
453 return onInterceptTouchEvent(event);
454 }
455
456 private boolean isOutOfInnerBounds(int x, int y) {
457 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
458 }
459
460 private boolean isOutOfBounds(int x, int y) {
461 return x < -5 || y < -5 || x > (getWidth() + 5)
462 || y > (getHeight() + 5);
463 }
464
465 @Override
466 public boolean onInterceptTouchEvent(MotionEvent event) {
467 int action = event.getAction();
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800468 if (mHasCaption && isShowingCaption()) {
469 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
470 // was (starting) outside the window. Window resizing events should be handled by
471 // WindowManager.
Wale Ogunwale8804af22015-11-17 09:18:15 -0800472 // TODO: Investigate how to handle the outside touch in window manager
473 // without generating these events.
474 // Currently we receive these because we need to enlarge the window's
475 // touch region so that the monitor channel receives the events
476 // in the outside touch area.
477 if (action == MotionEvent.ACTION_DOWN) {
478 final int x = (int) event.getX();
479 final int y = (int) event.getY();
480 if (isOutOfInnerBounds(x, y)) {
481 return true;
482 }
483 }
484 }
485
486 if (mFeatureId >= 0) {
487 if (action == MotionEvent.ACTION_DOWN) {
488 int x = (int)event.getX();
489 int y = (int)event.getY();
490 if (isOutOfBounds(x, y)) {
491 mWindow.closePanel(mFeatureId);
492 return true;
493 }
494 }
495 }
496
497 if (!SWEEP_OPEN_MENU) {
498 return false;
499 }
500
501 if (mFeatureId >= 0) {
502 if (action == MotionEvent.ACTION_DOWN) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800503 Log.i(mLogTag, "Watchiing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800504 mWatchingForMenu = true;
505 mDownY = (int) event.getY();
506 return false;
507 }
508
509 if (!mWatchingForMenu) {
510 return false;
511 }
512
513 int y = (int)event.getY();
514 if (action == MotionEvent.ACTION_MOVE) {
515 if (y > (mDownY+30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800516 Log.i(mLogTag, "Closing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800517 mWindow.closePanel(mFeatureId);
518 mWatchingForMenu = false;
519 return true;
520 }
521 } else if (action == MotionEvent.ACTION_UP) {
522 mWatchingForMenu = false;
523 }
524
525 return false;
526 }
527
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800528 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
Wale Ogunwale8804af22015-11-17 09:18:15 -0800529 // + " (in " + getHeight() + ")");
530
531 if (action == MotionEvent.ACTION_DOWN) {
532 int y = (int)event.getY();
533 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800534 Log.i(mLogTag, "Watching!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800535 mWatchingForMenu = true;
536 }
537 return false;
538 }
539
540 if (!mWatchingForMenu) {
541 return false;
542 }
543
544 int y = (int)event.getY();
545 if (action == MotionEvent.ACTION_MOVE) {
546 if (y < (getHeight()-30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800547 Log.i(mLogTag, "Opening!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800548 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
549 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
550 mWatchingForMenu = false;
551 return true;
552 }
553 } else if (action == MotionEvent.ACTION_UP) {
554 mWatchingForMenu = false;
555 }
556
557 return false;
558 }
559
560 @Override
561 public void sendAccessibilityEvent(int eventType) {
562 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
563 return;
564 }
565
566 // if we are showing a feature that should be announced and one child
567 // make this child the event source since this is the feature itself
568 // otherwise the callback will take over and announce its client
569 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
570 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
571 mFeatureId == Window.FEATURE_PROGRESS ||
572 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
573 && getChildCount() == 1) {
574 getChildAt(0).sendAccessibilityEvent(eventType);
575 } else {
576 super.sendAccessibilityEvent(eventType);
577 }
578 }
579
580 @Override
581 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
582 final Window.Callback cb = mWindow.getCallback();
583 if (cb != null && !mWindow.isDestroyed()) {
584 if (cb.dispatchPopulateAccessibilityEvent(event)) {
585 return true;
586 }
587 }
588 return super.dispatchPopulateAccessibilityEventInternal(event);
589 }
590
591 @Override
592 protected boolean setFrame(int l, int t, int r, int b) {
593 boolean changed = super.setFrame(l, t, r, b);
594 if (changed) {
595 final Rect drawingBounds = mDrawingBounds;
596 getDrawingRect(drawingBounds);
597
598 Drawable fg = getForeground();
599 if (fg != null) {
600 final Rect frameOffsets = mFrameOffsets;
601 drawingBounds.left += frameOffsets.left;
602 drawingBounds.top += frameOffsets.top;
603 drawingBounds.right -= frameOffsets.right;
604 drawingBounds.bottom -= frameOffsets.bottom;
605 fg.setBounds(drawingBounds);
606 final Rect framePadding = mFramePadding;
607 drawingBounds.left += framePadding.left - frameOffsets.left;
608 drawingBounds.top += framePadding.top - frameOffsets.top;
609 drawingBounds.right -= framePadding.right - frameOffsets.right;
610 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
611 }
612
613 Drawable bg = getBackground();
614 if (bg != null) {
615 bg.setBounds(drawingBounds);
616 }
617
618 if (SWEEP_OPEN_MENU) {
619 if (mMenuBackground == null && mFeatureId < 0
620 && mWindow.getAttributes().height
621 == WindowManager.LayoutParams.MATCH_PARENT) {
622 mMenuBackground = getContext().getDrawable(
623 R.drawable.menu_background);
624 }
625 if (mMenuBackground != null) {
626 mMenuBackground.setBounds(drawingBounds.left,
627 drawingBounds.bottom-6, drawingBounds.right,
628 drawingBounds.bottom+20);
629 }
630 }
631 }
632 return changed;
633 }
634
635 @Override
636 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
637 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800638 final boolean isPortrait =
639 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800640
641 final int widthMode = getMode(widthMeasureSpec);
642 final int heightMode = getMode(heightMeasureSpec);
643
644 boolean fixedWidth = false;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800645 mApplyFloatingHorizontalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800646 if (widthMode == AT_MOST) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800647 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800648 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
649 final int w;
650 if (tvw.type == TypedValue.TYPE_DIMENSION) {
651 w = (int) tvw.getDimension(metrics);
652 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
653 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
654 } else {
655 w = 0;
656 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800657 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
Jorim Jaggiffd04902016-02-23 19:20:59 -0500658 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800659 if (w > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800660 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
661 Math.min(w, widthSize), EXACTLY);
662 fixedWidth = true;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800663 } else {
664 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
Jorim Jaggiffd04902016-02-23 19:20:59 -0500665 widthSize - mFloatingInsets.left - mFloatingInsets.right,
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800666 AT_MOST);
667 mApplyFloatingHorizontalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800668 }
669 }
670 }
671
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800672 mApplyFloatingVerticalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800673 if (heightMode == AT_MOST) {
674 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
675 : mWindow.mFixedHeightMinor;
676 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
677 final int h;
678 if (tvh.type == TypedValue.TYPE_DIMENSION) {
679 h = (int) tvh.getDimension(metrics);
680 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
681 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
682 } else {
683 h = 0;
684 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800685 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800686 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800687 if (h > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800688 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
689 Math.min(h, heightSize), EXACTLY);
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800690 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800691 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
692 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
693 mApplyFloatingVerticalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800694 }
695 }
696 }
697
698 getOutsets(mOutsets);
699 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
700 int mode = MeasureSpec.getMode(heightMeasureSpec);
701 if (mode != MeasureSpec.UNSPECIFIED) {
702 int height = MeasureSpec.getSize(heightMeasureSpec);
703 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
704 height + mOutsets.top + mOutsets.bottom, mode);
705 }
706 }
707 if (mOutsets.left > 0 || mOutsets.right > 0) {
708 int mode = MeasureSpec.getMode(widthMeasureSpec);
709 if (mode != MeasureSpec.UNSPECIFIED) {
710 int width = MeasureSpec.getSize(widthMeasureSpec);
711 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
712 width + mOutsets.left + mOutsets.right, mode);
713 }
714 }
715
716 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
717
718 int width = getMeasuredWidth();
719 boolean measure = false;
720
721 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
722
723 if (!fixedWidth && widthMode == AT_MOST) {
724 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
725 if (tv.type != TypedValue.TYPE_NULL) {
726 final int min;
727 if (tv.type == TypedValue.TYPE_DIMENSION) {
728 min = (int)tv.getDimension(metrics);
729 } else if (tv.type == TypedValue.TYPE_FRACTION) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800730 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800731 } else {
732 min = 0;
733 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800734 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
735 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800736
737 if (width < min) {
738 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
739 measure = true;
740 }
741 }
742 }
743
744 // TODO: Support height?
745
746 if (measure) {
747 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
748 }
749 }
750
751 @Override
752 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
753 super.onLayout(changed, left, top, right, bottom);
754 getOutsets(mOutsets);
755 if (mOutsets.left > 0) {
756 offsetLeftAndRight(-mOutsets.left);
757 }
758 if (mOutsets.top > 0) {
759 offsetTopAndBottom(-mOutsets.top);
760 }
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800761 if (mApplyFloatingVerticalInsets) {
762 offsetTopAndBottom(mFloatingInsets.top);
763 }
764 if (mApplyFloatingHorizontalInsets) {
765 offsetLeftAndRight(mFloatingInsets.left);
766 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800767
768 // If the application changed its SystemUI metrics, we might also have to adapt
769 // our shadow elevation.
770 updateElevation();
771 mAllowUpdateElevation = true;
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700772
773 if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
774 getViewRootImpl().requestInvalidateRootRenderNode();
775 }
Wale Ogunwale8804af22015-11-17 09:18:15 -0800776 }
777
778 @Override
779 public void draw(Canvas canvas) {
780 super.draw(canvas);
781
782 if (mMenuBackground != null) {
783 mMenuBackground.draw(canvas);
784 }
785 }
786
787 @Override
788 public boolean showContextMenuForChild(View originalView) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700789 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800790 }
791
792 @Override
793 public boolean showContextMenuForChild(View originalView, float x, float y) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700794 return showContextMenuForChildInternal(originalView, x, y);
Alan Viverette021627e2015-11-25 14:22:00 -0500795 }
796
797 private boolean showContextMenuForChildInternal(View originalView,
Oren Blasberg23e282d2016-04-20 13:43:45 -0700798 float x, float y) {
Alan Viverette021627e2015-11-25 14:22:00 -0500799 // Only allow one context menu at a time.
800 if (mWindow.mContextMenuHelper != null) {
801 mWindow.mContextMenuHelper.dismiss();
802 mWindow.mContextMenuHelper = null;
803 }
804
805 // Reuse the context menu builder.
Alan Viverette77fb85e2015-12-14 11:42:44 -0500806 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800807 if (mWindow.mContextMenu == null) {
808 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
Alan Viverette77fb85e2015-12-14 11:42:44 -0500809 mWindow.mContextMenu.setCallback(callback);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800810 } else {
811 mWindow.mContextMenu.clearAll();
812 }
813
Alan Viverette021627e2015-11-25 14:22:00 -0500814 final MenuHelper helper;
Oren Blasberg23e282d2016-04-20 13:43:45 -0700815 final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
Alan Viverette021627e2015-11-25 14:22:00 -0500816 if (isPopup) {
817 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
818 } else {
819 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
Wale Ogunwale8804af22015-11-17 09:18:15 -0800820 }
Alan Viverette021627e2015-11-25 14:22:00 -0500821
Alan Viverette9084d222015-12-16 09:56:37 -0500822 if (helper != null) {
823 // If it's a dialog, the callback needs to handle showing
824 // sub-menus. Either way, the callback is required for propagating
825 // selection to Context.onContextMenuItemSelected().
826 callback.setShowDialogForSubmenu(!isPopup);
827 helper.setPresenterCallback(callback);
828 }
Alan Viverette021627e2015-11-25 14:22:00 -0500829
830 mWindow.mContextMenuHelper = helper;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800831 return helper != null;
832 }
833
834 @Override
835 public ActionMode startActionModeForChild(View originalView,
836 ActionMode.Callback callback) {
837 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
838 }
839
840 @Override
841 public ActionMode startActionModeForChild(
842 View child, ActionMode.Callback callback, int type) {
843 return startActionMode(child, callback, type);
844 }
845
846 @Override
847 public ActionMode startActionMode(ActionMode.Callback callback) {
848 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
849 }
850
851 @Override
852 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
853 return startActionMode(this, callback, type);
854 }
855
856 private ActionMode startActionMode(
857 View originatingView, ActionMode.Callback callback, int type) {
858 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
859 ActionMode mode = null;
860 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
861 try {
862 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
863 } catch (AbstractMethodError ame) {
864 // Older apps might not implement the typed version of this method.
865 if (type == ActionMode.TYPE_PRIMARY) {
866 try {
867 mode = mWindow.getCallback().onWindowStartingActionMode(
868 wrappedCallback);
869 } catch (AbstractMethodError ame2) {
870 // Older apps might not implement this callback method at all.
871 }
872 }
873 }
874 }
875 if (mode != null) {
876 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
877 cleanupPrimaryActionMode();
878 mPrimaryActionMode = mode;
879 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
880 if (mFloatingActionMode != null) {
881 mFloatingActionMode.finish();
882 }
883 mFloatingActionMode = mode;
884 }
885 } else {
886 mode = createActionMode(type, wrappedCallback, originatingView);
887 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
888 setHandledActionMode(mode);
889 } else {
890 mode = null;
891 }
892 }
893 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
894 try {
895 mWindow.getCallback().onActionModeStarted(mode);
896 } catch (AbstractMethodError ame) {
897 // Older apps might not implement this callback method.
898 }
899 }
900 return mode;
901 }
902
903 private void cleanupPrimaryActionMode() {
904 if (mPrimaryActionMode != null) {
905 mPrimaryActionMode.finish();
906 mPrimaryActionMode = null;
907 }
908 if (mPrimaryActionModeView != null) {
909 mPrimaryActionModeView.killMode();
910 }
911 }
912
913 private void cleanupFloatingActionModeViews() {
914 if (mFloatingToolbar != null) {
915 mFloatingToolbar.dismiss();
916 mFloatingToolbar = null;
917 }
918 if (mFloatingActionModeOriginatingView != null) {
919 if (mFloatingToolbarPreDrawListener != null) {
920 mFloatingActionModeOriginatingView.getViewTreeObserver()
921 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
922 mFloatingToolbarPreDrawListener = null;
923 }
924 mFloatingActionModeOriginatingView = null;
925 }
926 }
927
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800928 void startChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800929 mChanging = true;
930 }
931
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800932 void finishChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800933 mChanging = false;
934 drawableChanged();
935 }
936
937 public void setWindowBackground(Drawable drawable) {
938 if (getBackground() != drawable) {
939 setBackgroundDrawable(drawable);
940 if (drawable != null) {
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700941 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
942 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700943 } else {
944 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700945 getContext(), 0, mWindow.mBackgroundFallbackResource,
946 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700947 }
948 if (mResizingBackgroundDrawable != null) {
949 mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800950 } else {
951 mBackgroundPadding.setEmpty();
952 }
953 drawableChanged();
954 }
955 }
956
957 public void setWindowFrame(Drawable drawable) {
958 if (getForeground() != drawable) {
959 setForeground(drawable);
960 if (drawable != null) {
961 drawable.getPadding(mFramePadding);
962 } else {
963 mFramePadding.setEmpty();
964 }
965 drawableChanged();
966 }
967 }
968
969 @Override
970 public void onWindowSystemUiVisibilityChanged(int visible) {
971 updateColorViews(null /* insets */, true /* animate */);
972 }
973
974 @Override
975 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800976 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
977 mFloatingInsets.setEmpty();
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800978 if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800979 // For dialog windows we want to make sure they don't go over the status bar or nav bar.
980 // We consume the system insets and we will reuse them later during the measure phase.
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800981 // We allow the app to ignore this and handle insets itself by using
982 // FLAG_LAYOUT_IN_SCREEN.
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800983 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
984 mFloatingInsets.top = insets.getSystemWindowInsetTop();
985 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +0100986 insets = insets.inset(0, insets.getSystemWindowInsetTop(),
987 0, insets.getSystemWindowInsetBottom());
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800988 }
989 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
990 mFloatingInsets.left = insets.getSystemWindowInsetTop();
991 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +0100992 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
993 insets.getSystemWindowInsetRight(), 0);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800994 }
995 }
Wale Ogunwale8804af22015-11-17 09:18:15 -0800996 mFrameOffsets.set(insets.getSystemWindowInsets());
997 insets = updateColorViews(insets, true /* animate */);
998 insets = updateStatusGuard(insets);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800999 if (getForeground() != null) {
1000 drawableChanged();
1001 }
1002 return insets;
1003 }
1004
1005 @Override
1006 public boolean isTransitionGroup() {
1007 return false;
1008 }
1009
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001010 public static int getColorViewTopInset(int stableTop, int systemTop) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001011 return Math.min(stableTop, systemTop);
1012 }
1013
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001014 public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001015 return Math.min(stableBottom, systemBottom);
1016 }
1017
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001018 public static int getColorViewRightInset(int stableRight, int systemRight) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001019 return Math.min(stableRight, systemRight);
1020 }
1021
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001022 public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001023 return Math.min(stableLeft, systemLeft);
1024 }
1025
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001026 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001027 return bottomInset == 0 && rightInset > 0;
1028 }
1029
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001030 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001031 return bottomInset == 0 && leftInset > 0;
1032 }
1033
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001034 public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001035 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1036 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001037 }
1038
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001039 public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
1040 Rect contentInsets, Rect outRect) {
1041 final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
1042 final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
1043 final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
1044 final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1045 if (isNavBarToRightEdge(bottomInset, rightInset)) {
1046 outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1047 } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1048 outRect.set(0, 0, size, canvasHeight);
1049 } else {
1050 outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1051 }
1052 }
1053
Wale Ogunwale8804af22015-11-17 09:18:15 -08001054 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1055 WindowManager.LayoutParams attrs = mWindow.getAttributes();
1056 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1057
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001058 // IME is an exceptional floating window that requires color view.
1059 final boolean isImeWindow =
1060 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1061 if (!mWindow.mIsFloating || isImeWindow) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001062 boolean disallowAnimate = !isLaidOut();
1063 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1064 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1065 mLastWindowFlags = attrs.flags;
1066
1067 if (insets != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001068 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001069 insets.getSystemWindowInsetTop());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001070 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001071 insets.getSystemWindowInsetBottom());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001072 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001073 insets.getSystemWindowInsetRight());
Adrian Roos85d202b2016-06-02 16:27:47 -07001074 mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
1075 insets.getSystemWindowInsetLeft());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001076
1077 // Don't animate if the presence of stable insets has changed, because that
1078 // indicates that the window was either just added and received them for the
1079 // first time, or the window size or position has changed.
1080 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
1081 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1082 mLastHasTopStableInset = hasTopStableInset;
1083
1084 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
1085 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1086 mLastHasBottomStableInset = hasBottomStableInset;
1087
1088 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
1089 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1090 mLastHasRightStableInset = hasRightStableInset;
Adrian Roos85d202b2016-06-02 16:27:47 -07001091
1092 boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
1093 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1094 mLastHasLeftStableInset = hasLeftStableInset;
1095
Jorim Jaggie5638a62016-03-25 22:57:01 -07001096 mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001097 }
1098
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001099 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
Adrian Roos85d202b2016-06-02 16:27:47 -07001100 boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1101 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001102 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
Jason Monkea506c62017-09-01 12:40:06 -04001103 mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
1104 navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
Adrian Roos85d202b2016-06-02 16:27:47 -07001105 0 /* sideInset */, animate && !disallowAnimate, false /* force */);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001106
1107 boolean statusBarNeedsRightInset = navBarToRightEdge
1108 && mNavigationColorViewState.present;
Adrian Roos85d202b2016-06-02 16:27:47 -07001109 boolean statusBarNeedsLeftInset = navBarToLeftEdge
1110 && mNavigationColorViewState.present;
1111 int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1112 : statusBarNeedsLeftInset ? mLastLeftInset : 0;
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001113 updateColorViewInt(mStatusColorViewState, sysUiVisibility,
Jason Monkea506c62017-09-01 12:40:06 -04001114 calculateStatusBarColor(), 0, mLastTopInset,
Adrian Roos85d202b2016-06-02 16:27:47 -07001115 false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1116 animate && !disallowAnimate,
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001117 mForceWindowDrawsStatusBarBackground);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001118 }
1119
1120 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
1121 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
1122 // explicitly asked for it.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001123 boolean consumingNavBar =
1124 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1125 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
Jorim Jaggi0ffd49c2016-02-12 15:04:21 -08001126 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
Jorim Jaggie5638a62016-03-25 22:57:01 -07001127 || mLastShouldAlwaysConsumeNavBar;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001128
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001129 // If we didn't request fullscreen layout, but we still got it because of the
1130 // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
1131 boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1132 && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1133 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1134 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1135 && mForceWindowDrawsStatusBarBackground
1136 && mLastTopInset != 0;
1137
1138 int consumedTop = consumingStatusBar ? mLastTopInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001139 int consumedRight = consumingNavBar ? mLastRightInset : 0;
1140 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
Adrian Roos85d202b2016-06-02 16:27:47 -07001141 int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001142
1143 if (mContentRoot != null
1144 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1145 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001146 if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
Adrian Roos85d202b2016-06-02 16:27:47 -07001147 || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001148 lp.topMargin = consumedTop;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001149 lp.rightMargin = consumedRight;
1150 lp.bottomMargin = consumedBottom;
Adrian Roos85d202b2016-06-02 16:27:47 -07001151 lp.leftMargin = consumedLeft;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001152 mContentRoot.setLayoutParams(lp);
1153
1154 if (insets == null) {
1155 // The insets have changed, but we're not currently in the process
1156 // of dispatching them.
1157 requestApplyInsets();
1158 }
1159 }
1160 if (insets != null) {
Adrian Roosf7b74262017-11-22 14:21:01 +01001161 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001162 }
1163 }
1164
1165 if (insets != null) {
1166 insets = insets.consumeStableInsets();
1167 }
1168 return insets;
1169 }
1170
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001171 private int calculateStatusBarColor() {
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001172 return calculateStatusBarColor(mWindow.getAttributes().flags,
1173 mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
1174 }
1175
1176 public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
1177 int statusBarColor) {
1178 return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
1179 : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001180 : Color.BLACK;
1181 }
1182
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001183 private int getCurrentColor(ColorViewState state) {
1184 if (state.visible) {
1185 return state.color;
1186 } else {
1187 return 0;
1188 }
1189 }
1190
Wale Ogunwale8804af22015-11-17 09:18:15 -08001191 /**
1192 * Update a color view
1193 *
1194 * @param state the color view to update.
1195 * @param sysUiVis the current systemUiVisibility to apply.
1196 * @param color the current color to apply.
Jason Monkea506c62017-09-01 12:40:06 -04001197 * @param dividerColor the current divider color to apply.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001198 * @param size the current size in the non-parent-matching dimension.
1199 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1200 * horizontal edge,
Adrian Roos85d202b2016-06-02 16:27:47 -07001201 * @param sideMargin sideMargin for the color view.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001202 * @param animate if true, the change will be animated.
1203 */
1204 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
Jason Monkea506c62017-09-01 12:40:06 -04001205 int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
Adrian Roos85d202b2016-06-02 16:27:47 -07001206 boolean animate, boolean force) {
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001207 state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
1208 boolean show = state.attributes.isVisible(state.present, color,
1209 mWindow.getAttributes().flags, force);
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001210 boolean showView = show && !isResizing() && size > 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001211
1212 boolean visibilityChanged = false;
1213 View view = state.view;
1214
1215 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1216 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
Adrian Roos85d202b2016-06-02 16:27:47 -07001217 int resolvedGravity = verticalBar
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001218 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1219 : state.attributes.verticalGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001220
1221 if (view == null) {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001222 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001223 state.view = view = new View(mContext);
Jason Monkea506c62017-09-01 12:40:06 -04001224 setColor(view, color, dividerColor, verticalBar, seascape);
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001225 view.setTransitionName(state.attributes.transitionName);
1226 view.setId(state.attributes.id);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001227 visibilityChanged = true;
1228 view.setVisibility(INVISIBLE);
1229 state.targetVisibility = VISIBLE;
1230
1231 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1232 resolvedGravity);
Adrian Roos85d202b2016-06-02 16:27:47 -07001233 if (seascape) {
1234 lp.leftMargin = sideMargin;
1235 } else {
1236 lp.rightMargin = sideMargin;
1237 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001238 addView(view, lp);
1239 updateColorViewTranslations();
1240 }
1241 } else {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001242 int vis = showView ? VISIBLE : INVISIBLE;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001243 visibilityChanged = state.targetVisibility != vis;
1244 state.targetVisibility = vis;
1245 LayoutParams lp = (LayoutParams) view.getLayoutParams();
Adrian Roos85d202b2016-06-02 16:27:47 -07001246 int rightMargin = seascape ? 0 : sideMargin;
1247 int leftMargin = seascape ? sideMargin : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001248 if (lp.height != resolvedHeight || lp.width != resolvedWidth
Adrian Roos85d202b2016-06-02 16:27:47 -07001249 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1250 || lp.leftMargin != leftMargin) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001251 lp.height = resolvedHeight;
1252 lp.width = resolvedWidth;
1253 lp.gravity = resolvedGravity;
1254 lp.rightMargin = rightMargin;
Adrian Roos85d202b2016-06-02 16:27:47 -07001255 lp.leftMargin = leftMargin;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001256 view.setLayoutParams(lp);
1257 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001258 if (showView) {
Jason Monkea506c62017-09-01 12:40:06 -04001259 setColor(view, color, dividerColor, verticalBar, seascape);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001260 }
1261 }
1262 if (visibilityChanged) {
1263 view.animate().cancel();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001264 if (animate && !isResizing()) {
1265 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001266 if (view.getVisibility() != VISIBLE) {
1267 view.setVisibility(VISIBLE);
1268 view.setAlpha(0.0f);
1269 }
1270 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1271 setDuration(mBarEnterExitDuration);
1272 } else {
1273 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1274 .setDuration(mBarEnterExitDuration)
1275 .withEndAction(new Runnable() {
1276 @Override
1277 public void run() {
1278 state.view.setAlpha(1.0f);
1279 state.view.setVisibility(INVISIBLE);
1280 }
1281 });
1282 }
1283 } else {
1284 view.setAlpha(1.0f);
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001285 view.setVisibility(showView ? VISIBLE : INVISIBLE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001286 }
1287 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001288 state.visible = show;
1289 state.color = color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001290 }
1291
Jason Monkea506c62017-09-01 12:40:06 -04001292 private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1293 boolean seascape) {
1294 if (dividerColor != 0) {
1295 final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1296 if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1297 final int size = Math.round(
1298 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1299 v.getContext().getResources().getDisplayMetrics()));
1300 // Use an inset to make the divider line on the side that faces the app.
1301 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1302 verticalBar && !seascape ? size : 0,
1303 !verticalBar ? size : 0,
1304 verticalBar && seascape ? size : 0, 0);
1305 v.setBackground(new LayerDrawable(new Drawable[] {
1306 new ColorDrawable(dividerColor), d }));
1307 v.setTag(new Pair<>(verticalBar, seascape));
1308 } else {
1309 final LayerDrawable d = (LayerDrawable) v.getBackground();
1310 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1311 ((ColorDrawable) inset.getDrawable()).setColor(color);
1312 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1313 }
1314 } else {
1315 v.setTag(null);
1316 v.setBackgroundColor(color);
1317 }
1318 }
1319
Wale Ogunwale8804af22015-11-17 09:18:15 -08001320 private void updateColorViewTranslations() {
1321 // Put the color views back in place when they get moved off the screen
1322 // due to the the ViewRootImpl panning.
1323 int rootScrollY = mRootScrollY;
1324 if (mStatusColorViewState.view != null) {
1325 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1326 }
1327 if (mNavigationColorViewState.view != null) {
1328 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1329 }
1330 }
1331
1332 private WindowInsets updateStatusGuard(WindowInsets insets) {
1333 boolean showStatusGuard = false;
1334 // Show the status guard when the non-overlay contextual action bar is showing
1335 if (mPrimaryActionModeView != null) {
1336 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1337 // Insets are magic!
1338 final MarginLayoutParams mlp = (MarginLayoutParams)
1339 mPrimaryActionModeView.getLayoutParams();
1340 boolean mlpChanged = false;
1341 if (mPrimaryActionModeView.isShown()) {
1342 if (mTempRect == null) {
1343 mTempRect = new Rect();
1344 }
1345 final Rect rect = mTempRect;
1346
1347 // If the parent doesn't consume the insets, manually
1348 // apply the default system window insets.
1349 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1350 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1351 if (mlp.topMargin != newMargin) {
1352 mlpChanged = true;
1353 mlp.topMargin = insets.getSystemWindowInsetTop();
1354
1355 if (mStatusGuard == null) {
1356 mStatusGuard = new View(mContext);
1357 mStatusGuard.setBackgroundColor(mContext.getColor(
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001358 R.color.decor_view_status_guard));
Wale Ogunwale8804af22015-11-17 09:18:15 -08001359 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1360 new LayoutParams(LayoutParams.MATCH_PARENT,
1361 mlp.topMargin, Gravity.START | Gravity.TOP));
1362 } else {
1363 final LayoutParams lp = (LayoutParams)
1364 mStatusGuard.getLayoutParams();
1365 if (lp.height != mlp.topMargin) {
1366 lp.height = mlp.topMargin;
1367 mStatusGuard.setLayoutParams(lp);
1368 }
1369 }
1370 }
1371
1372 // The action mode's theme may differ from the app, so
1373 // always show the status guard above it if we have one.
1374 showStatusGuard = mStatusGuard != null;
1375
1376 // We only need to consume the insets if the action
1377 // mode is overlaid on the app content (e.g. it's
1378 // sitting in a FrameLayout, see
1379 // screen_simple_overlay_action_mode.xml).
1380 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1381 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
Adrian Roosf7b74262017-11-22 14:21:01 +01001382 if (nonOverlay && showStatusGuard) {
1383 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1384 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001385 } else {
1386 // reset top margin
1387 if (mlp.topMargin != 0) {
1388 mlpChanged = true;
1389 mlp.topMargin = 0;
1390 }
1391 }
1392 if (mlpChanged) {
1393 mPrimaryActionModeView.setLayoutParams(mlp);
1394 }
1395 }
1396 }
1397 if (mStatusGuard != null) {
1398 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1399 }
1400 return insets;
1401 }
1402
Winson Chung4d8681f2017-05-23 16:22:08 -07001403 /**
1404 * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1405 * an opaque shadow even if the window background is completely transparent. This only applies
1406 * to activities that are currently the task root.
1407 */
1408 public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1409 if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1410 return;
1411 }
1412
1413 if (isInPictureInPictureMode) {
1414 final Window.WindowControllerCallback callback =
1415 mWindow.getWindowControllerCallback();
1416 if (callback != null && callback.isTaskRoot()) {
1417 // Call super implementation directly as we don't want to save the PIP outline
1418 // provider to be restored
1419 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1420 }
1421 } else {
1422 // Restore the previous outline provider
1423 if (getOutlineProvider() != mLastOutlineProvider) {
1424 setOutlineProvider(mLastOutlineProvider);
1425 }
1426 }
1427 mIsInPictureInPictureMode = isInPictureInPictureMode;
1428 }
1429
1430 @Override
1431 public void setOutlineProvider(ViewOutlineProvider provider) {
1432 super.setOutlineProvider(provider);
1433
1434 // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1435 mLastOutlineProvider = provider;
1436 }
1437
Wale Ogunwale8804af22015-11-17 09:18:15 -08001438 private void drawableChanged() {
1439 if (mChanging) {
1440 return;
1441 }
1442
1443 setPadding(mFramePadding.left + mBackgroundPadding.left,
1444 mFramePadding.top + mBackgroundPadding.top,
1445 mFramePadding.right + mBackgroundPadding.right,
1446 mFramePadding.bottom + mBackgroundPadding.bottom);
1447 requestLayout();
1448 invalidate();
1449
1450 int opacity = PixelFormat.OPAQUE;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001451 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1452 if (winConfig.hasWindowShadow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001453 // If the window has a shadow, it must be translucent.
1454 opacity = PixelFormat.TRANSLUCENT;
1455 } else{
1456 // Note: If there is no background, we will assume opaque. The
1457 // common case seems to be that an application sets there to be
1458 // no background so it can draw everything itself. For that,
1459 // we would like to assume OPAQUE and let the app force it to
1460 // the slower TRANSLUCENT mode if that is really what it wants.
1461 Drawable bg = getBackground();
1462 Drawable fg = getForeground();
1463 if (bg != null) {
1464 if (fg == null) {
1465 opacity = bg.getOpacity();
1466 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1467 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1468 // If the frame padding is zero, then we can be opaque
1469 // if either the frame -or- the background is opaque.
1470 int fop = fg.getOpacity();
1471 int bop = bg.getOpacity();
1472 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001473 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001474 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1475 opacity = PixelFormat.OPAQUE;
1476 } else if (fop == PixelFormat.UNKNOWN) {
1477 opacity = bop;
1478 } else if (bop == PixelFormat.UNKNOWN) {
1479 opacity = fop;
1480 } else {
1481 opacity = Drawable.resolveOpacity(fop, bop);
1482 }
1483 } else {
1484 // For now we have to assume translucent if there is a
1485 // frame with padding... there is no way to tell if the
1486 // frame and background together will draw all pixels.
1487 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001488 Log.v(mLogTag, "Padding: " + mFramePadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001489 opacity = PixelFormat.TRANSLUCENT;
1490 }
1491 }
1492 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001493 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001494 }
1495
1496 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001497 Log.v(mLogTag, "Selected default opacity: " + opacity);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001498
1499 mDefaultOpacity = opacity;
1500 if (mFeatureId < 0) {
1501 mWindow.setDefaultWindowFormat(opacity);
1502 }
1503 }
1504
1505 @Override
1506 public void onWindowFocusChanged(boolean hasWindowFocus) {
1507 super.onWindowFocusChanged(hasWindowFocus);
1508
1509 // If the user is chording a menu shortcut, release the chord since
1510 // this window lost focus
1511 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1512 && mWindow.mPanelChordingKey != 0) {
1513 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1514 }
1515
1516 final Window.Callback cb = mWindow.getCallback();
1517 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1518 cb.onWindowFocusChanged(hasWindowFocus);
1519 }
1520
1521 if (mPrimaryActionMode != null) {
1522 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1523 }
1524 if (mFloatingActionMode != null) {
1525 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1526 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001527
1528 updateElevation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001529 }
1530
1531 @Override
1532 protected void onAttachedToWindow() {
1533 super.onAttachedToWindow();
1534
1535 final Window.Callback cb = mWindow.getCallback();
1536 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1537 cb.onAttachedToWindow();
1538 }
1539
1540 if (mFeatureId == -1) {
1541 /*
1542 * The main window has been attached, try to restore any panels
1543 * that may have been open before. This is called in cases where
1544 * an activity is being killed for configuration change and the
1545 * menu was open. When the activity is recreated, the menu
1546 * should be shown again.
1547 */
1548 mWindow.openPanelsAfterRestore();
1549 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001550
1551 if (!mWindowResizeCallbacksAdded) {
1552 // If there is no window callback installed there was no window set before. Set it now.
1553 // Note that our ViewRootImpl object will not change.
1554 getViewRootImpl().addWindowCallbacks(this);
1555 mWindowResizeCallbacksAdded = true;
1556 } else if (mBackdropFrameRenderer != null) {
1557 // We are resizing and this call happened due to a configuration change. Tell the
1558 // renderer about it.
1559 mBackdropFrameRenderer.onConfigurationChange();
1560 }
Andrii Kulian51c1b672017-04-07 18:39:32 -07001561 mWindow.onViewRootImplSet(getViewRootImpl());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001562 }
1563
1564 @Override
1565 protected void onDetachedFromWindow() {
1566 super.onDetachedFromWindow();
1567
1568 final Window.Callback cb = mWindow.getCallback();
1569 if (cb != null && mFeatureId < 0) {
1570 cb.onDetachedFromWindow();
1571 }
1572
1573 if (mWindow.mDecorContentParent != null) {
1574 mWindow.mDecorContentParent.dismissPopups();
1575 }
1576
1577 if (mPrimaryActionModePopup != null) {
1578 removeCallbacks(mShowPrimaryActionModePopup);
1579 if (mPrimaryActionModePopup.isShowing()) {
1580 mPrimaryActionModePopup.dismiss();
1581 }
1582 mPrimaryActionModePopup = null;
1583 }
1584 if (mFloatingToolbar != null) {
1585 mFloatingToolbar.dismiss();
1586 mFloatingToolbar = null;
1587 }
1588
1589 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1590 if (st != null && st.menu != null && mFeatureId < 0) {
1591 st.menu.close();
1592 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001593
Jorim Jaggib2005a02016-04-08 14:13:30 -07001594 releaseThreadedRenderer();
1595
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001596 if (mWindowResizeCallbacksAdded) {
1597 getViewRootImpl().removeWindowCallbacks(this);
1598 mWindowResizeCallbacksAdded = false;
1599 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001600 }
1601
1602 @Override
1603 public void onCloseSystemDialogs(String reason) {
1604 if (mFeatureId >= 0) {
1605 mWindow.closeAllPanels();
1606 }
1607 }
1608
1609 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1610 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1611 }
1612
1613 public InputQueue.Callback willYouTakeTheInputQueue() {
1614 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1615 }
1616
1617 public void setSurfaceType(int type) {
1618 mWindow.setType(type);
1619 }
1620
1621 public void setSurfaceFormat(int format) {
1622 mWindow.setFormat(format);
1623 }
1624
1625 public void setSurfaceKeepScreenOn(boolean keepOn) {
1626 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1627 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1628 }
1629
1630 @Override
1631 public void onRootViewScrollYChanged(int rootScrollY) {
1632 mRootScrollY = rootScrollY;
1633 updateColorViewTranslations();
1634 }
1635
1636 private ActionMode createActionMode(
1637 int type, ActionMode.Callback2 callback, View originatingView) {
1638 switch (type) {
1639 case ActionMode.TYPE_PRIMARY:
1640 default:
1641 return createStandaloneActionMode(callback);
1642 case ActionMode.TYPE_FLOATING:
1643 return createFloatingActionMode(originatingView, callback);
1644 }
1645 }
1646
1647 private void setHandledActionMode(ActionMode mode) {
1648 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1649 setHandledPrimaryActionMode(mode);
1650 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1651 setHandledFloatingActionMode(mode);
1652 }
1653 }
1654
1655 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1656 endOnGoingFadeAnimation();
1657 cleanupPrimaryActionMode();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001658 // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1659 // instance at all, or if there is one, but it is detached from window. The latter case
1660 // might happen when app is resized in multi-window mode and decor view is preserved
1661 // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1662 // app memory leaks because killMode() is called when the dismiss animation ends and from
1663 // cleanupPrimaryActionMode() invocation above.
1664 if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001665 if (mWindow.isFloating()) {
1666 // Use the action bar theme.
1667 final TypedValue outValue = new TypedValue();
1668 final Resources.Theme baseTheme = mContext.getTheme();
1669 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1670
1671 final Context actionBarContext;
1672 if (outValue.resourceId != 0) {
1673 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1674 actionBarTheme.setTo(baseTheme);
1675 actionBarTheme.applyStyle(outValue.resourceId, true);
1676
1677 actionBarContext = new ContextThemeWrapper(mContext, 0);
1678 actionBarContext.getTheme().setTo(actionBarTheme);
1679 } else {
1680 actionBarContext = mContext;
1681 }
1682
1683 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1684 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1685 R.attr.actionModePopupWindowStyle);
1686 mPrimaryActionModePopup.setWindowLayoutType(
1687 WindowManager.LayoutParams.TYPE_APPLICATION);
1688 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1689 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1690
1691 actionBarContext.getTheme().resolveAttribute(
1692 R.attr.actionBarSize, outValue, true);
1693 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1694 actionBarContext.getResources().getDisplayMetrics());
1695 mPrimaryActionModeView.setContentHeight(height);
1696 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1697 mShowPrimaryActionModePopup = new Runnable() {
1698 public void run() {
1699 mPrimaryActionModePopup.showAtLocation(
1700 mPrimaryActionModeView.getApplicationWindowToken(),
1701 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1702 endOnGoingFadeAnimation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001703
Chris Banese65b3fb2016-06-01 11:39:54 +01001704 if (shouldAnimatePrimaryActionModeView()) {
1705 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1706 0f, 1f);
1707 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1708 @Override
1709 public void onAnimationStart(Animator animation) {
1710 mPrimaryActionModeView.setVisibility(VISIBLE);
1711 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001712
Chris Banese65b3fb2016-06-01 11:39:54 +01001713 @Override
1714 public void onAnimationEnd(Animator animation) {
1715 mPrimaryActionModeView.setAlpha(1f);
1716 mFadeAnim = null;
1717 }
1718 });
1719 mFadeAnim.start();
1720 } else {
1721 mPrimaryActionModeView.setAlpha(1f);
1722 mPrimaryActionModeView.setVisibility(VISIBLE);
1723 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001724 }
1725 };
1726 } else {
Alan Viverette51efddb2017-04-05 10:00:01 -04001727 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001728 if (stub != null) {
1729 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001730 mPrimaryActionModePopup = null;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001731 }
1732 }
1733 }
1734 if (mPrimaryActionModeView != null) {
1735 mPrimaryActionModeView.killMode();
1736 ActionMode mode = new StandaloneActionMode(
1737 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1738 callback, mPrimaryActionModePopup == null);
1739 return mode;
1740 }
1741 return null;
1742 }
1743
1744 private void endOnGoingFadeAnimation() {
1745 if (mFadeAnim != null) {
1746 mFadeAnim.end();
1747 }
1748 }
1749
1750 private void setHandledPrimaryActionMode(ActionMode mode) {
1751 endOnGoingFadeAnimation();
1752 mPrimaryActionMode = mode;
1753 mPrimaryActionMode.invalidate();
1754 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1755 if (mPrimaryActionModePopup != null) {
1756 post(mShowPrimaryActionModePopup);
1757 } else {
Chris Banese65b3fb2016-06-01 11:39:54 +01001758 if (shouldAnimatePrimaryActionModeView()) {
1759 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1760 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1761 @Override
1762 public void onAnimationStart(Animator animation) {
1763 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1764 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001765
Chris Banese65b3fb2016-06-01 11:39:54 +01001766 @Override
1767 public void onAnimationEnd(Animator animation) {
1768 mPrimaryActionModeView.setAlpha(1f);
1769 mFadeAnim = null;
1770 }
1771 });
1772 mFadeAnim.start();
1773 } else {
1774 mPrimaryActionModeView.setAlpha(1f);
1775 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1776 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001777 }
1778 mPrimaryActionModeView.sendAccessibilityEvent(
1779 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1780 }
1781
Chris Banese65b3fb2016-06-01 11:39:54 +01001782 boolean shouldAnimatePrimaryActionModeView() {
1783 // We only to animate the action mode in if the decor has already been laid out.
1784 // If it hasn't been laid out, it hasn't been drawn to screen yet.
1785 return isLaidOut();
1786 }
1787
Wale Ogunwale8804af22015-11-17 09:18:15 -08001788 private ActionMode createFloatingActionMode(
1789 View originatingView, ActionMode.Callback2 callback) {
1790 if (mFloatingActionMode != null) {
1791 mFloatingActionMode.finish();
1792 }
1793 cleanupFloatingActionModeViews();
Tarandeep Singhc9c83a92017-08-29 14:39:22 -07001794 mFloatingToolbar = new FloatingToolbar(mWindow);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001795 final FloatingActionMode mode =
Abodunrinwa Toki17293cc2017-05-22 14:16:04 +01001796 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001797 mFloatingActionModeOriginatingView = originatingView;
1798 mFloatingToolbarPreDrawListener =
1799 new ViewTreeObserver.OnPreDrawListener() {
1800 @Override
1801 public boolean onPreDraw() {
1802 mode.updateViewLocationInWindow();
1803 return true;
1804 }
1805 };
1806 return mode;
1807 }
1808
1809 private void setHandledFloatingActionMode(ActionMode mode) {
1810 mFloatingActionMode = mode;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001811 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1812 mFloatingActionModeOriginatingView.getViewTreeObserver()
1813 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1814 }
1815
1816 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001817 * Informs the decor if the caption is attached and visible.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001818 * @param attachedAndVisible true when the decor is visible.
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001819 * Note that this will even be called if there is no caption.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001820 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001821 void enableCaption(boolean attachedAndVisible) {
1822 if (mHasCaption != attachedAndVisible) {
1823 mHasCaption = attachedAndVisible;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001824 if (getForeground() != null) {
1825 drawableChanged();
1826 }
1827 }
1828 }
1829
Wale Ogunwale8804af22015-11-17 09:18:15 -08001830 void setWindow(PhoneWindow phoneWindow) {
1831 mWindow = phoneWindow;
1832 Context context = getContext();
1833 if (context instanceof DecorContext) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001834 DecorContext decorContext = (DecorContext) context;
1835 decorContext.setPhoneWindow(mWindow);
1836 }
1837 }
1838
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08001839 @Override
chaviwa213d302018-04-23 13:34:36 -07001840 public Resources getResources() {
1841 // Make sure the Resources object is propogated from the Context since it can be updated in
1842 // the Context object.
1843 return getContext().getResources();
1844 }
1845
1846 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08001847 protected void onConfigurationChanged(Configuration newConfig) {
1848 super.onConfigurationChanged(newConfig);
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001849
1850 final boolean displayWindowDecor =
1851 newConfig.windowConfiguration.hasWindowDecorCaption();
1852 if (mDecorCaptionView == null && displayWindowDecor) {
1853 // Configuration now requires a caption.
1854 final LayoutInflater inflater = mWindow.getLayoutInflater();
1855 mDecorCaptionView = createDecorCaptionView(inflater);
1856 if (mDecorCaptionView != null) {
1857 if (mDecorCaptionView.getParent() == null) {
1858 addView(mDecorCaptionView, 0,
1859 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001860 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001861 removeView(mContentRoot);
1862 mDecorCaptionView.addView(mContentRoot,
1863 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001864 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001865 } else if (mDecorCaptionView != null) {
1866 // We might have to change the kind of surface before we do anything else.
1867 mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
1868 enableCaption(displayWindowDecor);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001869 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001870
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001871 updateAvailableWidth();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001872 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001873 }
1874
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001875 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001876 if (mBackdropFrameRenderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001877 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001878 mBackdropFrameRenderer.onResourcesLoaded(
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001879 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001880 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1881 getCurrentColor(mNavigationColorViewState));
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001882 }
1883
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001884 mDecorCaptionView = createDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001885 final View root = inflater.inflate(layoutResource, null);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001886 if (mDecorCaptionView != null) {
1887 if (mDecorCaptionView.getParent() == null) {
1888 addView(mDecorCaptionView,
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001889 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1890 }
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001891 mDecorCaptionView.addView(root,
Filip Gruszczynski63250652015-11-18 14:43:01 -08001892 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001893 } else {
Jorim Jaggi0a13bfd2016-02-04 18:34:50 -08001894
1895 // Put it below the color views.
1896 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001897 }
1898 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001899 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001900 }
1901
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001902 private void loadBackgroundDrawablesIfNeeded() {
1903 if (mResizingBackgroundDrawable == null) {
Winson Chung1af8eda2016-02-05 17:55:56 +00001904 mResizingBackgroundDrawable = getResizingBackgroundDrawable(getContext(),
Jorim Jaggi8e879f12016-05-25 16:41:49 -07001905 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource,
1906 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Winson Chung1af8eda2016-02-05 17:55:56 +00001907 if (mResizingBackgroundDrawable == null) {
1908 // We shouldn't really get here as the background fallback should be always
1909 // available since it is defaulted by the system.
1910 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
1911 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001912 }
1913 if (mCaptionBackgroundDrawable == null) {
1914 mCaptionBackgroundDrawable = getContext().getDrawable(
1915 R.drawable.decor_caption_title_focused);
1916 }
Chong Zhang0df63d52016-02-24 15:39:53 -08001917 if (mResizingBackgroundDrawable != null) {
1918 mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
1919 mResizingBackgroundDrawable.setCallback(null);
1920 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001921 }
1922
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001923 // Free floating overlapping windows require a caption.
1924 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001925 DecorCaptionView decorCaptionView = null;
1926 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001927 View view = getChildAt(i);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001928 if (view instanceof DecorCaptionView) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001929 // The decor was most likely saved from a relaunch - so reuse it.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001930 decorCaptionView = (DecorCaptionView) view;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001931 removeViewAt(i);
1932 }
1933 }
1934 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001935 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Chong Zhangfea963e2016-08-15 17:14:16 -07001936 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001937 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001938 // Only a non floating application window on one of the allowed workspaces can get a caption
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001939 if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001940 // Dependent on the brightness of the used title we either use the
1941 // dark or the light button frame.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001942 if (decorCaptionView == null) {
1943 decorCaptionView = inflateDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001944 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001945 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001946 } else {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001947 decorCaptionView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001948 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001949
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001950 // Tell the decor if it has a visible caption.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001951 enableCaption(decorCaptionView != null);
1952 return decorCaptionView;
1953 }
1954
1955 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1956 final Context context = getContext();
1957 // We make a copy of the inflater, so it has the right context associated with it.
1958 inflater = inflater.from(context);
1959 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1960 null);
1961 setDecorCaptionShade(context, view);
1962 return view;
1963 }
1964
1965 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1966 final int shade = mWindow.getDecorCaptionShade();
1967 switch (shade) {
1968 case DECOR_CAPTION_SHADE_LIGHT:
1969 setLightDecorCaptionShade(view);
1970 break;
1971 case DECOR_CAPTION_SHADE_DARK:
1972 setDarkDecorCaptionShade(view);
1973 break;
1974 default: {
1975 TypedValue value = new TypedValue();
1976 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1977 // We invert the shade depending on brightness of the theme. Dark shade for light
1978 // theme and vice versa. Thanks to this the buttons should be visible on the
1979 // background.
1980 if (Color.luminance(value.data) < 0.5) {
1981 setLightDecorCaptionShade(view);
1982 } else {
1983 setDarkDecorCaptionShade(view);
1984 }
1985 break;
1986 }
1987 }
1988 }
1989
1990 void updateDecorCaptionShade() {
1991 if (mDecorCaptionView != null) {
1992 setDecorCaptionShade(getContext(), mDecorCaptionView);
1993 }
1994 }
1995
1996 private void setLightDecorCaptionShade(DecorCaptionView view) {
1997 view.findViewById(R.id.maximize_window).setBackgroundResource(
1998 R.drawable.decor_maximize_button_light);
1999 view.findViewById(R.id.close_window).setBackgroundResource(
2000 R.drawable.decor_close_button_light);
2001 }
2002
2003 private void setDarkDecorCaptionShade(DecorCaptionView view) {
2004 view.findViewById(R.id.maximize_window).setBackgroundResource(
2005 R.drawable.decor_maximize_button_dark);
2006 view.findViewById(R.id.close_window).setBackgroundResource(
2007 R.drawable.decor_close_button_dark);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002008 }
2009
2010 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002011 * Returns the color used to fill areas the app has not rendered content to yet when the
2012 * user is resizing the window of an activity in multi-window mode.
2013 */
Winson Chung1af8eda2016-02-05 17:55:56 +00002014 public static Drawable getResizingBackgroundDrawable(Context context, int backgroundRes,
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002015 int backgroundFallbackRes, boolean windowTranslucent) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002016 if (backgroundRes != 0) {
2017 final Drawable drawable = context.getDrawable(backgroundRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002018 if (drawable != null) {
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002019 return enforceNonTranslucentBackground(drawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002020 }
2021 }
2022
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002023 if (backgroundFallbackRes != 0) {
2024 final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002025 if (fallbackDrawable != null) {
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002026 return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002027 }
2028 }
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002029 return new ColorDrawable(Color.BLACK);
2030 }
2031
2032 /**
2033 * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2034 * window is not translucent.
2035 */
2036 private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2037 boolean windowTranslucent) {
2038 if (!windowTranslucent && drawable instanceof ColorDrawable) {
2039 ColorDrawable colorDrawable = (ColorDrawable) drawable;
2040 int color = colorDrawable.getColor();
2041 if (Color.alpha(color) != 255) {
2042 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2043 .mutate();
2044 copy.setColor(
2045 Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2046 return copy;
2047 }
2048 }
2049 return drawable;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002050 }
2051
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002052 void clearContentView() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002053 if (mDecorCaptionView != null) {
Filip Gruszczynski63250652015-11-18 14:43:01 -08002054 mDecorCaptionView.removeContentView();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002055 } else {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002056 // This window doesn't have caption, so we need to remove everything except our views
2057 // we might have added.
2058 for (int i = getChildCount() - 1; i >= 0; i--) {
2059 View v = getChildAt(i);
2060 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
Yohei Yukawa8f162c62018-01-10 13:18:09 -08002061 && v != mStatusGuard) {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002062 removeViewAt(i);
2063 }
2064 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002065 }
2066 }
2067
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002068 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002069 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2070 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002071 if (mBackdropFrameRenderer != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002072 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002073 }
2074 }
2075
2076 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002077 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002078 Rect stableInsets, int resizeMode) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002079 if (mWindow.isDestroyed()) {
2080 // If the owner's window is gone, we should not be able to come here anymore.
2081 releaseThreadedRenderer();
2082 return;
2083 }
2084 if (mBackdropFrameRenderer != null) {
2085 return;
2086 }
Stan Iliev45faba52016-06-28 13:33:15 -04002087 final ThreadedRenderer renderer = getThreadedRenderer();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002088 if (renderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002089 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002090 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002091 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002092 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2093 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002094 stableInsets, resizeMode);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002095
2096 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2097 // If we want to get the shadow shown while resizing, we would need to elevate a new
2098 // element which owns the caption and has the elevation.
2099 updateElevation();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002100
2101 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002102 }
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002103 mResizeMode = resizeMode;
2104 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002105 }
2106
2107 @Override
2108 public void onWindowDragResizeEnd() {
2109 releaseThreadedRenderer();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002110 updateColorViews(null /* insets */, false);
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002111 mResizeMode = RESIZE_MODE_INVALID;
2112 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002113 }
2114
2115 @Override
2116 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2117 if (mBackdropFrameRenderer == null) {
2118 return false;
2119 }
2120 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2121 }
2122
2123 @Override
2124 public void onRequestDraw(boolean reportNextDraw) {
2125 if (mBackdropFrameRenderer != null) {
2126 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2127 } else if (reportNextDraw) {
2128 // If render thread is gone, just report immediately.
2129 if (isAttachedToWindow()) {
2130 getViewRootImpl().reportDrawFinish();
2131 }
2132 }
2133 }
2134
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002135 @Override
2136 public void onPostDraw(DisplayListCanvas canvas) {
2137 drawResizingShadowIfNeeded(canvas);
2138 }
2139
2140 private void initResizingPaints() {
2141 final int startColor = mContext.getResources().getColor(
2142 R.color.resize_shadow_start_color, null);
2143 final int endColor = mContext.getResources().getColor(
2144 R.color.resize_shadow_end_color, null);
2145 final int middleColor = (startColor + endColor) / 2;
2146 mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2147 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2148 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2149 mVerticalResizeShadowPaint.setShader(new LinearGradient(
2150 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2151 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2152 }
2153
2154 private void drawResizingShadowIfNeeded(DisplayListCanvas canvas) {
2155 if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2156 || mWindow.isTranslucent()
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002157 || mWindow.isShowingWallpaper()) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002158 return;
2159 }
2160 canvas.save();
2161 canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2162 canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2163 canvas.restore();
2164 canvas.save();
2165 canvas.translate(getWidth() - mFrameOffsets.right, 0);
2166 canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2167 canvas.restore();
2168 }
2169
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002170 /** Release the renderer thread which is usually done when the user stops resizing. */
2171 private void releaseThreadedRenderer() {
Chong Zhangd3fd96c2016-02-08 18:25:24 -08002172 if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2173 mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2174 mLastBackgroundDrawableCb = null;
2175 }
2176
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002177 if (mBackdropFrameRenderer != null) {
2178 mBackdropFrameRenderer.releaseRenderer();
2179 mBackdropFrameRenderer = null;
2180 // Bring the shadow back.
2181 updateElevation();
2182 }
2183 }
2184
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002185 private boolean isResizing() {
2186 return mBackdropFrameRenderer != null;
2187 }
2188
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002189 /**
2190 * The elevation gets set for the first time and the framework needs to be informed that
2191 * the surface layer gets created with the shadow size in mind.
2192 */
2193 private void initializeElevation() {
2194 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2195 mAllowUpdateElevation = false;
2196 updateElevation();
2197 }
2198
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002199 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002200 float elevation = 0;
2201 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2202 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2203 // since the shadow is bound to the content size and not the target size.
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002204 final int windowingMode =
2205 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2206 if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002207 elevation = hasWindowFocus() ?
2208 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
Jaewan Kim880eff62016-04-27 10:36:35 +09002209 // Add a maximum shadow height value to the top level view.
2210 // Note that pinned stack doesn't have focus
2211 // so maximum shadow height adjustment isn't needed.
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002212 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
Robert Carr232b5f82017-04-17 15:11:35 -07002213 if (!mAllowUpdateElevation) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002214 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2215 }
2216 // Convert the DP elevation into physical pixels.
2217 elevation = dipToPx(elevation);
2218 mElevationAdjustedForStack = true;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002219 } else if (windowingMode == WINDOWING_MODE_PINNED) {
Robert Carr32bcb102018-01-29 15:03:23 -08002220 elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
Robert Carr232b5f82017-04-17 15:11:35 -07002221 mElevationAdjustedForStack = true;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002222 } else {
2223 mElevationAdjustedForStack = false;
2224 }
2225
2226 // Don't change the elevation if we didn't previously adjust it for the stack it was in
2227 // or it didn't change.
2228 if ((wasAdjustedForStack || mElevationAdjustedForStack)
2229 && getElevation() != elevation) {
2230 mWindow.setElevation(elevation);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002231 }
2232 }
2233
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002234 boolean isShowingCaption() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002235 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002236 }
2237
2238 int getCaptionHeight() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002239 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002240 }
2241
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002242 /**
2243 * Converts a DIP measure into physical pixels.
2244 * @param dip The dip value.
2245 * @return Returns the number of pixels.
2246 */
2247 private float dipToPx(float dip) {
2248 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2249 getResources().getDisplayMetrics());
2250 }
2251
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002252 /**
2253 * Provide an override of the caption background drawable.
2254 */
2255 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2256 mUserCaptionBackgroundDrawable = drawable;
2257 if (mBackdropFrameRenderer != null) {
2258 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2259 }
2260 }
2261
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002262 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2263 if (params == null) {
2264 return "";
2265 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002266 final String[] split = params.getTitle().toString().split("\\.");
2267 if (split.length > 0) {
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002268 return split[split.length - 1];
2269 } else {
2270 return "";
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002271 }
2272 }
2273
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002274 void updateLogTag(WindowManager.LayoutParams params) {
2275 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2276 }
2277
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002278 private void updateAvailableWidth() {
2279 Resources res = getResources();
2280 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2281 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2282 }
2283
Clara Bayarri75e09792015-07-29 16:20:40 +01002284 /**
2285 * @hide
2286 */
2287 @Override
Clara Bayarrifcd7e802016-03-10 12:58:18 +00002288 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
Clara Bayarri75e09792015-07-29 16:20:40 +01002289 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
Michael Wright936f27c2017-04-11 23:23:42 +01002290 final Menu menu = st != null ? st.menu : null;
2291 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2292 mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
Clara Bayarri75e09792015-07-29 16:20:40 +01002293 }
2294 }
2295
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002296 @Override
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -08002297 public void dispatchPointerCaptureChanged(boolean hasCapture) {
2298 super.dispatchPointerCaptureChanged(hasCapture);
2299 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2300 mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2301 }
2302 }
2303
2304 @Override
Phil Weaverf00cd142017-03-03 13:44:00 -08002305 public int getAccessibilityViewId() {
2306 return AccessibilityNodeInfo.ROOT_ITEM_ID;
2307 }
2308
2309 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002310 public String toString() {
2311 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2312 + getTitleSuffix(mWindow.getAttributes()) + "]";
2313 }
2314
Wale Ogunwale8804af22015-11-17 09:18:15 -08002315 private static class ColorViewState {
2316 View view = null;
2317 int targetVisibility = View.INVISIBLE;
2318 boolean present = false;
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002319 boolean visible;
2320 int color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002321
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002322 final ColorViewAttributes attributes;
2323
2324 ColorViewState(ColorViewAttributes attributes) {
2325 this.attributes = attributes;
2326 }
2327 }
2328
2329 public static class ColorViewAttributes {
2330
Wale Ogunwale8804af22015-11-17 09:18:15 -08002331 final int id;
2332 final int systemUiHideFlag;
2333 final int translucentFlag;
2334 final int verticalGravity;
2335 final int horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002336 final int seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002337 final String transitionName;
2338 final int hideWindowFlag;
2339
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002340 private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2341 int horizontalGravity, int seascapeGravity, String transitionName, int id,
2342 int hideWindowFlag) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08002343 this.id = id;
2344 this.systemUiHideFlag = systemUiHideFlag;
2345 this.translucentFlag = translucentFlag;
2346 this.verticalGravity = verticalGravity;
2347 this.horizontalGravity = horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002348 this.seascapeGravity = seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002349 this.transitionName = transitionName;
2350 this.hideWindowFlag = hideWindowFlag;
2351 }
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002352
2353 public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2354 return (sysUiVis & systemUiHideFlag) == 0
2355 && (windowFlags & hideWindowFlag) == 0
2356 && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2357 || force);
2358 }
2359
2360 public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2361 return present
2362 && (color & Color.BLACK) != 0
2363 && ((windowFlags & translucentFlag) == 0 || force);
2364 }
2365
2366 public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2367 final boolean present = isPresent(sysUiVis, windowFlags, force);
2368 return isVisible(present, color, windowFlags, force);
2369 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002370 }
2371
2372 /**
2373 * Clears out internal references when the action mode is destroyed.
2374 */
2375 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2376 private final ActionMode.Callback mWrapped;
2377
2378 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2379 mWrapped = wrapped;
2380 }
2381
2382 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2383 return mWrapped.onCreateActionMode(mode, menu);
2384 }
2385
2386 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2387 requestFitSystemWindows();
2388 return mWrapped.onPrepareActionMode(mode, menu);
2389 }
2390
2391 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2392 return mWrapped.onActionItemClicked(mode, item);
2393 }
2394
2395 public void onDestroyActionMode(ActionMode mode) {
2396 mWrapped.onDestroyActionMode(mode);
2397 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
Jorim Jaggi8f5701b2016-04-04 18:36:02 -07002398 >= M;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002399 final boolean isPrimary;
2400 final boolean isFloating;
2401 if (isMncApp) {
2402 isPrimary = mode == mPrimaryActionMode;
2403 isFloating = mode == mFloatingActionMode;
2404 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002405 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002406 + mode + " was not the current primary action mode! Expected "
2407 + mPrimaryActionMode);
2408 }
2409 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002410 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002411 + mode + " was not the current floating action mode! Expected "
2412 + mFloatingActionMode);
2413 }
2414 } else {
2415 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2416 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2417 }
2418 if (isPrimary) {
2419 if (mPrimaryActionModePopup != null) {
2420 removeCallbacks(mShowPrimaryActionModePopup);
2421 }
2422 if (mPrimaryActionModeView != null) {
2423 endOnGoingFadeAnimation();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002424 // Store action mode view reference, so we can access it safely when animation
2425 // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2426 // so no need to store reference to it in separate variable.
2427 final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002428 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2429 1f, 0f);
2430 mFadeAnim.addListener(new Animator.AnimatorListener() {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002431
Wale Ogunwale8804af22015-11-17 09:18:15 -08002432 @Override
2433 public void onAnimationStart(Animator animation) {
2434
2435 }
2436
2437 @Override
2438 public void onAnimationEnd(Animator animation) {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002439 // If mPrimaryActionModeView has changed - it means that we've
2440 // cleared the content while preserving decor view. We don't
2441 // want to change the state of new instances accidentally here.
2442 if (lastActionModeView == mPrimaryActionModeView) {
2443 lastActionModeView.setVisibility(GONE);
2444 if (mPrimaryActionModePopup != null) {
2445 mPrimaryActionModePopup.dismiss();
2446 }
2447 lastActionModeView.killMode();
2448 mFadeAnim = null;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002449 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002450 }
2451
2452 @Override
2453 public void onAnimationCancel(Animator animation) {
2454
2455 }
2456
2457 @Override
2458 public void onAnimationRepeat(Animator animation) {
2459
2460 }
2461 });
2462 mFadeAnim.start();
2463 }
2464
2465 mPrimaryActionMode = null;
2466 } else if (isFloating) {
2467 cleanupFloatingActionModeViews();
2468 mFloatingActionMode = null;
2469 }
2470 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2471 try {
2472 mWindow.getCallback().onActionModeFinished(mode);
2473 } catch (AbstractMethodError ame) {
2474 // Older apps might not implement this callback method.
2475 }
2476 }
2477 requestFitSystemWindows();
2478 }
2479
2480 @Override
2481 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2482 if (mWrapped instanceof ActionMode.Callback2) {
2483 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2484 } else {
2485 super.onGetContentRect(mode, view, outRect);
2486 }
2487 }
2488 }
2489}