blob: cdaf33f0352824050469c81949d2e39f15f61bab [file] [log] [blame]
Wale Ogunwale8804af22015-11-17 09:18:15 -08001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License
15 */
16
17package com.android.internal.policy;
18
Robert Carr32bcb102018-01-29 15:03:23 -080019import static android.app.WindowConfiguration.PINNED_WINDOWING_MODE_ELEVATION_IN_DIP;
Wale Ogunwale3382ab12017-07-27 08:55:03 -070020import static android.app.WindowConfiguration.WINDOWING_MODE_FREEFORM;
21import static android.app.WindowConfiguration.WINDOWING_MODE_PINNED;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -080022import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
Jorim Jaggi8f5701b2016-04-04 18:36:02 -070023import static android.os.Build.VERSION_CODES.M;
24import static android.os.Build.VERSION_CODES.N;
Wale Ogunwale8804af22015-11-17 09:18:15 -080025import static android.view.View.MeasureSpec.AT_MOST;
26import static android.view.View.MeasureSpec.EXACTLY;
27import static android.view.View.MeasureSpec.getMode;
28import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
29import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -080030import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
31import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
Wale Ogunwale8804af22015-11-17 09:18:15 -080032import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
33import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
Jorim Jaggi9f6798a2016-02-10 22:16:06 -080034import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_INSET_DECOR;
Jorim Jaggi65bff3e2016-02-08 19:17:07 -080035import static android.view.WindowManager.LayoutParams.FLAG_LAYOUT_IN_SCREEN;
Wale Ogunwale8804af22015-11-17 09:18:15 -080036import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
37import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080038import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
39import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Chong Zhangfea963e2016-08-15 17:14:16 -070040import static android.view.WindowManager.LayoutParams.TYPE_DRAWN_APPLICATION;
John Reck32f140aa62018-10-04 15:08:24 -070041
Clara Bayarri75e09792015-07-29 16:20:40 +010042import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
Wale Ogunwale8804af22015-11-17 09:18:15 -080043
John Reck32f140aa62018-10-04 15:08:24 -070044import android.animation.Animator;
45import android.animation.AnimatorListenerAdapter;
46import android.animation.ObjectAnimator;
47import android.annotation.Nullable;
48import android.annotation.TestApi;
49import android.app.WindowConfiguration;
50import android.content.Context;
51import android.content.res.Configuration;
52import android.content.res.Resources;
53import android.graphics.Canvas;
54import android.graphics.Color;
55import android.graphics.LinearGradient;
56import android.graphics.Outline;
57import android.graphics.Paint;
58import android.graphics.PixelFormat;
59import android.graphics.RecordingCanvas;
60import android.graphics.Rect;
61import android.graphics.Region;
62import android.graphics.Shader;
63import android.graphics.drawable.ColorDrawable;
64import android.graphics.drawable.Drawable;
65import android.graphics.drawable.InsetDrawable;
66import android.graphics.drawable.LayerDrawable;
67import android.util.DisplayMetrics;
68import android.util.Log;
69import android.util.Pair;
70import android.util.TypedValue;
71import android.view.ActionMode;
72import android.view.ContextThemeWrapper;
73import android.view.Gravity;
74import android.view.InputQueue;
75import android.view.KeyEvent;
76import android.view.KeyboardShortcutGroup;
77import android.view.LayoutInflater;
78import android.view.Menu;
79import android.view.MenuItem;
80import android.view.MotionEvent;
81import android.view.ThreadedRenderer;
82import android.view.View;
83import android.view.ViewGroup;
84import android.view.ViewOutlineProvider;
85import android.view.ViewStub;
86import android.view.ViewTreeObserver;
87import android.view.Window;
88import android.view.WindowCallbacks;
89import android.view.WindowInsets;
90import android.view.WindowManager;
91import android.view.accessibility.AccessibilityEvent;
92import android.view.accessibility.AccessibilityManager;
93import android.view.accessibility.AccessibilityNodeInfo;
94import android.view.animation.AnimationUtils;
95import android.view.animation.Interpolator;
96import android.widget.FrameLayout;
97import android.widget.PopupWindow;
98
99import com.android.internal.R;
100import com.android.internal.policy.PhoneWindow.PanelFeatureState;
101import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
102import com.android.internal.view.FloatingActionMode;
103import com.android.internal.view.RootViewSurfaceTaker;
104import com.android.internal.view.StandaloneActionMode;
105import com.android.internal.view.menu.ContextMenuBuilder;
106import com.android.internal.view.menu.MenuHelper;
107import com.android.internal.widget.ActionBarContextView;
108import com.android.internal.widget.BackgroundFallback;
109import com.android.internal.widget.DecorCaptionView;
110import com.android.internal.widget.FloatingToolbar;
111
112import java.util.List;
113
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800114/** @hide */
115public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800116 private static final String TAG = "DecorView";
117
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800118 private static final boolean DEBUG_MEASURE = false;
119
Wale Ogunwale8804af22015-11-17 09:18:15 -0800120 private static final boolean SWEEP_OPEN_MENU = false;
121
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800122 // The height of a window which has focus in DIP.
123 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
124 // The height of a window which has not in DIP.
125 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
126
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200127 public static final ColorViewAttributes STATUS_BAR_COLOR_VIEW_ATTRIBUTES =
128 new ColorViewAttributes(SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
129 Gravity.TOP, Gravity.LEFT, Gravity.RIGHT,
130 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
131 com.android.internal.R.id.statusBarBackground,
132 FLAG_FULLSCREEN);
133
134 public static final ColorViewAttributes NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES =
135 new ColorViewAttributes(
136 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
137 Gravity.BOTTOM, Gravity.RIGHT, Gravity.LEFT,
138 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
139 com.android.internal.R.id.navigationBarBackground,
140 0 /* hideWindowFlag */);
141
Winson Chung4d8681f2017-05-23 16:22:08 -0700142 // This is used to workaround an issue where the PiP shadow can be transparent if the window
143 // background is transparent
144 private static final ViewOutlineProvider PIP_OUTLINE_PROVIDER = new ViewOutlineProvider() {
145 @Override
146 public void getOutline(View view, Outline outline) {
147 outline.setRect(0, 0, view.getWidth(), view.getHeight());
148 outline.setAlpha(1f);
149 }
150 };
151
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800152 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
153 // size calculation takes the shadow size into account. We set the elevation currently
154 // to max until the first layout command has been executed.
155 private boolean mAllowUpdateElevation = false;
156
157 private boolean mElevationAdjustedForStack = false;
158
Winson Chung4d8681f2017-05-23 16:22:08 -0700159 // Keeps track of the picture-in-picture mode for the view shadow
160 private boolean mIsInPictureInPictureMode;
161
162 // Stores the previous outline provider prior to applying PIP_OUTLINE_PROVIDER
163 private ViewOutlineProvider mLastOutlineProvider;
164
Wale Ogunwale8804af22015-11-17 09:18:15 -0800165 int mDefaultOpacity = PixelFormat.OPAQUE;
166
167 /** The feature ID of the panel, or -1 if this is the application's DecorView */
168 private final int mFeatureId;
169
170 private final Rect mDrawingBounds = new Rect();
171
172 private final Rect mBackgroundPadding = new Rect();
173
174 private final Rect mFramePadding = new Rect();
175
176 private final Rect mFrameOffsets = new Rect();
177
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800178 private boolean mHasCaption = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800179
180 private boolean mChanging;
181
182 private Drawable mMenuBackground;
183 private boolean mWatchingForMenu;
184 private int mDownY;
185
186 ActionMode mPrimaryActionMode;
187 private ActionMode mFloatingActionMode;
188 private ActionBarContextView mPrimaryActionModeView;
189 private PopupWindow mPrimaryActionModePopup;
190 private Runnable mShowPrimaryActionModePopup;
191 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
192 private View mFloatingActionModeOriginatingView;
193 private FloatingToolbar mFloatingToolbar;
194 private ObjectAnimator mFadeAnim;
195
196 // View added at runtime to draw under the status bar area
197 private View mStatusGuard;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800198
Jorim Jaggi30d64f32017-04-07 16:33:17 +0200199 private final ColorViewState mStatusColorViewState =
200 new ColorViewState(STATUS_BAR_COLOR_VIEW_ATTRIBUTES);
201 private final ColorViewState mNavigationColorViewState =
202 new ColorViewState(NAVIGATION_BAR_COLOR_VIEW_ATTRIBUTES);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800203
204 private final Interpolator mShowInterpolator;
205 private final Interpolator mHideInterpolator;
206 private final int mBarEnterExitDuration;
Jorim Jaggi8f5701b2016-04-04 18:36:02 -0700207 final boolean mForceWindowDrawsStatusBarBackground;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800208 private final int mSemiTransparentStatusBarColor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800209
210 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
211
212 private int mLastTopInset = 0;
213 private int mLastBottomInset = 0;
214 private int mLastRightInset = 0;
Adrian Roos85d202b2016-06-02 16:27:47 -0700215 private int mLastLeftInset = 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800216 private boolean mLastHasTopStableInset = false;
217 private boolean mLastHasBottomStableInset = false;
218 private boolean mLastHasRightStableInset = false;
Adrian Roos85d202b2016-06-02 16:27:47 -0700219 private boolean mLastHasLeftStableInset = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800220 private int mLastWindowFlags = 0;
Jorim Jaggie5638a62016-03-25 22:57:01 -0700221 private boolean mLastShouldAlwaysConsumeNavBar = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800222
223 private int mRootScrollY = 0;
224
225 private PhoneWindow mWindow;
226
227 ViewGroup mContentRoot;
228
229 private Rect mTempRect;
230 private Rect mOutsets = new Rect();
231
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800232 // This is the caption view for the window, containing the caption and window control
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800233 // buttons. The visibility of this decor depends on the workspace and the window type.
234 // If the window type does not require such a view, this member might be null.
Garfield Tan3b9613c2018-12-26 17:08:51 -0800235 private DecorCaptionView mDecorCaptionView;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800236
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800237 private boolean mWindowResizeCallbacksAdded = false;
Chong Zhangd3fd96c2016-02-08 18:25:24 -0800238 private Drawable.Callback mLastBackgroundDrawableCb = null;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800239 private BackdropFrameRenderer mBackdropFrameRenderer = null;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800240 private Drawable mResizingBackgroundDrawable;
241 private Drawable mCaptionBackgroundDrawable;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800242 private Drawable mUserCaptionBackgroundDrawable;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800243
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800244 private float mAvailableWidth;
245
246 String mLogTag = TAG;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800247 private final Rect mFloatingInsets = new Rect();
248 private boolean mApplyFloatingVerticalInsets = false;
249 private boolean mApplyFloatingHorizontalInsets = false;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800250
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700251 private int mResizeMode = RESIZE_MODE_INVALID;
252 private final int mResizeShadowSize;
253 private final Paint mVerticalResizeShadowPaint = new Paint();
254 private final Paint mHorizontalResizeShadowPaint = new Paint();
255
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800256 DecorView(Context context, int featureId, PhoneWindow window,
257 WindowManager.LayoutParams params) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800258 super(context);
259 mFeatureId = featureId;
260
261 mShowInterpolator = AnimationUtils.loadInterpolator(context,
262 android.R.interpolator.linear_out_slow_in);
263 mHideInterpolator = AnimationUtils.loadInterpolator(context,
264 android.R.interpolator.fast_out_linear_in);
265
266 mBarEnterExitDuration = context.getResources().getInteger(
267 R.integer.dock_enter_exit_duration);
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800268 mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean(
Jorim Jaggi8f5701b2016-04-04 18:36:02 -0700269 R.bool.config_forceWindowDrawsStatusBarBackground)
270 && context.getApplicationInfo().targetSdkVersion >= N;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800271 mSemiTransparentStatusBarColor = context.getResources().getColor(
272 R.color.system_bar_background_semi_transparent, null /* theme */);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800273
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800274 updateAvailableWidth();
275
Wale Ogunwale8804af22015-11-17 09:18:15 -0800276 setWindow(window);
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800277
278 updateLogTag(params);
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700279
280 mResizeShadowSize = context.getResources().getDimensionPixelSize(
281 R.dimen.resize_shadow_size);
282 initResizingPaints();
Wale Ogunwale8804af22015-11-17 09:18:15 -0800283 }
284
Nader Jawad56c68bc2018-04-24 17:08:51 -0700285 void setBackgroundFallback(@Nullable Drawable fallbackDrawable) {
286 mBackgroundFallback.setDrawable(fallbackDrawable);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800287 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
288 }
289
Nader Jawad56c68bc2018-04-24 17:08:51 -0700290 @TestApi
291 public @Nullable Drawable getBackgroundFallback() {
292 return mBackgroundFallback.getDrawable();
293 }
294
Wale Ogunwale8804af22015-11-17 09:18:15 -0800295 @Override
Chris Craik867b8122016-05-05 16:19:22 -0700296 public boolean gatherTransparentRegion(Region region) {
297 boolean statusOpaque = gatherTransparentRegion(mStatusColorViewState, region);
298 boolean navOpaque = gatherTransparentRegion(mNavigationColorViewState, region);
299 boolean decorOpaque = super.gatherTransparentRegion(region);
300
301 // combine bools after computation, so each method above always executes
302 return statusOpaque || navOpaque || decorOpaque;
303 }
304
305 boolean gatherTransparentRegion(ColorViewState colorViewState, Region region) {
306 if (colorViewState.view != null && colorViewState.visible && isResizing()) {
307 // If a visible ColorViewState is in a resizing host DecorView, forcibly register its
308 // opaque area, since it's drawn by a different root RenderNode. It would otherwise be
309 // rejected by ViewGroup#gatherTransparentRegion() for the view not being VISIBLE.
310 return colorViewState.view.gatherTransparentRegion(region);
311 }
312 return false; // no opaque area added
313 }
314
315 @Override
Wale Ogunwale8804af22015-11-17 09:18:15 -0800316 public void onDraw(Canvas c) {
317 super.onDraw(c);
Jorim Jaggi853d17d2017-05-19 14:53:55 +0200318
Adrian Roos786ea782018-04-30 16:05:00 +0200319 mBackgroundFallback.draw(this, mContentRoot, c, mWindow.mContentParent,
320 mStatusColorViewState.view, mNavigationColorViewState.view);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800321 }
322
323 @Override
324 public boolean dispatchKeyEvent(KeyEvent event) {
325 final int keyCode = event.getKeyCode();
326 final int action = event.getAction();
327 final boolean isDown = action == KeyEvent.ACTION_DOWN;
328
329 if (isDown && (event.getRepeatCount() == 0)) {
330 // First handle chording of panel key: if a panel key is held
331 // but not released, try to execute a shortcut in it.
332 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
333 boolean handled = dispatchKeyShortcutEvent(event);
334 if (handled) {
335 return true;
336 }
337 }
338
339 // If a panel is open, perform a shortcut on it without the
340 // chorded panel key
341 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
342 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
343 return true;
344 }
345 }
346 }
347
348 if (!mWindow.isDestroyed()) {
349 final Window.Callback cb = mWindow.getCallback();
350 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
351 : super.dispatchKeyEvent(event);
352 if (handled) {
353 return true;
354 }
355 }
356
357 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
358 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
359 }
360
361 @Override
362 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
363 // If the panel is already prepared, then perform the shortcut using it.
364 boolean handled;
365 if (mWindow.mPreparedPanel != null) {
366 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
367 Menu.FLAG_PERFORM_NO_CLOSE);
368 if (handled) {
369 if (mWindow.mPreparedPanel != null) {
370 mWindow.mPreparedPanel.isHandled = true;
371 }
372 return true;
373 }
374 }
375
376 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
377 final Window.Callback cb = mWindow.getCallback();
378 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
379 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
380 if (handled) {
381 return true;
382 }
383
384 // If the panel is not prepared, then we may be trying to handle a shortcut key
385 // combination such as Control+C. Temporarily prepare the panel then mark it
386 // unprepared again when finished to ensure that the panel will again be prepared
387 // the next time it is shown for real.
388 PhoneWindow.PanelFeatureState st =
389 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
390 if (st != null && mWindow.mPreparedPanel == null) {
391 mWindow.preparePanel(st, ev);
392 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
393 Menu.FLAG_PERFORM_NO_CLOSE);
394 st.isPrepared = false;
395 if (handled) {
396 return true;
397 }
398 }
399 return false;
400 }
401
402 @Override
403 public boolean dispatchTouchEvent(MotionEvent ev) {
404 final Window.Callback cb = mWindow.getCallback();
405 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
406 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
407 }
408
409 @Override
410 public boolean dispatchTrackballEvent(MotionEvent ev) {
411 final Window.Callback cb = mWindow.getCallback();
412 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
413 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
414 }
415
416 @Override
417 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
418 final Window.Callback cb = mWindow.getCallback();
419 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
420 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
421 }
422
423 public boolean superDispatchKeyEvent(KeyEvent event) {
424 // Give priority to closing action modes if applicable.
425 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
426 final int action = event.getAction();
427 // Back cancels action modes first.
428 if (mPrimaryActionMode != null) {
429 if (action == KeyEvent.ACTION_UP) {
430 mPrimaryActionMode.finish();
431 }
432 return true;
433 }
434 }
435
Evan Rosky5e29c072017-06-02 17:31:22 -0700436 if (super.dispatchKeyEvent(event)) {
437 return true;
438 }
439
Evan Roskycd80e612018-05-17 17:46:09 -0700440 return (getViewRootImpl() != null) && getViewRootImpl().dispatchUnhandledKeyEvent(event);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800441 }
442
443 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
444 return super.dispatchKeyShortcutEvent(event);
445 }
446
447 public boolean superDispatchTouchEvent(MotionEvent event) {
448 return super.dispatchTouchEvent(event);
449 }
450
451 public boolean superDispatchTrackballEvent(MotionEvent event) {
452 return super.dispatchTrackballEvent(event);
453 }
454
455 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
456 return super.dispatchGenericMotionEvent(event);
457 }
458
459 @Override
460 public boolean onTouchEvent(MotionEvent event) {
461 return onInterceptTouchEvent(event);
462 }
463
464 private boolean isOutOfInnerBounds(int x, int y) {
465 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
466 }
467
468 private boolean isOutOfBounds(int x, int y) {
469 return x < -5 || y < -5 || x > (getWidth() + 5)
470 || y > (getHeight() + 5);
471 }
472
473 @Override
474 public boolean onInterceptTouchEvent(MotionEvent event) {
475 int action = event.getAction();
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800476 if (mHasCaption && isShowingCaption()) {
477 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
478 // was (starting) outside the window. Window resizing events should be handled by
479 // WindowManager.
Wale Ogunwale8804af22015-11-17 09:18:15 -0800480 // TODO: Investigate how to handle the outside touch in window manager
481 // without generating these events.
482 // Currently we receive these because we need to enlarge the window's
483 // touch region so that the monitor channel receives the events
484 // in the outside touch area.
485 if (action == MotionEvent.ACTION_DOWN) {
486 final int x = (int) event.getX();
487 final int y = (int) event.getY();
488 if (isOutOfInnerBounds(x, y)) {
489 return true;
490 }
491 }
492 }
493
494 if (mFeatureId >= 0) {
495 if (action == MotionEvent.ACTION_DOWN) {
496 int x = (int)event.getX();
497 int y = (int)event.getY();
498 if (isOutOfBounds(x, y)) {
499 mWindow.closePanel(mFeatureId);
500 return true;
501 }
502 }
503 }
504
505 if (!SWEEP_OPEN_MENU) {
506 return false;
507 }
508
509 if (mFeatureId >= 0) {
510 if (action == MotionEvent.ACTION_DOWN) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800511 Log.i(mLogTag, "Watchiing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800512 mWatchingForMenu = true;
513 mDownY = (int) event.getY();
514 return false;
515 }
516
517 if (!mWatchingForMenu) {
518 return false;
519 }
520
521 int y = (int)event.getY();
522 if (action == MotionEvent.ACTION_MOVE) {
523 if (y > (mDownY+30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800524 Log.i(mLogTag, "Closing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800525 mWindow.closePanel(mFeatureId);
526 mWatchingForMenu = false;
527 return true;
528 }
529 } else if (action == MotionEvent.ACTION_UP) {
530 mWatchingForMenu = false;
531 }
532
533 return false;
534 }
535
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800536 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
Wale Ogunwale8804af22015-11-17 09:18:15 -0800537 // + " (in " + getHeight() + ")");
538
539 if (action == MotionEvent.ACTION_DOWN) {
540 int y = (int)event.getY();
541 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800542 Log.i(mLogTag, "Watching!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800543 mWatchingForMenu = true;
544 }
545 return false;
546 }
547
548 if (!mWatchingForMenu) {
549 return false;
550 }
551
552 int y = (int)event.getY();
553 if (action == MotionEvent.ACTION_MOVE) {
554 if (y < (getHeight()-30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800555 Log.i(mLogTag, "Opening!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800556 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
557 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
558 mWatchingForMenu = false;
559 return true;
560 }
561 } else if (action == MotionEvent.ACTION_UP) {
562 mWatchingForMenu = false;
563 }
564
565 return false;
566 }
567
568 @Override
569 public void sendAccessibilityEvent(int eventType) {
570 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
571 return;
572 }
573
574 // if we are showing a feature that should be announced and one child
575 // make this child the event source since this is the feature itself
576 // otherwise the callback will take over and announce its client
577 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
578 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
579 mFeatureId == Window.FEATURE_PROGRESS ||
580 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
581 && getChildCount() == 1) {
582 getChildAt(0).sendAccessibilityEvent(eventType);
583 } else {
584 super.sendAccessibilityEvent(eventType);
585 }
586 }
587
588 @Override
589 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
590 final Window.Callback cb = mWindow.getCallback();
591 if (cb != null && !mWindow.isDestroyed()) {
592 if (cb.dispatchPopulateAccessibilityEvent(event)) {
593 return true;
594 }
595 }
596 return super.dispatchPopulateAccessibilityEventInternal(event);
597 }
598
599 @Override
600 protected boolean setFrame(int l, int t, int r, int b) {
601 boolean changed = super.setFrame(l, t, r, b);
602 if (changed) {
603 final Rect drawingBounds = mDrawingBounds;
604 getDrawingRect(drawingBounds);
605
606 Drawable fg = getForeground();
607 if (fg != null) {
608 final Rect frameOffsets = mFrameOffsets;
609 drawingBounds.left += frameOffsets.left;
610 drawingBounds.top += frameOffsets.top;
611 drawingBounds.right -= frameOffsets.right;
612 drawingBounds.bottom -= frameOffsets.bottom;
613 fg.setBounds(drawingBounds);
614 final Rect framePadding = mFramePadding;
615 drawingBounds.left += framePadding.left - frameOffsets.left;
616 drawingBounds.top += framePadding.top - frameOffsets.top;
617 drawingBounds.right -= framePadding.right - frameOffsets.right;
618 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
619 }
620
621 Drawable bg = getBackground();
622 if (bg != null) {
623 bg.setBounds(drawingBounds);
624 }
625
626 if (SWEEP_OPEN_MENU) {
627 if (mMenuBackground == null && mFeatureId < 0
628 && mWindow.getAttributes().height
629 == WindowManager.LayoutParams.MATCH_PARENT) {
630 mMenuBackground = getContext().getDrawable(
631 R.drawable.menu_background);
632 }
633 if (mMenuBackground != null) {
634 mMenuBackground.setBounds(drawingBounds.left,
635 drawingBounds.bottom-6, drawingBounds.right,
636 drawingBounds.bottom+20);
637 }
638 }
639 }
640 return changed;
641 }
642
643 @Override
644 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
645 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800646 final boolean isPortrait =
647 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800648
649 final int widthMode = getMode(widthMeasureSpec);
650 final int heightMode = getMode(heightMeasureSpec);
651
652 boolean fixedWidth = false;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800653 mApplyFloatingHorizontalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800654 if (widthMode == AT_MOST) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800655 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800656 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
657 final int w;
658 if (tvw.type == TypedValue.TYPE_DIMENSION) {
659 w = (int) tvw.getDimension(metrics);
660 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
661 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
662 } else {
663 w = 0;
664 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800665 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
Jorim Jaggiffd04902016-02-23 19:20:59 -0500666 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800667 if (w > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800668 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
669 Math.min(w, widthSize), EXACTLY);
670 fixedWidth = true;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800671 } else {
672 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
Jorim Jaggiffd04902016-02-23 19:20:59 -0500673 widthSize - mFloatingInsets.left - mFloatingInsets.right,
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800674 AT_MOST);
675 mApplyFloatingHorizontalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800676 }
677 }
678 }
679
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800680 mApplyFloatingVerticalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800681 if (heightMode == AT_MOST) {
682 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
683 : mWindow.mFixedHeightMinor;
684 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
685 final int h;
686 if (tvh.type == TypedValue.TYPE_DIMENSION) {
687 h = (int) tvh.getDimension(metrics);
688 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
689 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
690 } else {
691 h = 0;
692 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800693 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800694 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800695 if (h > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800696 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
697 Math.min(h, heightSize), EXACTLY);
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800698 } else if ((mWindow.getAttributes().flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800699 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
700 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
701 mApplyFloatingVerticalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800702 }
703 }
704 }
705
706 getOutsets(mOutsets);
707 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
708 int mode = MeasureSpec.getMode(heightMeasureSpec);
709 if (mode != MeasureSpec.UNSPECIFIED) {
710 int height = MeasureSpec.getSize(heightMeasureSpec);
711 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
712 height + mOutsets.top + mOutsets.bottom, mode);
713 }
714 }
715 if (mOutsets.left > 0 || mOutsets.right > 0) {
716 int mode = MeasureSpec.getMode(widthMeasureSpec);
717 if (mode != MeasureSpec.UNSPECIFIED) {
718 int width = MeasureSpec.getSize(widthMeasureSpec);
719 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
720 width + mOutsets.left + mOutsets.right, mode);
721 }
722 }
723
724 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
725
726 int width = getMeasuredWidth();
727 boolean measure = false;
728
729 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
730
731 if (!fixedWidth && widthMode == AT_MOST) {
732 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
733 if (tv.type != TypedValue.TYPE_NULL) {
734 final int min;
735 if (tv.type == TypedValue.TYPE_DIMENSION) {
736 min = (int)tv.getDimension(metrics);
737 } else if (tv.type == TypedValue.TYPE_FRACTION) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800738 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800739 } else {
740 min = 0;
741 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800742 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
743 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800744
745 if (width < min) {
746 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
747 measure = true;
748 }
749 }
750 }
751
752 // TODO: Support height?
753
754 if (measure) {
755 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
756 }
757 }
758
759 @Override
760 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
761 super.onLayout(changed, left, top, right, bottom);
762 getOutsets(mOutsets);
763 if (mOutsets.left > 0) {
764 offsetLeftAndRight(-mOutsets.left);
765 }
766 if (mOutsets.top > 0) {
767 offsetTopAndBottom(-mOutsets.top);
768 }
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800769 if (mApplyFloatingVerticalInsets) {
770 offsetTopAndBottom(mFloatingInsets.top);
771 }
772 if (mApplyFloatingHorizontalInsets) {
773 offsetLeftAndRight(mFloatingInsets.left);
774 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800775
776 // If the application changed its SystemUI metrics, we might also have to adapt
777 // our shadow elevation.
778 updateElevation();
779 mAllowUpdateElevation = true;
Jorim Jaggic39c7b02016-03-24 10:47:07 -0700780
781 if (changed && mResizeMode == RESIZE_MODE_DOCKED_DIVIDER) {
782 getViewRootImpl().requestInvalidateRootRenderNode();
783 }
Wale Ogunwale8804af22015-11-17 09:18:15 -0800784 }
785
786 @Override
787 public void draw(Canvas canvas) {
788 super.draw(canvas);
789
790 if (mMenuBackground != null) {
791 mMenuBackground.draw(canvas);
792 }
793 }
794
795 @Override
796 public boolean showContextMenuForChild(View originalView) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700797 return showContextMenuForChildInternal(originalView, Float.NaN, Float.NaN);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800798 }
799
800 @Override
801 public boolean showContextMenuForChild(View originalView, float x, float y) {
Oren Blasberg23e282d2016-04-20 13:43:45 -0700802 return showContextMenuForChildInternal(originalView, x, y);
Alan Viverette021627e2015-11-25 14:22:00 -0500803 }
804
805 private boolean showContextMenuForChildInternal(View originalView,
Oren Blasberg23e282d2016-04-20 13:43:45 -0700806 float x, float y) {
Alan Viverette021627e2015-11-25 14:22:00 -0500807 // Only allow one context menu at a time.
808 if (mWindow.mContextMenuHelper != null) {
809 mWindow.mContextMenuHelper.dismiss();
810 mWindow.mContextMenuHelper = null;
811 }
812
813 // Reuse the context menu builder.
Alan Viverette77fb85e2015-12-14 11:42:44 -0500814 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800815 if (mWindow.mContextMenu == null) {
816 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
Alan Viverette77fb85e2015-12-14 11:42:44 -0500817 mWindow.mContextMenu.setCallback(callback);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800818 } else {
819 mWindow.mContextMenu.clearAll();
820 }
821
Alan Viverette021627e2015-11-25 14:22:00 -0500822 final MenuHelper helper;
Oren Blasberg23e282d2016-04-20 13:43:45 -0700823 final boolean isPopup = !Float.isNaN(x) && !Float.isNaN(y);
Alan Viverette021627e2015-11-25 14:22:00 -0500824 if (isPopup) {
825 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
826 } else {
827 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
Wale Ogunwale8804af22015-11-17 09:18:15 -0800828 }
Alan Viverette021627e2015-11-25 14:22:00 -0500829
Alan Viverette9084d222015-12-16 09:56:37 -0500830 if (helper != null) {
831 // If it's a dialog, the callback needs to handle showing
832 // sub-menus. Either way, the callback is required for propagating
833 // selection to Context.onContextMenuItemSelected().
834 callback.setShowDialogForSubmenu(!isPopup);
835 helper.setPresenterCallback(callback);
836 }
Alan Viverette021627e2015-11-25 14:22:00 -0500837
838 mWindow.mContextMenuHelper = helper;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800839 return helper != null;
840 }
841
842 @Override
843 public ActionMode startActionModeForChild(View originalView,
844 ActionMode.Callback callback) {
845 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
846 }
847
848 @Override
849 public ActionMode startActionModeForChild(
850 View child, ActionMode.Callback callback, int type) {
851 return startActionMode(child, callback, type);
852 }
853
854 @Override
855 public ActionMode startActionMode(ActionMode.Callback callback) {
856 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
857 }
858
859 @Override
860 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
861 return startActionMode(this, callback, type);
862 }
863
864 private ActionMode startActionMode(
865 View originatingView, ActionMode.Callback callback, int type) {
866 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
867 ActionMode mode = null;
868 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
869 try {
870 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
871 } catch (AbstractMethodError ame) {
872 // Older apps might not implement the typed version of this method.
873 if (type == ActionMode.TYPE_PRIMARY) {
874 try {
875 mode = mWindow.getCallback().onWindowStartingActionMode(
876 wrappedCallback);
877 } catch (AbstractMethodError ame2) {
878 // Older apps might not implement this callback method at all.
879 }
880 }
881 }
882 }
883 if (mode != null) {
884 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
885 cleanupPrimaryActionMode();
886 mPrimaryActionMode = mode;
887 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
888 if (mFloatingActionMode != null) {
889 mFloatingActionMode.finish();
890 }
891 mFloatingActionMode = mode;
892 }
893 } else {
894 mode = createActionMode(type, wrappedCallback, originatingView);
895 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
896 setHandledActionMode(mode);
897 } else {
898 mode = null;
899 }
900 }
901 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
902 try {
903 mWindow.getCallback().onActionModeStarted(mode);
904 } catch (AbstractMethodError ame) {
905 // Older apps might not implement this callback method.
906 }
907 }
908 return mode;
909 }
910
911 private void cleanupPrimaryActionMode() {
912 if (mPrimaryActionMode != null) {
913 mPrimaryActionMode.finish();
914 mPrimaryActionMode = null;
915 }
916 if (mPrimaryActionModeView != null) {
917 mPrimaryActionModeView.killMode();
918 }
919 }
920
921 private void cleanupFloatingActionModeViews() {
922 if (mFloatingToolbar != null) {
923 mFloatingToolbar.dismiss();
924 mFloatingToolbar = null;
925 }
926 if (mFloatingActionModeOriginatingView != null) {
927 if (mFloatingToolbarPreDrawListener != null) {
928 mFloatingActionModeOriginatingView.getViewTreeObserver()
929 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
930 mFloatingToolbarPreDrawListener = null;
931 }
932 mFloatingActionModeOriginatingView = null;
933 }
934 }
935
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800936 void startChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800937 mChanging = true;
938 }
939
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800940 void finishChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800941 mChanging = false;
942 drawableChanged();
943 }
944
945 public void setWindowBackground(Drawable drawable) {
946 if (getBackground() != drawable) {
947 setBackgroundDrawable(drawable);
948 if (drawable != null) {
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700949 mResizingBackgroundDrawable = enforceNonTranslucentBackground(drawable,
950 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700951 } else {
952 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
Nader Jawad56c68bc2018-04-24 17:08:51 -0700953 mWindow.mBackgroundDrawable, mWindow.mBackgroundFallbackDrawable,
Jorim Jaggi8e879f12016-05-25 16:41:49 -0700954 mWindow.isTranslucent() || mWindow.isShowingWallpaper());
Robert Carr692a5fe2016-05-19 14:11:15 -0700955 }
956 if (mResizingBackgroundDrawable != null) {
957 mResizingBackgroundDrawable.getPadding(mBackgroundPadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800958 } else {
959 mBackgroundPadding.setEmpty();
960 }
961 drawableChanged();
962 }
963 }
964
965 public void setWindowFrame(Drawable drawable) {
966 if (getForeground() != drawable) {
967 setForeground(drawable);
968 if (drawable != null) {
969 drawable.getPadding(mFramePadding);
970 } else {
971 mFramePadding.setEmpty();
972 }
973 drawableChanged();
974 }
975 }
976
977 @Override
978 public void onWindowSystemUiVisibilityChanged(int visible) {
979 updateColorViews(null /* insets */, true /* animate */);
Garfield Tan3b9613c2018-12-26 17:08:51 -0800980 updateDecorCaptionStatus(getResources().getConfiguration());
Wale Ogunwale8804af22015-11-17 09:18:15 -0800981 }
982
983 @Override
984 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800985 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
986 mFloatingInsets.setEmpty();
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800987 if ((attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800988 // For dialog windows we want to make sure they don't go over the status bar or nav bar.
989 // We consume the system insets and we will reuse them later during the measure phase.
Jorim Jaggi65bff3e2016-02-08 19:17:07 -0800990 // We allow the app to ignore this and handle insets itself by using
991 // FLAG_LAYOUT_IN_SCREEN.
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800992 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
993 mFloatingInsets.top = insets.getSystemWindowInsetTop();
994 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +0100995 insets = insets.inset(0, insets.getSystemWindowInsetTop(),
996 0, insets.getSystemWindowInsetBottom());
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800997 }
998 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
999 mFloatingInsets.left = insets.getSystemWindowInsetTop();
1000 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
Adrian Roosf7b74262017-11-22 14:21:01 +01001001 insets = insets.inset(insets.getSystemWindowInsetLeft(), 0,
1002 insets.getSystemWindowInsetRight(), 0);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -08001003 }
1004 }
Adrian Roos60f59292018-08-24 16:29:06 +02001005 mFrameOffsets.set(insets.getSystemWindowInsetsAsRect());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001006 insets = updateColorViews(insets, true /* animate */);
1007 insets = updateStatusGuard(insets);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001008 if (getForeground() != null) {
1009 drawableChanged();
1010 }
1011 return insets;
1012 }
1013
1014 @Override
1015 public boolean isTransitionGroup() {
1016 return false;
1017 }
1018
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001019 public static int getColorViewTopInset(int stableTop, int systemTop) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001020 return Math.min(stableTop, systemTop);
1021 }
1022
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001023 public static int getColorViewBottomInset(int stableBottom, int systemBottom) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001024 return Math.min(stableBottom, systemBottom);
1025 }
1026
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001027 public static int getColorViewRightInset(int stableRight, int systemRight) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001028 return Math.min(stableRight, systemRight);
1029 }
1030
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001031 public static int getColorViewLeftInset(int stableLeft, int systemLeft) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001032 return Math.min(stableLeft, systemLeft);
1033 }
1034
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001035 public static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001036 return bottomInset == 0 && rightInset > 0;
1037 }
1038
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001039 public static boolean isNavBarToLeftEdge(int bottomInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001040 return bottomInset == 0 && leftInset > 0;
1041 }
1042
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001043 public static int getNavBarSize(int bottomInset, int rightInset, int leftInset) {
Adrian Roos85d202b2016-06-02 16:27:47 -07001044 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset
1045 : isNavBarToLeftEdge(bottomInset, leftInset) ? leftInset : bottomInset;
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001046 }
1047
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001048 public static void getNavigationBarRect(int canvasWidth, int canvasHeight, Rect stableInsets,
1049 Rect contentInsets, Rect outRect) {
1050 final int bottomInset = getColorViewBottomInset(stableInsets.bottom, contentInsets.bottom);
1051 final int leftInset = getColorViewLeftInset(stableInsets.left, contentInsets.left);
1052 final int rightInset = getColorViewLeftInset(stableInsets.right, contentInsets.right);
1053 final int size = getNavBarSize(bottomInset, rightInset, leftInset);
1054 if (isNavBarToRightEdge(bottomInset, rightInset)) {
1055 outRect.set(canvasWidth - size, 0, canvasWidth, canvasHeight);
1056 } else if (isNavBarToLeftEdge(bottomInset, leftInset)) {
1057 outRect.set(0, 0, size, canvasHeight);
1058 } else {
1059 outRect.set(0, canvasHeight - size, canvasWidth, canvasHeight);
1060 }
1061 }
1062
Wale Ogunwale8804af22015-11-17 09:18:15 -08001063 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
1064 WindowManager.LayoutParams attrs = mWindow.getAttributes();
1065 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
1066
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001067 // IME is an exceptional floating window that requires color view.
1068 final boolean isImeWindow =
1069 mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD;
1070 if (!mWindow.mIsFloating || isImeWindow) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001071 boolean disallowAnimate = !isLaidOut();
1072 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
1073 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
1074 mLastWindowFlags = attrs.flags;
1075
1076 if (insets != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001077 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001078 insets.getSystemWindowInsetTop());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001079 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001080 insets.getSystemWindowInsetBottom());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001081 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
Wale Ogunwale8804af22015-11-17 09:18:15 -08001082 insets.getSystemWindowInsetRight());
Adrian Roos85d202b2016-06-02 16:27:47 -07001083 mLastLeftInset = getColorViewRightInset(insets.getStableInsetLeft(),
1084 insets.getSystemWindowInsetLeft());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001085
1086 // Don't animate if the presence of stable insets has changed, because that
1087 // indicates that the window was either just added and received them for the
1088 // first time, or the window size or position has changed.
1089 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
1090 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
1091 mLastHasTopStableInset = hasTopStableInset;
1092
1093 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
1094 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
1095 mLastHasBottomStableInset = hasBottomStableInset;
1096
1097 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
1098 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
1099 mLastHasRightStableInset = hasRightStableInset;
Adrian Roos85d202b2016-06-02 16:27:47 -07001100
1101 boolean hasLeftStableInset = insets.getStableInsetLeft() != 0;
1102 disallowAnimate |= (hasLeftStableInset != mLastHasLeftStableInset);
1103 mLastHasLeftStableInset = hasLeftStableInset;
1104
Jorim Jaggie5638a62016-03-25 22:57:01 -07001105 mLastShouldAlwaysConsumeNavBar = insets.shouldAlwaysConsumeNavBar();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001106 }
1107
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001108 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
Adrian Roos85d202b2016-06-02 16:27:47 -07001109 boolean navBarToLeftEdge = isNavBarToLeftEdge(mLastBottomInset, mLastLeftInset);
1110 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset, mLastLeftInset);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001111 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
Jason Monkea506c62017-09-01 12:40:06 -04001112 mWindow.mNavigationBarColor, mWindow.mNavigationBarDividerColor, navBarSize,
1113 navBarToRightEdge || navBarToLeftEdge, navBarToLeftEdge,
Adrian Roos85d202b2016-06-02 16:27:47 -07001114 0 /* sideInset */, animate && !disallowAnimate, false /* force */);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001115
1116 boolean statusBarNeedsRightInset = navBarToRightEdge
1117 && mNavigationColorViewState.present;
Adrian Roos85d202b2016-06-02 16:27:47 -07001118 boolean statusBarNeedsLeftInset = navBarToLeftEdge
1119 && mNavigationColorViewState.present;
1120 int statusBarSideInset = statusBarNeedsRightInset ? mLastRightInset
1121 : statusBarNeedsLeftInset ? mLastLeftInset : 0;
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001122 updateColorViewInt(mStatusColorViewState, sysUiVisibility,
Jason Monkea506c62017-09-01 12:40:06 -04001123 calculateStatusBarColor(), 0, mLastTopInset,
Adrian Roos85d202b2016-06-02 16:27:47 -07001124 false /* matchVertical */, statusBarNeedsLeftInset, statusBarSideInset,
1125 animate && !disallowAnimate,
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001126 mForceWindowDrawsStatusBarBackground);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001127 }
1128
1129 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
1130 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
1131 // explicitly asked for it.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001132 boolean consumingNavBar =
1133 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1134 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
Jorim Jaggi0ffd49c2016-02-12 15:04:21 -08001135 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0
Jorim Jaggie5638a62016-03-25 22:57:01 -07001136 || mLastShouldAlwaysConsumeNavBar;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001137
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001138 // If we didn't request fullscreen layout, but we still got it because of the
1139 // mForceWindowDrawsStatusBarBackground flag, also consume top inset.
1140 boolean consumingStatusBar = (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN) == 0
1141 && (sysUiVisibility & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) == 0
1142 && (attrs.flags & FLAG_LAYOUT_IN_SCREEN) == 0
1143 && (attrs.flags & FLAG_LAYOUT_INSET_DECOR) == 0
1144 && mForceWindowDrawsStatusBarBackground
1145 && mLastTopInset != 0;
1146
1147 int consumedTop = consumingStatusBar ? mLastTopInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001148 int consumedRight = consumingNavBar ? mLastRightInset : 0;
1149 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
Adrian Roos85d202b2016-06-02 16:27:47 -07001150 int consumedLeft = consumingNavBar ? mLastLeftInset : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001151
1152 if (mContentRoot != null
1153 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1154 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001155 if (lp.topMargin != consumedTop || lp.rightMargin != consumedRight
Adrian Roos85d202b2016-06-02 16:27:47 -07001156 || lp.bottomMargin != consumedBottom || lp.leftMargin != consumedLeft) {
Jorim Jaggi9f6798a2016-02-10 22:16:06 -08001157 lp.topMargin = consumedTop;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001158 lp.rightMargin = consumedRight;
1159 lp.bottomMargin = consumedBottom;
Adrian Roos85d202b2016-06-02 16:27:47 -07001160 lp.leftMargin = consumedLeft;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001161 mContentRoot.setLayoutParams(lp);
1162
1163 if (insets == null) {
1164 // The insets have changed, but we're not currently in the process
1165 // of dispatching them.
1166 requestApplyInsets();
1167 }
1168 }
1169 if (insets != null) {
Adrian Roosf7b74262017-11-22 14:21:01 +01001170 insets = insets.inset(consumedLeft, consumedTop, consumedRight, consumedBottom);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001171 }
1172 }
1173
1174 if (insets != null) {
1175 insets = insets.consumeStableInsets();
1176 }
1177 return insets;
1178 }
1179
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001180 private int calculateStatusBarColor() {
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001181 return calculateStatusBarColor(mWindow.getAttributes().flags,
1182 mSemiTransparentStatusBarColor, mWindow.mStatusBarColor);
1183 }
1184
1185 public static int calculateStatusBarColor(int flags, int semiTransparentStatusBarColor,
1186 int statusBarColor) {
1187 return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? semiTransparentStatusBarColor
1188 : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? statusBarColor
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001189 : Color.BLACK;
1190 }
1191
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001192 private int getCurrentColor(ColorViewState state) {
1193 if (state.visible) {
1194 return state.color;
1195 } else {
1196 return 0;
1197 }
1198 }
1199
Wale Ogunwale8804af22015-11-17 09:18:15 -08001200 /**
1201 * Update a color view
1202 *
1203 * @param state the color view to update.
1204 * @param sysUiVis the current systemUiVisibility to apply.
1205 * @param color the current color to apply.
Jason Monkea506c62017-09-01 12:40:06 -04001206 * @param dividerColor the current divider color to apply.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001207 * @param size the current size in the non-parent-matching dimension.
1208 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1209 * horizontal edge,
Adrian Roos85d202b2016-06-02 16:27:47 -07001210 * @param sideMargin sideMargin for the color view.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001211 * @param animate if true, the change will be animated.
1212 */
1213 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
Jason Monkea506c62017-09-01 12:40:06 -04001214 int dividerColor, int size, boolean verticalBar, boolean seascape, int sideMargin,
Adrian Roos85d202b2016-06-02 16:27:47 -07001215 boolean animate, boolean force) {
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001216 state.present = state.attributes.isPresent(sysUiVis, mWindow.getAttributes().flags, force);
1217 boolean show = state.attributes.isVisible(state.present, color,
1218 mWindow.getAttributes().flags, force);
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001219 boolean showView = show && !isResizing() && size > 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001220
1221 boolean visibilityChanged = false;
1222 View view = state.view;
1223
1224 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1225 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
Adrian Roos85d202b2016-06-02 16:27:47 -07001226 int resolvedGravity = verticalBar
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001227 ? (seascape ? state.attributes.seascapeGravity : state.attributes.horizontalGravity)
1228 : state.attributes.verticalGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001229
1230 if (view == null) {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001231 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001232 state.view = view = new View(mContext);
Jason Monkea506c62017-09-01 12:40:06 -04001233 setColor(view, color, dividerColor, verticalBar, seascape);
Jorim Jaggi30d64f32017-04-07 16:33:17 +02001234 view.setTransitionName(state.attributes.transitionName);
1235 view.setId(state.attributes.id);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001236 visibilityChanged = true;
1237 view.setVisibility(INVISIBLE);
1238 state.targetVisibility = VISIBLE;
1239
1240 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1241 resolvedGravity);
Adrian Roos85d202b2016-06-02 16:27:47 -07001242 if (seascape) {
1243 lp.leftMargin = sideMargin;
1244 } else {
1245 lp.rightMargin = sideMargin;
1246 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001247 addView(view, lp);
1248 updateColorViewTranslations();
1249 }
1250 } else {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001251 int vis = showView ? VISIBLE : INVISIBLE;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001252 visibilityChanged = state.targetVisibility != vis;
1253 state.targetVisibility = vis;
1254 LayoutParams lp = (LayoutParams) view.getLayoutParams();
Adrian Roos85d202b2016-06-02 16:27:47 -07001255 int rightMargin = seascape ? 0 : sideMargin;
1256 int leftMargin = seascape ? sideMargin : 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001257 if (lp.height != resolvedHeight || lp.width != resolvedWidth
Adrian Roos85d202b2016-06-02 16:27:47 -07001258 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin
1259 || lp.leftMargin != leftMargin) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001260 lp.height = resolvedHeight;
1261 lp.width = resolvedWidth;
1262 lp.gravity = resolvedGravity;
1263 lp.rightMargin = rightMargin;
Adrian Roos85d202b2016-06-02 16:27:47 -07001264 lp.leftMargin = leftMargin;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001265 view.setLayoutParams(lp);
1266 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001267 if (showView) {
Jason Monkea506c62017-09-01 12:40:06 -04001268 setColor(view, color, dividerColor, verticalBar, seascape);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001269 }
1270 }
1271 if (visibilityChanged) {
1272 view.animate().cancel();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001273 if (animate && !isResizing()) {
1274 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001275 if (view.getVisibility() != VISIBLE) {
1276 view.setVisibility(VISIBLE);
1277 view.setAlpha(0.0f);
1278 }
1279 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1280 setDuration(mBarEnterExitDuration);
1281 } else {
1282 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1283 .setDuration(mBarEnterExitDuration)
1284 .withEndAction(new Runnable() {
1285 @Override
1286 public void run() {
1287 state.view.setAlpha(1.0f);
1288 state.view.setVisibility(INVISIBLE);
1289 }
1290 });
1291 }
1292 } else {
1293 view.setAlpha(1.0f);
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001294 view.setVisibility(showView ? VISIBLE : INVISIBLE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001295 }
1296 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001297 state.visible = show;
1298 state.color = color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001299 }
1300
Jason Monkea506c62017-09-01 12:40:06 -04001301 private static void setColor(View v, int color, int dividerColor, boolean verticalBar,
1302 boolean seascape) {
1303 if (dividerColor != 0) {
1304 final Pair<Boolean, Boolean> dir = (Pair<Boolean, Boolean>) v.getTag();
1305 if (dir == null || dir.first != verticalBar || dir.second != seascape) {
1306 final int size = Math.round(
1307 TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, 1,
1308 v.getContext().getResources().getDisplayMetrics()));
1309 // Use an inset to make the divider line on the side that faces the app.
1310 final InsetDrawable d = new InsetDrawable(new ColorDrawable(color),
1311 verticalBar && !seascape ? size : 0,
1312 !verticalBar ? size : 0,
1313 verticalBar && seascape ? size : 0, 0);
1314 v.setBackground(new LayerDrawable(new Drawable[] {
1315 new ColorDrawable(dividerColor), d }));
1316 v.setTag(new Pair<>(verticalBar, seascape));
1317 } else {
1318 final LayerDrawable d = (LayerDrawable) v.getBackground();
1319 final InsetDrawable inset = ((InsetDrawable) d.getDrawable(1));
1320 ((ColorDrawable) inset.getDrawable()).setColor(color);
1321 ((ColorDrawable) d.getDrawable(0)).setColor(dividerColor);
1322 }
1323 } else {
1324 v.setTag(null);
1325 v.setBackgroundColor(color);
1326 }
1327 }
1328
Wale Ogunwale8804af22015-11-17 09:18:15 -08001329 private void updateColorViewTranslations() {
1330 // Put the color views back in place when they get moved off the screen
1331 // due to the the ViewRootImpl panning.
1332 int rootScrollY = mRootScrollY;
1333 if (mStatusColorViewState.view != null) {
1334 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1335 }
1336 if (mNavigationColorViewState.view != null) {
1337 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1338 }
1339 }
1340
1341 private WindowInsets updateStatusGuard(WindowInsets insets) {
1342 boolean showStatusGuard = false;
1343 // Show the status guard when the non-overlay contextual action bar is showing
1344 if (mPrimaryActionModeView != null) {
1345 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1346 // Insets are magic!
1347 final MarginLayoutParams mlp = (MarginLayoutParams)
1348 mPrimaryActionModeView.getLayoutParams();
1349 boolean mlpChanged = false;
1350 if (mPrimaryActionModeView.isShown()) {
1351 if (mTempRect == null) {
1352 mTempRect = new Rect();
1353 }
1354 final Rect rect = mTempRect;
1355
1356 // If the parent doesn't consume the insets, manually
1357 // apply the default system window insets.
1358 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1359 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1360 if (mlp.topMargin != newMargin) {
1361 mlpChanged = true;
1362 mlp.topMargin = insets.getSystemWindowInsetTop();
1363
1364 if (mStatusGuard == null) {
1365 mStatusGuard = new View(mContext);
1366 mStatusGuard.setBackgroundColor(mContext.getColor(
Yohei Yukawa8f162c62018-01-10 13:18:09 -08001367 R.color.decor_view_status_guard));
Wale Ogunwale8804af22015-11-17 09:18:15 -08001368 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1369 new LayoutParams(LayoutParams.MATCH_PARENT,
1370 mlp.topMargin, Gravity.START | Gravity.TOP));
1371 } else {
1372 final LayoutParams lp = (LayoutParams)
1373 mStatusGuard.getLayoutParams();
1374 if (lp.height != mlp.topMargin) {
1375 lp.height = mlp.topMargin;
1376 mStatusGuard.setLayoutParams(lp);
1377 }
1378 }
1379 }
1380
1381 // The action mode's theme may differ from the app, so
1382 // always show the status guard above it if we have one.
1383 showStatusGuard = mStatusGuard != null;
1384
1385 // We only need to consume the insets if the action
1386 // mode is overlaid on the app content (e.g. it's
1387 // sitting in a FrameLayout, see
1388 // screen_simple_overlay_action_mode.xml).
1389 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1390 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
Adrian Roosf7b74262017-11-22 14:21:01 +01001391 if (nonOverlay && showStatusGuard) {
1392 insets = insets.inset(0, insets.getSystemWindowInsetTop(), 0, 0);
1393 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001394 } else {
1395 // reset top margin
1396 if (mlp.topMargin != 0) {
1397 mlpChanged = true;
1398 mlp.topMargin = 0;
1399 }
1400 }
1401 if (mlpChanged) {
1402 mPrimaryActionModeView.setLayoutParams(mlp);
1403 }
1404 }
1405 }
1406 if (mStatusGuard != null) {
1407 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1408 }
1409 return insets;
1410 }
1411
Winson Chung4d8681f2017-05-23 16:22:08 -07001412 /**
1413 * Overrides the view outline when the activity enters picture-in-picture to ensure that it has
1414 * an opaque shadow even if the window background is completely transparent. This only applies
1415 * to activities that are currently the task root.
1416 */
1417 public void updatePictureInPictureOutlineProvider(boolean isInPictureInPictureMode) {
1418 if (mIsInPictureInPictureMode == isInPictureInPictureMode) {
1419 return;
1420 }
1421
1422 if (isInPictureInPictureMode) {
1423 final Window.WindowControllerCallback callback =
1424 mWindow.getWindowControllerCallback();
1425 if (callback != null && callback.isTaskRoot()) {
1426 // Call super implementation directly as we don't want to save the PIP outline
1427 // provider to be restored
1428 super.setOutlineProvider(PIP_OUTLINE_PROVIDER);
1429 }
1430 } else {
1431 // Restore the previous outline provider
1432 if (getOutlineProvider() != mLastOutlineProvider) {
1433 setOutlineProvider(mLastOutlineProvider);
1434 }
1435 }
1436 mIsInPictureInPictureMode = isInPictureInPictureMode;
1437 }
1438
1439 @Override
1440 public void setOutlineProvider(ViewOutlineProvider provider) {
1441 super.setOutlineProvider(provider);
1442
1443 // Save the outline provider set to ensure that we can restore when the activity leaves PiP
1444 mLastOutlineProvider = provider;
1445 }
1446
Wale Ogunwale8804af22015-11-17 09:18:15 -08001447 private void drawableChanged() {
1448 if (mChanging) {
1449 return;
1450 }
1451
1452 setPadding(mFramePadding.left + mBackgroundPadding.left,
1453 mFramePadding.top + mBackgroundPadding.top,
1454 mFramePadding.right + mBackgroundPadding.right,
1455 mFramePadding.bottom + mBackgroundPadding.bottom);
1456 requestLayout();
1457 invalidate();
1458
1459 int opacity = PixelFormat.OPAQUE;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001460 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
1461 if (winConfig.hasWindowShadow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001462 // If the window has a shadow, it must be translucent.
1463 opacity = PixelFormat.TRANSLUCENT;
1464 } else{
1465 // Note: If there is no background, we will assume opaque. The
1466 // common case seems to be that an application sets there to be
1467 // no background so it can draw everything itself. For that,
1468 // we would like to assume OPAQUE and let the app force it to
1469 // the slower TRANSLUCENT mode if that is really what it wants.
1470 Drawable bg = getBackground();
1471 Drawable fg = getForeground();
1472 if (bg != null) {
1473 if (fg == null) {
1474 opacity = bg.getOpacity();
1475 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1476 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1477 // If the frame padding is zero, then we can be opaque
1478 // if either the frame -or- the background is opaque.
1479 int fop = fg.getOpacity();
1480 int bop = bg.getOpacity();
1481 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001482 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001483 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1484 opacity = PixelFormat.OPAQUE;
1485 } else if (fop == PixelFormat.UNKNOWN) {
1486 opacity = bop;
1487 } else if (bop == PixelFormat.UNKNOWN) {
1488 opacity = fop;
1489 } else {
1490 opacity = Drawable.resolveOpacity(fop, bop);
1491 }
1492 } else {
1493 // For now we have to assume translucent if there is a
1494 // frame with padding... there is no way to tell if the
1495 // frame and background together will draw all pixels.
1496 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001497 Log.v(mLogTag, "Padding: " + mFramePadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001498 opacity = PixelFormat.TRANSLUCENT;
1499 }
1500 }
1501 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001502 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001503 }
1504
1505 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001506 Log.v(mLogTag, "Selected default opacity: " + opacity);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001507
1508 mDefaultOpacity = opacity;
1509 if (mFeatureId < 0) {
1510 mWindow.setDefaultWindowFormat(opacity);
1511 }
1512 }
1513
1514 @Override
1515 public void onWindowFocusChanged(boolean hasWindowFocus) {
1516 super.onWindowFocusChanged(hasWindowFocus);
1517
1518 // If the user is chording a menu shortcut, release the chord since
1519 // this window lost focus
1520 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1521 && mWindow.mPanelChordingKey != 0) {
1522 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1523 }
1524
1525 final Window.Callback cb = mWindow.getCallback();
1526 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1527 cb.onWindowFocusChanged(hasWindowFocus);
1528 }
1529
1530 if (mPrimaryActionMode != null) {
1531 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1532 }
1533 if (mFloatingActionMode != null) {
1534 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1535 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001536
1537 updateElevation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001538 }
1539
1540 @Override
1541 protected void onAttachedToWindow() {
1542 super.onAttachedToWindow();
1543
1544 final Window.Callback cb = mWindow.getCallback();
1545 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1546 cb.onAttachedToWindow();
1547 }
1548
1549 if (mFeatureId == -1) {
1550 /*
1551 * The main window has been attached, try to restore any panels
1552 * that may have been open before. This is called in cases where
1553 * an activity is being killed for configuration change and the
1554 * menu was open. When the activity is recreated, the menu
1555 * should be shown again.
1556 */
1557 mWindow.openPanelsAfterRestore();
1558 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001559
1560 if (!mWindowResizeCallbacksAdded) {
1561 // If there is no window callback installed there was no window set before. Set it now.
1562 // Note that our ViewRootImpl object will not change.
1563 getViewRootImpl().addWindowCallbacks(this);
1564 mWindowResizeCallbacksAdded = true;
1565 } else if (mBackdropFrameRenderer != null) {
1566 // We are resizing and this call happened due to a configuration change. Tell the
1567 // renderer about it.
1568 mBackdropFrameRenderer.onConfigurationChange();
1569 }
Andrii Kulian51c1b672017-04-07 18:39:32 -07001570 mWindow.onViewRootImplSet(getViewRootImpl());
Wale Ogunwale8804af22015-11-17 09:18:15 -08001571 }
1572
1573 @Override
1574 protected void onDetachedFromWindow() {
1575 super.onDetachedFromWindow();
1576
1577 final Window.Callback cb = mWindow.getCallback();
1578 if (cb != null && mFeatureId < 0) {
1579 cb.onDetachedFromWindow();
1580 }
1581
1582 if (mWindow.mDecorContentParent != null) {
1583 mWindow.mDecorContentParent.dismissPopups();
1584 }
1585
1586 if (mPrimaryActionModePopup != null) {
1587 removeCallbacks(mShowPrimaryActionModePopup);
1588 if (mPrimaryActionModePopup.isShowing()) {
1589 mPrimaryActionModePopup.dismiss();
1590 }
1591 mPrimaryActionModePopup = null;
1592 }
1593 if (mFloatingToolbar != null) {
1594 mFloatingToolbar.dismiss();
1595 mFloatingToolbar = null;
1596 }
1597
1598 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1599 if (st != null && st.menu != null && mFeatureId < 0) {
1600 st.menu.close();
1601 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001602
Jorim Jaggib2005a02016-04-08 14:13:30 -07001603 releaseThreadedRenderer();
1604
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001605 if (mWindowResizeCallbacksAdded) {
1606 getViewRootImpl().removeWindowCallbacks(this);
1607 mWindowResizeCallbacksAdded = false;
1608 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001609 }
1610
1611 @Override
1612 public void onCloseSystemDialogs(String reason) {
1613 if (mFeatureId >= 0) {
1614 mWindow.closeAllPanels();
1615 }
1616 }
1617
1618 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1619 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1620 }
1621
1622 public InputQueue.Callback willYouTakeTheInputQueue() {
1623 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1624 }
1625
1626 public void setSurfaceType(int type) {
1627 mWindow.setType(type);
1628 }
1629
1630 public void setSurfaceFormat(int format) {
1631 mWindow.setFormat(format);
1632 }
1633
1634 public void setSurfaceKeepScreenOn(boolean keepOn) {
1635 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1636 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1637 }
1638
1639 @Override
1640 public void onRootViewScrollYChanged(int rootScrollY) {
1641 mRootScrollY = rootScrollY;
1642 updateColorViewTranslations();
1643 }
1644
1645 private ActionMode createActionMode(
1646 int type, ActionMode.Callback2 callback, View originatingView) {
1647 switch (type) {
1648 case ActionMode.TYPE_PRIMARY:
1649 default:
1650 return createStandaloneActionMode(callback);
1651 case ActionMode.TYPE_FLOATING:
1652 return createFloatingActionMode(originatingView, callback);
1653 }
1654 }
1655
1656 private void setHandledActionMode(ActionMode mode) {
1657 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1658 setHandledPrimaryActionMode(mode);
1659 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1660 setHandledFloatingActionMode(mode);
1661 }
1662 }
1663
1664 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1665 endOnGoingFadeAnimation();
1666 cleanupPrimaryActionMode();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001667 // We want to create new mPrimaryActionModeView in two cases: if there is no existing
1668 // instance at all, or if there is one, but it is detached from window. The latter case
1669 // might happen when app is resized in multi-window mode and decor view is preserved
1670 // along with the main app window. Keeping mPrimaryActionModeView reference doesn't cause
1671 // app memory leaks because killMode() is called when the dismiss animation ends and from
1672 // cleanupPrimaryActionMode() invocation above.
1673 if (mPrimaryActionModeView == null || !mPrimaryActionModeView.isAttachedToWindow()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001674 if (mWindow.isFloating()) {
1675 // Use the action bar theme.
1676 final TypedValue outValue = new TypedValue();
1677 final Resources.Theme baseTheme = mContext.getTheme();
1678 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1679
1680 final Context actionBarContext;
1681 if (outValue.resourceId != 0) {
1682 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1683 actionBarTheme.setTo(baseTheme);
1684 actionBarTheme.applyStyle(outValue.resourceId, true);
1685
1686 actionBarContext = new ContextThemeWrapper(mContext, 0);
1687 actionBarContext.getTheme().setTo(actionBarTheme);
1688 } else {
1689 actionBarContext = mContext;
1690 }
1691
1692 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1693 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1694 R.attr.actionModePopupWindowStyle);
1695 mPrimaryActionModePopup.setWindowLayoutType(
1696 WindowManager.LayoutParams.TYPE_APPLICATION);
1697 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1698 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1699
1700 actionBarContext.getTheme().resolveAttribute(
1701 R.attr.actionBarSize, outValue, true);
1702 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1703 actionBarContext.getResources().getDisplayMetrics());
1704 mPrimaryActionModeView.setContentHeight(height);
1705 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1706 mShowPrimaryActionModePopup = new Runnable() {
1707 public void run() {
1708 mPrimaryActionModePopup.showAtLocation(
1709 mPrimaryActionModeView.getApplicationWindowToken(),
1710 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1711 endOnGoingFadeAnimation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001712
Chris Banese65b3fb2016-06-01 11:39:54 +01001713 if (shouldAnimatePrimaryActionModeView()) {
1714 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1715 0f, 1f);
1716 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1717 @Override
1718 public void onAnimationStart(Animator animation) {
1719 mPrimaryActionModeView.setVisibility(VISIBLE);
1720 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001721
Chris Banese65b3fb2016-06-01 11:39:54 +01001722 @Override
1723 public void onAnimationEnd(Animator animation) {
1724 mPrimaryActionModeView.setAlpha(1f);
1725 mFadeAnim = null;
1726 }
1727 });
1728 mFadeAnim.start();
1729 } else {
1730 mPrimaryActionModeView.setAlpha(1f);
1731 mPrimaryActionModeView.setVisibility(VISIBLE);
1732 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001733 }
1734 };
1735 } else {
Alan Viverette51efddb2017-04-05 10:00:01 -04001736 ViewStub stub = findViewById(R.id.action_mode_bar_stub);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001737 if (stub != null) {
1738 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07001739 mPrimaryActionModePopup = null;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001740 }
1741 }
1742 }
1743 if (mPrimaryActionModeView != null) {
1744 mPrimaryActionModeView.killMode();
1745 ActionMode mode = new StandaloneActionMode(
1746 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1747 callback, mPrimaryActionModePopup == null);
1748 return mode;
1749 }
1750 return null;
1751 }
1752
1753 private void endOnGoingFadeAnimation() {
1754 if (mFadeAnim != null) {
1755 mFadeAnim.end();
1756 }
1757 }
1758
1759 private void setHandledPrimaryActionMode(ActionMode mode) {
1760 endOnGoingFadeAnimation();
1761 mPrimaryActionMode = mode;
1762 mPrimaryActionMode.invalidate();
1763 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1764 if (mPrimaryActionModePopup != null) {
1765 post(mShowPrimaryActionModePopup);
1766 } else {
Chris Banese65b3fb2016-06-01 11:39:54 +01001767 if (shouldAnimatePrimaryActionModeView()) {
1768 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1769 mFadeAnim.addListener(new AnimatorListenerAdapter() {
1770 @Override
1771 public void onAnimationStart(Animator animation) {
1772 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1773 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001774
Chris Banese65b3fb2016-06-01 11:39:54 +01001775 @Override
1776 public void onAnimationEnd(Animator animation) {
1777 mPrimaryActionModeView.setAlpha(1f);
1778 mFadeAnim = null;
1779 }
1780 });
1781 mFadeAnim.start();
1782 } else {
1783 mPrimaryActionModeView.setAlpha(1f);
1784 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1785 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001786 }
1787 mPrimaryActionModeView.sendAccessibilityEvent(
1788 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1789 }
1790
Chris Banese65b3fb2016-06-01 11:39:54 +01001791 boolean shouldAnimatePrimaryActionModeView() {
1792 // We only to animate the action mode in if the decor has already been laid out.
1793 // If it hasn't been laid out, it hasn't been drawn to screen yet.
1794 return isLaidOut();
1795 }
1796
Wale Ogunwale8804af22015-11-17 09:18:15 -08001797 private ActionMode createFloatingActionMode(
1798 View originatingView, ActionMode.Callback2 callback) {
1799 if (mFloatingActionMode != null) {
1800 mFloatingActionMode.finish();
1801 }
1802 cleanupFloatingActionModeViews();
Tarandeep Singhc9c83a92017-08-29 14:39:22 -07001803 mFloatingToolbar = new FloatingToolbar(mWindow);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001804 final FloatingActionMode mode =
Abodunrinwa Toki17293cc2017-05-22 14:16:04 +01001805 new FloatingActionMode(mContext, callback, originatingView, mFloatingToolbar);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001806 mFloatingActionModeOriginatingView = originatingView;
1807 mFloatingToolbarPreDrawListener =
1808 new ViewTreeObserver.OnPreDrawListener() {
1809 @Override
1810 public boolean onPreDraw() {
1811 mode.updateViewLocationInWindow();
1812 return true;
1813 }
1814 };
1815 return mode;
1816 }
1817
1818 private void setHandledFloatingActionMode(ActionMode mode) {
1819 mFloatingActionMode = mode;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001820 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1821 mFloatingActionModeOriginatingView.getViewTreeObserver()
1822 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1823 }
1824
1825 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001826 * Informs the decor if the caption is attached and visible.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001827 * @param attachedAndVisible true when the decor is visible.
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001828 * Note that this will even be called if there is no caption.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001829 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001830 void enableCaption(boolean attachedAndVisible) {
1831 if (mHasCaption != attachedAndVisible) {
1832 mHasCaption = attachedAndVisible;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001833 if (getForeground() != null) {
1834 drawableChanged();
1835 }
1836 }
1837 }
1838
Wale Ogunwale8804af22015-11-17 09:18:15 -08001839 void setWindow(PhoneWindow phoneWindow) {
1840 mWindow = phoneWindow;
1841 Context context = getContext();
1842 if (context instanceof DecorContext) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001843 DecorContext decorContext = (DecorContext) context;
1844 decorContext.setPhoneWindow(mWindow);
1845 }
1846 }
1847
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08001848 @Override
chaviwa213d302018-04-23 13:34:36 -07001849 public Resources getResources() {
1850 // Make sure the Resources object is propogated from the Context since it can be updated in
1851 // the Context object.
1852 return getContext().getResources();
1853 }
1854
1855 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08001856 protected void onConfigurationChanged(Configuration newConfig) {
1857 super.onConfigurationChanged(newConfig);
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001858
Garfield Tan3b9613c2018-12-26 17:08:51 -08001859 updateDecorCaptionStatus(newConfig);
1860
1861 updateAvailableWidth();
1862 initializeElevation();
1863 }
1864
1865 /**
1866 * Determines if the workspace is entirely covered by the window.
1867 * @return {@code true} when the window is filling the entire screen/workspace.
1868 **/
1869 private boolean isFillingScreen(Configuration config) {
1870 final boolean isFullscreen = config.windowConfiguration.getWindowingMode()
1871 == WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
1872 return isFullscreen && (0 != ((getWindowSystemUiVisibility() | getSystemUiVisibility())
1873 & (View.SYSTEM_UI_FLAG_FULLSCREEN | View.SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
1874 | View.SYSTEM_UI_FLAG_IMMERSIVE | View.SYSTEM_UI_FLAG_LOW_PROFILE)));
1875 }
1876
1877 private void updateDecorCaptionStatus(Configuration config) {
1878 final boolean displayWindowDecor = config.windowConfiguration.hasWindowDecorCaption()
1879 && !isFillingScreen(config);
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001880 if (mDecorCaptionView == null && displayWindowDecor) {
1881 // Configuration now requires a caption.
1882 final LayoutInflater inflater = mWindow.getLayoutInflater();
1883 mDecorCaptionView = createDecorCaptionView(inflater);
1884 if (mDecorCaptionView != null) {
1885 if (mDecorCaptionView.getParent() == null) {
1886 addView(mDecorCaptionView, 0,
1887 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001888 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001889 removeView(mContentRoot);
1890 mDecorCaptionView.addView(mContentRoot,
1891 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001892 }
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001893 } else if (mDecorCaptionView != null) {
1894 // We might have to change the kind of surface before we do anything else.
1895 mDecorCaptionView.onConfigurationChanged(displayWindowDecor);
1896 enableCaption(displayWindowDecor);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001897 }
1898 }
1899
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001900 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001901 if (mBackdropFrameRenderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001902 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001903 mBackdropFrameRenderer.onResourcesLoaded(
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001904 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001905 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1906 getCurrentColor(mNavigationColorViewState));
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001907 }
1908
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001909 mDecorCaptionView = createDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001910 final View root = inflater.inflate(layoutResource, null);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001911 if (mDecorCaptionView != null) {
1912 if (mDecorCaptionView.getParent() == null) {
1913 addView(mDecorCaptionView,
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001914 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1915 }
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001916 mDecorCaptionView.addView(root,
Filip Gruszczynski63250652015-11-18 14:43:01 -08001917 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001918 } else {
Jorim Jaggi0a13bfd2016-02-04 18:34:50 -08001919
1920 // Put it below the color views.
1921 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001922 }
1923 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001924 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001925 }
1926
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001927 private void loadBackgroundDrawablesIfNeeded() {
1928 if (mResizingBackgroundDrawable == null) {
Nader Jawad56c68bc2018-04-24 17:08:51 -07001929 mResizingBackgroundDrawable = getResizingBackgroundDrawable(mWindow.mBackgroundDrawable,
1930 mWindow.mBackgroundFallbackDrawable, mWindow.isTranslucent()
1931 || mWindow.isShowingWallpaper());
Winson Chung1af8eda2016-02-05 17:55:56 +00001932 if (mResizingBackgroundDrawable == null) {
1933 // We shouldn't really get here as the background fallback should be always
1934 // available since it is defaulted by the system.
1935 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
1936 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001937 }
1938 if (mCaptionBackgroundDrawable == null) {
1939 mCaptionBackgroundDrawable = getContext().getDrawable(
1940 R.drawable.decor_caption_title_focused);
1941 }
Chong Zhang0df63d52016-02-24 15:39:53 -08001942 if (mResizingBackgroundDrawable != null) {
1943 mLastBackgroundDrawableCb = mResizingBackgroundDrawable.getCallback();
1944 mResizingBackgroundDrawable.setCallback(null);
1945 }
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001946 }
1947
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001948 // Free floating overlapping windows require a caption.
1949 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001950 DecorCaptionView decorCaptionView = null;
1951 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001952 View view = getChildAt(i);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001953 if (view instanceof DecorCaptionView) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001954 // The decor was most likely saved from a relaunch - so reuse it.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001955 decorCaptionView = (DecorCaptionView) view;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001956 removeViewAt(i);
1957 }
1958 }
1959 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001960 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Chong Zhangfea963e2016-08-15 17:14:16 -07001961 attrs.type == TYPE_APPLICATION || attrs.type == TYPE_DRAWN_APPLICATION;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001962 final WindowConfiguration winConfig = getResources().getConfiguration().windowConfiguration;
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001963 // Only a non floating application window on one of the allowed workspaces can get a caption
Wale Ogunwale3382ab12017-07-27 08:55:03 -07001964 if (!mWindow.isFloating() && isApplication && winConfig.hasWindowDecorCaption()) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001965 // Dependent on the brightness of the used title we either use the
1966 // dark or the light button frame.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001967 if (decorCaptionView == null) {
1968 decorCaptionView = inflateDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001969 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001970 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001971 } else {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001972 decorCaptionView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001973 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001974
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001975 // Tell the decor if it has a visible caption.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001976 enableCaption(decorCaptionView != null);
1977 return decorCaptionView;
1978 }
1979
1980 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1981 final Context context = getContext();
1982 // We make a copy of the inflater, so it has the right context associated with it.
1983 inflater = inflater.from(context);
1984 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1985 null);
1986 setDecorCaptionShade(context, view);
1987 return view;
1988 }
1989
1990 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1991 final int shade = mWindow.getDecorCaptionShade();
1992 switch (shade) {
1993 case DECOR_CAPTION_SHADE_LIGHT:
1994 setLightDecorCaptionShade(view);
1995 break;
1996 case DECOR_CAPTION_SHADE_DARK:
1997 setDarkDecorCaptionShade(view);
1998 break;
1999 default: {
2000 TypedValue value = new TypedValue();
2001 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
2002 // We invert the shade depending on brightness of the theme. Dark shade for light
2003 // theme and vice versa. Thanks to this the buttons should be visible on the
2004 // background.
2005 if (Color.luminance(value.data) < 0.5) {
2006 setLightDecorCaptionShade(view);
2007 } else {
2008 setDarkDecorCaptionShade(view);
2009 }
2010 break;
2011 }
2012 }
2013 }
2014
2015 void updateDecorCaptionShade() {
2016 if (mDecorCaptionView != null) {
2017 setDecorCaptionShade(getContext(), mDecorCaptionView);
2018 }
2019 }
2020
2021 private void setLightDecorCaptionShade(DecorCaptionView view) {
2022 view.findViewById(R.id.maximize_window).setBackgroundResource(
2023 R.drawable.decor_maximize_button_light);
2024 view.findViewById(R.id.close_window).setBackgroundResource(
2025 R.drawable.decor_close_button_light);
2026 }
2027
2028 private void setDarkDecorCaptionShade(DecorCaptionView view) {
2029 view.findViewById(R.id.maximize_window).setBackgroundResource(
2030 R.drawable.decor_maximize_button_dark);
2031 view.findViewById(R.id.close_window).setBackgroundResource(
2032 R.drawable.decor_close_button_dark);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002033 }
2034
2035 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002036 * Returns the color used to fill areas the app has not rendered content to yet when the
2037 * user is resizing the window of an activity in multi-window mode.
2038 */
Nader Jawad56c68bc2018-04-24 17:08:51 -07002039 public static Drawable getResizingBackgroundDrawable(@Nullable Drawable backgroundDrawable,
2040 @Nullable Drawable fallbackDrawable, boolean windowTranslucent) {
2041 if (backgroundDrawable != null) {
2042 return enforceNonTranslucentBackground(backgroundDrawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002043 }
2044
Nader Jawad56c68bc2018-04-24 17:08:51 -07002045 if (fallbackDrawable != null) {
2046 return enforceNonTranslucentBackground(fallbackDrawable, windowTranslucent);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002047 }
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002048 return new ColorDrawable(Color.BLACK);
2049 }
2050
2051 /**
2052 * Enforces a drawable to be non-translucent to act as a background if needed, i.e. if the
2053 * window is not translucent.
2054 */
2055 private static Drawable enforceNonTranslucentBackground(Drawable drawable,
2056 boolean windowTranslucent) {
2057 if (!windowTranslucent && drawable instanceof ColorDrawable) {
2058 ColorDrawable colorDrawable = (ColorDrawable) drawable;
2059 int color = colorDrawable.getColor();
2060 if (Color.alpha(color) != 255) {
2061 ColorDrawable copy = (ColorDrawable) colorDrawable.getConstantState().newDrawable()
2062 .mutate();
2063 copy.setColor(
2064 Color.argb(255, Color.red(color), Color.green(color), Color.blue(color)));
2065 return copy;
2066 }
2067 }
2068 return drawable;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002069 }
2070
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002071 void clearContentView() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002072 if (mDecorCaptionView != null) {
Filip Gruszczynski63250652015-11-18 14:43:01 -08002073 mDecorCaptionView.removeContentView();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08002074 } else {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002075 // This window doesn't have caption, so we need to remove everything except our views
2076 // we might have added.
2077 for (int i = getChildCount() - 1; i >= 0; i--) {
2078 View v = getChildAt(i);
2079 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
Yohei Yukawa8f162c62018-01-10 13:18:09 -08002080 && v != mStatusGuard) {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08002081 removeViewAt(i);
2082 }
2083 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002084 }
2085 }
2086
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002087 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002088 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
2089 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002090 if (mBackdropFrameRenderer != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002091 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002092 }
2093 }
2094
2095 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002096 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002097 Rect stableInsets, int resizeMode) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002098 if (mWindow.isDestroyed()) {
2099 // If the owner's window is gone, we should not be able to come here anymore.
2100 releaseThreadedRenderer();
2101 return;
2102 }
2103 if (mBackdropFrameRenderer != null) {
2104 return;
2105 }
Stan Iliev45faba52016-06-28 13:33:15 -04002106 final ThreadedRenderer renderer = getThreadedRenderer();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002107 if (renderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08002108 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002109 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002110 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08002111 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
2112 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
Garfield Tanfbd8ea62018-10-16 17:09:49 -07002113 stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002114
2115 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
2116 // If we want to get the shadow shown while resizing, we would need to elevate a new
2117 // element which owns the caption and has the elevation.
2118 updateElevation();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002119
2120 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002121 }
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002122 mResizeMode = resizeMode;
2123 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002124 }
2125
2126 @Override
2127 public void onWindowDragResizeEnd() {
2128 releaseThreadedRenderer();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002129 updateColorViews(null /* insets */, false);
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002130 mResizeMode = RESIZE_MODE_INVALID;
2131 getViewRootImpl().requestInvalidateRootRenderNode();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002132 }
2133
2134 @Override
2135 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
2136 if (mBackdropFrameRenderer == null) {
2137 return false;
2138 }
2139 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
2140 }
2141
2142 @Override
2143 public void onRequestDraw(boolean reportNextDraw) {
2144 if (mBackdropFrameRenderer != null) {
2145 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
2146 } else if (reportNextDraw) {
2147 // If render thread is gone, just report immediately.
2148 if (isAttachedToWindow()) {
2149 getViewRootImpl().reportDrawFinish();
2150 }
2151 }
2152 }
2153
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002154 @Override
John Reck32f140aa62018-10-04 15:08:24 -07002155 public void onPostDraw(RecordingCanvas canvas) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002156 drawResizingShadowIfNeeded(canvas);
2157 }
2158
2159 private void initResizingPaints() {
2160 final int startColor = mContext.getResources().getColor(
2161 R.color.resize_shadow_start_color, null);
2162 final int endColor = mContext.getResources().getColor(
2163 R.color.resize_shadow_end_color, null);
2164 final int middleColor = (startColor + endColor) / 2;
2165 mHorizontalResizeShadowPaint.setShader(new LinearGradient(
2166 0, 0, 0, mResizeShadowSize, new int[] { startColor, middleColor, endColor },
2167 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2168 mVerticalResizeShadowPaint.setShader(new LinearGradient(
2169 0, 0, mResizeShadowSize, 0, new int[] { startColor, middleColor, endColor },
2170 new float[] { 0f, 0.3f, 1f }, Shader.TileMode.CLAMP));
2171 }
2172
John Reck32f140aa62018-10-04 15:08:24 -07002173 private void drawResizingShadowIfNeeded(RecordingCanvas canvas) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002174 if (mResizeMode != RESIZE_MODE_DOCKED_DIVIDER || mWindow.mIsFloating
2175 || mWindow.isTranslucent()
Jorim Jaggi8e879f12016-05-25 16:41:49 -07002176 || mWindow.isShowingWallpaper()) {
Jorim Jaggic39c7b02016-03-24 10:47:07 -07002177 return;
2178 }
2179 canvas.save();
2180 canvas.translate(0, getHeight() - mFrameOffsets.bottom);
2181 canvas.drawRect(0, 0, getWidth(), mResizeShadowSize, mHorizontalResizeShadowPaint);
2182 canvas.restore();
2183 canvas.save();
2184 canvas.translate(getWidth() - mFrameOffsets.right, 0);
2185 canvas.drawRect(0, 0, mResizeShadowSize, getHeight(), mVerticalResizeShadowPaint);
2186 canvas.restore();
2187 }
2188
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002189 /** Release the renderer thread which is usually done when the user stops resizing. */
2190 private void releaseThreadedRenderer() {
Chong Zhangd3fd96c2016-02-08 18:25:24 -08002191 if (mResizingBackgroundDrawable != null && mLastBackgroundDrawableCb != null) {
2192 mResizingBackgroundDrawable.setCallback(mLastBackgroundDrawableCb);
2193 mLastBackgroundDrawableCb = null;
2194 }
2195
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002196 if (mBackdropFrameRenderer != null) {
2197 mBackdropFrameRenderer.releaseRenderer();
2198 mBackdropFrameRenderer = null;
2199 // Bring the shadow back.
2200 updateElevation();
2201 }
2202 }
2203
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002204 private boolean isResizing() {
2205 return mBackdropFrameRenderer != null;
2206 }
2207
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002208 /**
2209 * The elevation gets set for the first time and the framework needs to be informed that
2210 * the surface layer gets created with the shadow size in mind.
2211 */
2212 private void initializeElevation() {
2213 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
2214 mAllowUpdateElevation = false;
2215 updateElevation();
2216 }
2217
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002218 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002219 float elevation = 0;
2220 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
2221 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
2222 // since the shadow is bound to the content size and not the target size.
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002223 final int windowingMode =
2224 getResources().getConfiguration().windowConfiguration.getWindowingMode();
2225 if ((windowingMode == WINDOWING_MODE_FREEFORM) && !isResizing()) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002226 elevation = hasWindowFocus() ?
2227 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
Jaewan Kim880eff62016-04-27 10:36:35 +09002228 // Add a maximum shadow height value to the top level view.
2229 // Note that pinned stack doesn't have focus
2230 // so maximum shadow height adjustment isn't needed.
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002231 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
Robert Carr232b5f82017-04-17 15:11:35 -07002232 if (!mAllowUpdateElevation) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002233 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
2234 }
2235 // Convert the DP elevation into physical pixels.
2236 elevation = dipToPx(elevation);
2237 mElevationAdjustedForStack = true;
Wale Ogunwale3382ab12017-07-27 08:55:03 -07002238 } else if (windowingMode == WINDOWING_MODE_PINNED) {
Robert Carr32bcb102018-01-29 15:03:23 -08002239 elevation = dipToPx(PINNED_WINDOWING_MODE_ELEVATION_IN_DIP);
Robert Carr232b5f82017-04-17 15:11:35 -07002240 mElevationAdjustedForStack = true;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002241 } else {
2242 mElevationAdjustedForStack = false;
2243 }
2244
2245 // Don't change the elevation if we didn't previously adjust it for the stack it was in
2246 // or it didn't change.
2247 if ((wasAdjustedForStack || mElevationAdjustedForStack)
2248 && getElevation() != elevation) {
Garfield Tanfbd8ea62018-10-16 17:09:49 -07002249 if (!isResizing()) {
2250 mWindow.setElevation(elevation);
2251 } else {
2252 // Just suppress the shadow when resizing, don't adjust surface insets because it'll
2253 // cause a flicker when drag resize for freeform window starts. #onContentDrawn()
2254 // will compensate the offset when passing to BackdropFrameRenderer.
2255 setElevation(elevation);
2256 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08002257 }
2258 }
2259
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002260 boolean isShowingCaption() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002261 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002262 }
2263
2264 int getCaptionHeight() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08002265 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08002266 }
2267
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002268 /**
2269 * Converts a DIP measure into physical pixels.
2270 * @param dip The dip value.
2271 * @return Returns the number of pixels.
2272 */
2273 private float dipToPx(float dip) {
2274 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2275 getResources().getDisplayMetrics());
2276 }
2277
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002278 /**
2279 * Provide an override of the caption background drawable.
2280 */
2281 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2282 mUserCaptionBackgroundDrawable = drawable;
2283 if (mBackdropFrameRenderer != null) {
2284 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2285 }
2286 }
2287
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002288 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2289 if (params == null) {
2290 return "";
2291 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002292 final String[] split = params.getTitle().toString().split("\\.");
2293 if (split.length > 0) {
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002294 return split[split.length - 1];
2295 } else {
2296 return "";
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002297 }
2298 }
2299
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002300 void updateLogTag(WindowManager.LayoutParams params) {
2301 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2302 }
2303
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002304 private void updateAvailableWidth() {
2305 Resources res = getResources();
2306 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2307 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2308 }
2309
Clara Bayarri75e09792015-07-29 16:20:40 +01002310 /**
2311 * @hide
2312 */
2313 @Override
Clara Bayarrifcd7e802016-03-10 12:58:18 +00002314 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list, int deviceId) {
Clara Bayarri75e09792015-07-29 16:20:40 +01002315 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
Michael Wright936f27c2017-04-11 23:23:42 +01002316 final Menu menu = st != null ? st.menu : null;
2317 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2318 mWindow.getCallback().onProvideKeyboardShortcuts(list, menu, deviceId);
Clara Bayarri75e09792015-07-29 16:20:40 +01002319 }
2320 }
2321
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002322 @Override
Vladislav Kaznacheev3787de12016-12-21 10:36:35 -08002323 public void dispatchPointerCaptureChanged(boolean hasCapture) {
2324 super.dispatchPointerCaptureChanged(hasCapture);
2325 if (!mWindow.isDestroyed() && mWindow.getCallback() != null) {
2326 mWindow.getCallback().onPointerCaptureChanged(hasCapture);
2327 }
2328 }
2329
2330 @Override
Phil Weaverf00cd142017-03-03 13:44:00 -08002331 public int getAccessibilityViewId() {
2332 return AccessibilityNodeInfo.ROOT_ITEM_ID;
2333 }
2334
2335 @Override
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002336 public String toString() {
2337 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2338 + getTitleSuffix(mWindow.getAttributes()) + "]";
2339 }
2340
Wale Ogunwale8804af22015-11-17 09:18:15 -08002341 private static class ColorViewState {
2342 View view = null;
2343 int targetVisibility = View.INVISIBLE;
2344 boolean present = false;
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002345 boolean visible;
2346 int color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002347
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002348 final ColorViewAttributes attributes;
2349
2350 ColorViewState(ColorViewAttributes attributes) {
2351 this.attributes = attributes;
2352 }
2353 }
2354
2355 public static class ColorViewAttributes {
2356
Wale Ogunwale8804af22015-11-17 09:18:15 -08002357 final int id;
2358 final int systemUiHideFlag;
2359 final int translucentFlag;
2360 final int verticalGravity;
2361 final int horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002362 final int seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002363 final String transitionName;
2364 final int hideWindowFlag;
2365
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002366 private ColorViewAttributes(int systemUiHideFlag, int translucentFlag, int verticalGravity,
2367 int horizontalGravity, int seascapeGravity, String transitionName, int id,
2368 int hideWindowFlag) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08002369 this.id = id;
2370 this.systemUiHideFlag = systemUiHideFlag;
2371 this.translucentFlag = translucentFlag;
2372 this.verticalGravity = verticalGravity;
2373 this.horizontalGravity = horizontalGravity;
Adrian Roos85d202b2016-06-02 16:27:47 -07002374 this.seascapeGravity = seascapeGravity;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002375 this.transitionName = transitionName;
2376 this.hideWindowFlag = hideWindowFlag;
2377 }
Jorim Jaggi30d64f32017-04-07 16:33:17 +02002378
2379 public boolean isPresent(int sysUiVis, int windowFlags, boolean force) {
2380 return (sysUiVis & systemUiHideFlag) == 0
2381 && (windowFlags & hideWindowFlag) == 0
2382 && ((windowFlags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
2383 || force);
2384 }
2385
2386 public boolean isVisible(boolean present, int color, int windowFlags, boolean force) {
2387 return present
2388 && (color & Color.BLACK) != 0
2389 && ((windowFlags & translucentFlag) == 0 || force);
2390 }
2391
2392 public boolean isVisible(int sysUiVis, int color, int windowFlags, boolean force) {
2393 final boolean present = isPresent(sysUiVis, windowFlags, force);
2394 return isVisible(present, color, windowFlags, force);
2395 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002396 }
2397
2398 /**
2399 * Clears out internal references when the action mode is destroyed.
2400 */
2401 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2402 private final ActionMode.Callback mWrapped;
2403
2404 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2405 mWrapped = wrapped;
2406 }
2407
2408 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2409 return mWrapped.onCreateActionMode(mode, menu);
2410 }
2411
2412 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2413 requestFitSystemWindows();
2414 return mWrapped.onPrepareActionMode(mode, menu);
2415 }
2416
2417 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2418 return mWrapped.onActionItemClicked(mode, item);
2419 }
2420
2421 public void onDestroyActionMode(ActionMode mode) {
2422 mWrapped.onDestroyActionMode(mode);
2423 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
Jorim Jaggi8f5701b2016-04-04 18:36:02 -07002424 >= M;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002425 final boolean isPrimary;
2426 final boolean isFloating;
2427 if (isMncApp) {
2428 isPrimary = mode == mPrimaryActionMode;
2429 isFloating = mode == mFloatingActionMode;
2430 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002431 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002432 + mode + " was not the current primary action mode! Expected "
2433 + mPrimaryActionMode);
2434 }
2435 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002436 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002437 + mode + " was not the current floating action mode! Expected "
2438 + mFloatingActionMode);
2439 }
2440 } else {
2441 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2442 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2443 }
2444 if (isPrimary) {
2445 if (mPrimaryActionModePopup != null) {
2446 removeCallbacks(mShowPrimaryActionModePopup);
2447 }
2448 if (mPrimaryActionModeView != null) {
2449 endOnGoingFadeAnimation();
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002450 // Store action mode view reference, so we can access it safely when animation
2451 // ends. mPrimaryActionModePopup is set together with mPrimaryActionModeView,
2452 // so no need to store reference to it in separate variable.
2453 final ActionBarContextView lastActionModeView = mPrimaryActionModeView;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002454 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2455 1f, 0f);
2456 mFadeAnim.addListener(new Animator.AnimatorListener() {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002457
Wale Ogunwale8804af22015-11-17 09:18:15 -08002458 @Override
2459 public void onAnimationStart(Animator animation) {
2460
2461 }
2462
2463 @Override
2464 public void onAnimationEnd(Animator animation) {
Andrii Kulian8d6ac262016-06-08 13:14:19 -07002465 // If mPrimaryActionModeView has changed - it means that we've
2466 // cleared the content while preserving decor view. We don't
2467 // want to change the state of new instances accidentally here.
2468 if (lastActionModeView == mPrimaryActionModeView) {
2469 lastActionModeView.setVisibility(GONE);
2470 if (mPrimaryActionModePopup != null) {
2471 mPrimaryActionModePopup.dismiss();
2472 }
2473 lastActionModeView.killMode();
2474 mFadeAnim = null;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002475 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08002476 }
2477
2478 @Override
2479 public void onAnimationCancel(Animator animation) {
2480
2481 }
2482
2483 @Override
2484 public void onAnimationRepeat(Animator animation) {
2485
2486 }
2487 });
2488 mFadeAnim.start();
2489 }
2490
2491 mPrimaryActionMode = null;
2492 } else if (isFloating) {
2493 cleanupFloatingActionModeViews();
2494 mFloatingActionMode = null;
2495 }
2496 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2497 try {
2498 mWindow.getCallback().onActionModeFinished(mode);
2499 } catch (AbstractMethodError ame) {
2500 // Older apps might not implement this callback method.
2501 }
2502 }
2503 requestFitSystemWindows();
2504 }
2505
2506 @Override
2507 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2508 if (mWrapped instanceof ActionMode.Callback2) {
2509 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2510 } else {
2511 super.onGetContentRect(mode, view, outRect);
2512 }
2513 }
2514 }
2515}