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