blob: d4ada957a14aa4e6b1e1c14da49725a961f58c4f [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
19import com.android.internal.R;
Clara Bayarri75e09792015-07-29 16:20:40 +010020import com.android.internal.policy.PhoneWindow.PanelFeatureState;
Alan Viverette77fb85e2015-12-14 11:42:44 -050021import com.android.internal.policy.PhoneWindow.PhoneWindowMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -080022import com.android.internal.view.FloatingActionMode;
23import com.android.internal.view.RootViewSurfaceTaker;
24import com.android.internal.view.StandaloneActionMode;
25import com.android.internal.view.menu.ContextMenuBuilder;
Alan Viverette021627e2015-11-25 14:22:00 -050026import com.android.internal.view.menu.MenuHelper;
Wale Ogunwale8804af22015-11-17 09:18:15 -080027import com.android.internal.widget.ActionBarContextView;
28import com.android.internal.widget.BackgroundFallback;
Wale Ogunwale62a91d62015-11-18 11:44:10 -080029import com.android.internal.widget.DecorCaptionView;
Wale Ogunwale8804af22015-11-17 09:18:15 -080030import com.android.internal.widget.FloatingToolbar;
31
Clara Bayarri75e09792015-07-29 16:20:40 +010032import java.util.List;
33
Wale Ogunwale8804af22015-11-17 09:18:15 -080034import android.animation.Animator;
35import android.animation.ObjectAnimator;
36import android.app.ActivityManager;
37import android.content.Context;
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -080038import android.content.res.Configuration;
Wale Ogunwale8804af22015-11-17 09:18:15 -080039import android.content.res.Resources;
40import android.graphics.Canvas;
41import android.graphics.Color;
42import android.graphics.PixelFormat;
43import android.graphics.Rect;
44import android.graphics.drawable.Drawable;
45import android.os.Build;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080046import android.os.RemoteException;
Wale Ogunwale8804af22015-11-17 09:18:15 -080047import android.util.DisplayMetrics;
48import android.util.Log;
49import android.util.TypedValue;
50import android.view.ActionMode;
51import android.view.ContextThemeWrapper;
52import android.view.Gravity;
53import android.view.InputQueue;
54import android.view.KeyEvent;
Clara Bayarri75e09792015-07-29 16:20:40 +010055import android.view.KeyboardShortcutGroup;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080056import android.view.LayoutInflater;
Wale Ogunwale8804af22015-11-17 09:18:15 -080057import android.view.Menu;
58import android.view.MenuItem;
59import android.view.MotionEvent;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080060import android.view.ThreadedRenderer;
Wale Ogunwale8804af22015-11-17 09:18:15 -080061import android.view.View;
62import android.view.ViewGroup;
63import android.view.ViewStub;
64import android.view.ViewTreeObserver;
65import android.view.Window;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080066import android.view.WindowCallbacks;
Wale Ogunwale8804af22015-11-17 09:18:15 -080067import android.view.WindowInsets;
68import android.view.WindowManager;
69import android.view.accessibility.AccessibilityEvent;
70import android.view.accessibility.AccessibilityManager;
71import android.view.animation.AnimationUtils;
72import android.view.animation.Interpolator;
73import android.widget.FrameLayout;
74import android.widget.PopupWindow;
75
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -080076import static android.app.ActivityManager.StackId;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080077import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
78import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -080079import static android.content.res.Configuration.ORIENTATION_PORTRAIT;
Wale Ogunwale8804af22015-11-17 09:18:15 -080080import static android.view.View.MeasureSpec.AT_MOST;
81import static android.view.View.MeasureSpec.EXACTLY;
82import static android.view.View.MeasureSpec.getMode;
83import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
84import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -080085import static android.view.Window.DECOR_CAPTION_SHADE_DARK;
86import static android.view.Window.DECOR_CAPTION_SHADE_LIGHT;
Wale Ogunwale8804af22015-11-17 09:18:15 -080087import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
88import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
89import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
90import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080091import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
92import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Clara Bayarri75e09792015-07-29 16:20:40 +010093import static com.android.internal.policy.PhoneWindow.FEATURE_OPTIONS_PANEL;
Wale Ogunwale8804af22015-11-17 09:18:15 -080094
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080095/** @hide */
96public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
Wale Ogunwale8804af22015-11-17 09:18:15 -080097 private static final String TAG = "DecorView";
98
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -080099 private static final boolean DEBUG_MEASURE = false;
100
Wale Ogunwale8804af22015-11-17 09:18:15 -0800101 private static final boolean SWEEP_OPEN_MENU = false;
102
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800103 // The height of a window which has focus in DIP.
104 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
105 // The height of a window which has not in DIP.
106 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
107
108 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
109 // size calculation takes the shadow size into account. We set the elevation currently
110 // to max until the first layout command has been executed.
111 private boolean mAllowUpdateElevation = false;
112
113 private boolean mElevationAdjustedForStack = false;
114
Wale Ogunwale8804af22015-11-17 09:18:15 -0800115 int mDefaultOpacity = PixelFormat.OPAQUE;
116
117 /** The feature ID of the panel, or -1 if this is the application's DecorView */
118 private final int mFeatureId;
119
120 private final Rect mDrawingBounds = new Rect();
121
122 private final Rect mBackgroundPadding = new Rect();
123
124 private final Rect mFramePadding = new Rect();
125
126 private final Rect mFrameOffsets = new Rect();
127
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800128 private boolean mHasCaption = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800129
130 private boolean mChanging;
131
132 private Drawable mMenuBackground;
133 private boolean mWatchingForMenu;
134 private int mDownY;
135
136 ActionMode mPrimaryActionMode;
137 private ActionMode mFloatingActionMode;
138 private ActionBarContextView mPrimaryActionModeView;
139 private PopupWindow mPrimaryActionModePopup;
140 private Runnable mShowPrimaryActionModePopup;
141 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
142 private View mFloatingActionModeOriginatingView;
143 private FloatingToolbar mFloatingToolbar;
144 private ObjectAnimator mFadeAnim;
145
146 // View added at runtime to draw under the status bar area
147 private View mStatusGuard;
148 // View added at runtime to draw under the navigation bar area
149 private View mNavigationGuard;
150
151 private final ColorViewState mStatusColorViewState = new ColorViewState(
152 SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
153 Gravity.TOP, Gravity.LEFT,
154 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
155 com.android.internal.R.id.statusBarBackground,
156 FLAG_FULLSCREEN);
157 private final ColorViewState mNavigationColorViewState = new ColorViewState(
158 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
159 Gravity.BOTTOM, Gravity.RIGHT,
160 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
161 com.android.internal.R.id.navigationBarBackground,
162 0 /* hideWindowFlag */);
163
164 private final Interpolator mShowInterpolator;
165 private final Interpolator mHideInterpolator;
166 private final int mBarEnterExitDuration;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800167 private final boolean mForceWindowDrawsStatusBarBackground;
168 private final int mSemiTransparentStatusBarColor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800169
170 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
171
172 private int mLastTopInset = 0;
173 private int mLastBottomInset = 0;
174 private int mLastRightInset = 0;
175 private boolean mLastHasTopStableInset = false;
176 private boolean mLastHasBottomStableInset = false;
177 private boolean mLastHasRightStableInset = false;
178 private int mLastWindowFlags = 0;
179
180 private int mRootScrollY = 0;
181
182 private PhoneWindow mWindow;
183
184 ViewGroup mContentRoot;
185
186 private Rect mTempRect;
187 private Rect mOutsets = new Rect();
188
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800189 // This is the caption view for the window, containing the caption and window control
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800190 // buttons. The visibility of this decor depends on the workspace and the window type.
191 // If the window type does not require such a view, this member might be null.
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800192 DecorCaptionView mDecorCaptionView;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800193
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800194 // Stack window is currently in. Since querying and changing the stack is expensive,
195 // this is the stack value the window is currently set up for.
196 int mStackId;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800197
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800198 private boolean mWindowResizeCallbacksAdded = false;
199
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800200 private BackdropFrameRenderer mBackdropFrameRenderer = null;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800201 private Drawable mResizingBackgroundDrawable;
202 private Drawable mCaptionBackgroundDrawable;
Filip Gruszczynski3dec0812015-12-09 08:42:41 -0800203 private Drawable mUserCaptionBackgroundDrawable;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800204
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800205 private float mAvailableWidth;
206
207 String mLogTag = TAG;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800208 private final Rect mFloatingInsets = new Rect();
209 private boolean mApplyFloatingVerticalInsets = false;
210 private boolean mApplyFloatingHorizontalInsets = false;
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800211
212 DecorView(Context context, int featureId, PhoneWindow window,
213 WindowManager.LayoutParams params) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800214 super(context);
215 mFeatureId = featureId;
216
217 mShowInterpolator = AnimationUtils.loadInterpolator(context,
218 android.R.interpolator.linear_out_slow_in);
219 mHideInterpolator = AnimationUtils.loadInterpolator(context,
220 android.R.interpolator.fast_out_linear_in);
221
222 mBarEnterExitDuration = context.getResources().getInteger(
223 R.integer.dock_enter_exit_duration);
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800224 mForceWindowDrawsStatusBarBackground = context.getResources().getBoolean(
225 R.bool.config_forceWindowDrawsStatusBarBackground);
226 mSemiTransparentStatusBarColor = context.getResources().getColor(
227 R.color.system_bar_background_semi_transparent, null /* theme */);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800228
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800229 updateAvailableWidth();
230
Wale Ogunwale8804af22015-11-17 09:18:15 -0800231 setWindow(window);
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800232
233 updateLogTag(params);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800234 }
235
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800236 void setBackgroundFallback(int resId) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800237 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
238 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
239 }
240
241 @Override
242 public void onDraw(Canvas c) {
243 super.onDraw(c);
244 mBackgroundFallback.draw(mContentRoot, c, mWindow.mContentParent);
245 }
246
247 @Override
248 public boolean dispatchKeyEvent(KeyEvent event) {
249 final int keyCode = event.getKeyCode();
250 final int action = event.getAction();
251 final boolean isDown = action == KeyEvent.ACTION_DOWN;
252
253 if (isDown && (event.getRepeatCount() == 0)) {
254 // First handle chording of panel key: if a panel key is held
255 // but not released, try to execute a shortcut in it.
256 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
257 boolean handled = dispatchKeyShortcutEvent(event);
258 if (handled) {
259 return true;
260 }
261 }
262
263 // If a panel is open, perform a shortcut on it without the
264 // chorded panel key
265 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
266 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
267 return true;
268 }
269 }
270 }
271
272 if (!mWindow.isDestroyed()) {
273 final Window.Callback cb = mWindow.getCallback();
274 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
275 : super.dispatchKeyEvent(event);
276 if (handled) {
277 return true;
278 }
279 }
280
281 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
282 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
283 }
284
285 @Override
286 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
287 // If the panel is already prepared, then perform the shortcut using it.
288 boolean handled;
289 if (mWindow.mPreparedPanel != null) {
290 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
291 Menu.FLAG_PERFORM_NO_CLOSE);
292 if (handled) {
293 if (mWindow.mPreparedPanel != null) {
294 mWindow.mPreparedPanel.isHandled = true;
295 }
296 return true;
297 }
298 }
299
300 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
301 final Window.Callback cb = mWindow.getCallback();
302 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
303 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
304 if (handled) {
305 return true;
306 }
307
308 // If the panel is not prepared, then we may be trying to handle a shortcut key
309 // combination such as Control+C. Temporarily prepare the panel then mark it
310 // unprepared again when finished to ensure that the panel will again be prepared
311 // the next time it is shown for real.
312 PhoneWindow.PanelFeatureState st =
313 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
314 if (st != null && mWindow.mPreparedPanel == null) {
315 mWindow.preparePanel(st, ev);
316 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
317 Menu.FLAG_PERFORM_NO_CLOSE);
318 st.isPrepared = false;
319 if (handled) {
320 return true;
321 }
322 }
323 return false;
324 }
325
326 @Override
327 public boolean dispatchTouchEvent(MotionEvent ev) {
328 final Window.Callback cb = mWindow.getCallback();
329 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
330 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
331 }
332
333 @Override
334 public boolean dispatchTrackballEvent(MotionEvent ev) {
335 final Window.Callback cb = mWindow.getCallback();
336 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
337 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
338 }
339
340 @Override
341 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
342 final Window.Callback cb = mWindow.getCallback();
343 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
344 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
345 }
346
347 public boolean superDispatchKeyEvent(KeyEvent event) {
348 // Give priority to closing action modes if applicable.
349 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
350 final int action = event.getAction();
351 // Back cancels action modes first.
352 if (mPrimaryActionMode != null) {
353 if (action == KeyEvent.ACTION_UP) {
354 mPrimaryActionMode.finish();
355 }
356 return true;
357 }
358 }
359
360 return super.dispatchKeyEvent(event);
361 }
362
363 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
364 return super.dispatchKeyShortcutEvent(event);
365 }
366
367 public boolean superDispatchTouchEvent(MotionEvent event) {
368 return super.dispatchTouchEvent(event);
369 }
370
371 public boolean superDispatchTrackballEvent(MotionEvent event) {
372 return super.dispatchTrackballEvent(event);
373 }
374
375 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
376 return super.dispatchGenericMotionEvent(event);
377 }
378
379 @Override
380 public boolean onTouchEvent(MotionEvent event) {
381 return onInterceptTouchEvent(event);
382 }
383
384 private boolean isOutOfInnerBounds(int x, int y) {
385 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
386 }
387
388 private boolean isOutOfBounds(int x, int y) {
389 return x < -5 || y < -5 || x > (getWidth() + 5)
390 || y > (getHeight() + 5);
391 }
392
393 @Override
394 public boolean onInterceptTouchEvent(MotionEvent event) {
395 int action = event.getAction();
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800396 if (mHasCaption && isShowingCaption()) {
397 // Don't dispatch ACTION_DOWN to the captionr if the window is resizable and the event
398 // was (starting) outside the window. Window resizing events should be handled by
399 // WindowManager.
Wale Ogunwale8804af22015-11-17 09:18:15 -0800400 // TODO: Investigate how to handle the outside touch in window manager
401 // without generating these events.
402 // Currently we receive these because we need to enlarge the window's
403 // touch region so that the monitor channel receives the events
404 // in the outside touch area.
405 if (action == MotionEvent.ACTION_DOWN) {
406 final int x = (int) event.getX();
407 final int y = (int) event.getY();
408 if (isOutOfInnerBounds(x, y)) {
409 return true;
410 }
411 }
412 }
413
414 if (mFeatureId >= 0) {
415 if (action == MotionEvent.ACTION_DOWN) {
416 int x = (int)event.getX();
417 int y = (int)event.getY();
418 if (isOutOfBounds(x, y)) {
419 mWindow.closePanel(mFeatureId);
420 return true;
421 }
422 }
423 }
424
425 if (!SWEEP_OPEN_MENU) {
426 return false;
427 }
428
429 if (mFeatureId >= 0) {
430 if (action == MotionEvent.ACTION_DOWN) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800431 Log.i(mLogTag, "Watchiing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800432 mWatchingForMenu = true;
433 mDownY = (int) event.getY();
434 return false;
435 }
436
437 if (!mWatchingForMenu) {
438 return false;
439 }
440
441 int y = (int)event.getY();
442 if (action == MotionEvent.ACTION_MOVE) {
443 if (y > (mDownY+30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800444 Log.i(mLogTag, "Closing!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800445 mWindow.closePanel(mFeatureId);
446 mWatchingForMenu = false;
447 return true;
448 }
449 } else if (action == MotionEvent.ACTION_UP) {
450 mWatchingForMenu = false;
451 }
452
453 return false;
454 }
455
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800456 //Log.i(mLogTag, "Intercept: action=" + action + " y=" + event.getY()
Wale Ogunwale8804af22015-11-17 09:18:15 -0800457 // + " (in " + getHeight() + ")");
458
459 if (action == MotionEvent.ACTION_DOWN) {
460 int y = (int)event.getY();
461 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800462 Log.i(mLogTag, "Watching!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800463 mWatchingForMenu = true;
464 }
465 return false;
466 }
467
468 if (!mWatchingForMenu) {
469 return false;
470 }
471
472 int y = (int)event.getY();
473 if (action == MotionEvent.ACTION_MOVE) {
474 if (y < (getHeight()-30)) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800475 Log.i(mLogTag, "Opening!");
Wale Ogunwale8804af22015-11-17 09:18:15 -0800476 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
477 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
478 mWatchingForMenu = false;
479 return true;
480 }
481 } else if (action == MotionEvent.ACTION_UP) {
482 mWatchingForMenu = false;
483 }
484
485 return false;
486 }
487
488 @Override
489 public void sendAccessibilityEvent(int eventType) {
490 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
491 return;
492 }
493
494 // if we are showing a feature that should be announced and one child
495 // make this child the event source since this is the feature itself
496 // otherwise the callback will take over and announce its client
497 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
498 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
499 mFeatureId == Window.FEATURE_PROGRESS ||
500 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
501 && getChildCount() == 1) {
502 getChildAt(0).sendAccessibilityEvent(eventType);
503 } else {
504 super.sendAccessibilityEvent(eventType);
505 }
506 }
507
508 @Override
509 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
510 final Window.Callback cb = mWindow.getCallback();
511 if (cb != null && !mWindow.isDestroyed()) {
512 if (cb.dispatchPopulateAccessibilityEvent(event)) {
513 return true;
514 }
515 }
516 return super.dispatchPopulateAccessibilityEventInternal(event);
517 }
518
519 @Override
520 protected boolean setFrame(int l, int t, int r, int b) {
521 boolean changed = super.setFrame(l, t, r, b);
522 if (changed) {
523 final Rect drawingBounds = mDrawingBounds;
524 getDrawingRect(drawingBounds);
525
526 Drawable fg = getForeground();
527 if (fg != null) {
528 final Rect frameOffsets = mFrameOffsets;
529 drawingBounds.left += frameOffsets.left;
530 drawingBounds.top += frameOffsets.top;
531 drawingBounds.right -= frameOffsets.right;
532 drawingBounds.bottom -= frameOffsets.bottom;
533 fg.setBounds(drawingBounds);
534 final Rect framePadding = mFramePadding;
535 drawingBounds.left += framePadding.left - frameOffsets.left;
536 drawingBounds.top += framePadding.top - frameOffsets.top;
537 drawingBounds.right -= framePadding.right - frameOffsets.right;
538 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
539 }
540
541 Drawable bg = getBackground();
542 if (bg != null) {
543 bg.setBounds(drawingBounds);
544 }
545
546 if (SWEEP_OPEN_MENU) {
547 if (mMenuBackground == null && mFeatureId < 0
548 && mWindow.getAttributes().height
549 == WindowManager.LayoutParams.MATCH_PARENT) {
550 mMenuBackground = getContext().getDrawable(
551 R.drawable.menu_background);
552 }
553 if (mMenuBackground != null) {
554 mMenuBackground.setBounds(drawingBounds.left,
555 drawingBounds.bottom-6, drawingBounds.right,
556 drawingBounds.bottom+20);
557 }
558 }
559 }
560 return changed;
561 }
562
563 @Override
564 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
565 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800566 final boolean isPortrait =
567 getResources().getConfiguration().orientation == ORIENTATION_PORTRAIT;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800568
569 final int widthMode = getMode(widthMeasureSpec);
570 final int heightMode = getMode(heightMeasureSpec);
571
572 boolean fixedWidth = false;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800573 mApplyFloatingHorizontalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800574 if (widthMode == AT_MOST) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800575 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor : mWindow.mFixedWidthMajor;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800576 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
577 final int w;
578 if (tvw.type == TypedValue.TYPE_DIMENSION) {
579 w = (int) tvw.getDimension(metrics);
580 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
581 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
582 } else {
583 w = 0;
584 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800585 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed width: " + w);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800586 if (w > 0) {
587 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
588 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
589 Math.min(w, widthSize), EXACTLY);
590 fixedWidth = true;
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800591 } else {
592 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
593 widthMeasureSpec - mFloatingInsets.left - mFloatingInsets.right,
594 AT_MOST);
595 mApplyFloatingHorizontalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800596 }
597 }
598 }
599
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800600 mApplyFloatingVerticalInsets = false;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800601 if (heightMode == AT_MOST) {
602 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
603 : mWindow.mFixedHeightMinor;
604 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
605 final int h;
606 if (tvh.type == TypedValue.TYPE_DIMENSION) {
607 h = (int) tvh.getDimension(metrics);
608 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
609 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
610 } else {
611 h = 0;
612 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800613 if (DEBUG_MEASURE) Log.d(mLogTag, "Fixed height: " + h);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800614 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800615 if (h > 0) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800616 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
617 Math.min(h, heightSize), EXACTLY);
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800618 } else if ((mWindow.getAttributes().flags & FLAG_FULLSCREEN) == 0) {
619 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
620 heightSize - mFloatingInsets.top - mFloatingInsets.bottom, AT_MOST);
621 mApplyFloatingVerticalInsets = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800622 }
623 }
624 }
625
626 getOutsets(mOutsets);
627 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
628 int mode = MeasureSpec.getMode(heightMeasureSpec);
629 if (mode != MeasureSpec.UNSPECIFIED) {
630 int height = MeasureSpec.getSize(heightMeasureSpec);
631 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
632 height + mOutsets.top + mOutsets.bottom, mode);
633 }
634 }
635 if (mOutsets.left > 0 || mOutsets.right > 0) {
636 int mode = MeasureSpec.getMode(widthMeasureSpec);
637 if (mode != MeasureSpec.UNSPECIFIED) {
638 int width = MeasureSpec.getSize(widthMeasureSpec);
639 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
640 width + mOutsets.left + mOutsets.right, mode);
641 }
642 }
643
644 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
645
646 int width = getMeasuredWidth();
647 boolean measure = false;
648
649 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
650
651 if (!fixedWidth && widthMode == AT_MOST) {
652 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
653 if (tv.type != TypedValue.TYPE_NULL) {
654 final int min;
655 if (tv.type == TypedValue.TYPE_DIMENSION) {
656 min = (int)tv.getDimension(metrics);
657 } else if (tv.type == TypedValue.TYPE_FRACTION) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -0800658 min = (int)tv.getFraction(mAvailableWidth, mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800659 } else {
660 min = 0;
661 }
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -0800662 if (DEBUG_MEASURE) Log.d(mLogTag, "Adjust for min width: " + min + ", value::"
663 + tv.coerceToString() + ", mAvailableWidth=" + mAvailableWidth);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800664
665 if (width < min) {
666 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
667 measure = true;
668 }
669 }
670 }
671
672 // TODO: Support height?
673
674 if (measure) {
675 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
676 }
677 }
678
679 @Override
680 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
681 super.onLayout(changed, left, top, right, bottom);
682 getOutsets(mOutsets);
683 if (mOutsets.left > 0) {
684 offsetLeftAndRight(-mOutsets.left);
685 }
686 if (mOutsets.top > 0) {
687 offsetTopAndBottom(-mOutsets.top);
688 }
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800689 if (mApplyFloatingVerticalInsets) {
690 offsetTopAndBottom(mFloatingInsets.top);
691 }
692 if (mApplyFloatingHorizontalInsets) {
693 offsetLeftAndRight(mFloatingInsets.left);
694 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800695
696 // If the application changed its SystemUI metrics, we might also have to adapt
697 // our shadow elevation.
698 updateElevation();
699 mAllowUpdateElevation = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800700 }
701
702 @Override
703 public void draw(Canvas canvas) {
704 super.draw(canvas);
705
706 if (mMenuBackground != null) {
707 mMenuBackground.draw(canvas);
708 }
709 }
710
711 @Override
712 public boolean showContextMenuForChild(View originalView) {
Alan Viverette021627e2015-11-25 14:22:00 -0500713 return showContextMenuForChildInternal(originalView, 0, 0, false);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800714 }
715
716 @Override
717 public boolean showContextMenuForChild(View originalView, float x, float y) {
Alan Viverette021627e2015-11-25 14:22:00 -0500718 return showContextMenuForChildInternal(originalView, x, y, true);
719 }
720
721 private boolean showContextMenuForChildInternal(View originalView,
722 float x, float y, boolean isPopup) {
723 // Only allow one context menu at a time.
724 if (mWindow.mContextMenuHelper != null) {
725 mWindow.mContextMenuHelper.dismiss();
726 mWindow.mContextMenuHelper = null;
727 }
728
729 // Reuse the context menu builder.
Alan Viverette77fb85e2015-12-14 11:42:44 -0500730 final PhoneWindowMenuCallback callback = mWindow.mContextMenuCallback;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800731 if (mWindow.mContextMenu == null) {
732 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
Alan Viverette77fb85e2015-12-14 11:42:44 -0500733 mWindow.mContextMenu.setCallback(callback);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800734 } else {
735 mWindow.mContextMenu.clearAll();
736 }
737
Alan Viverette021627e2015-11-25 14:22:00 -0500738 final MenuHelper helper;
739 if (isPopup) {
740 helper = mWindow.mContextMenu.showPopup(getContext(), originalView, x, y);
741 } else {
742 helper = mWindow.mContextMenu.showDialog(originalView, originalView.getWindowToken());
Wale Ogunwale8804af22015-11-17 09:18:15 -0800743 }
Alan Viverette021627e2015-11-25 14:22:00 -0500744
Alan Viverette9084d222015-12-16 09:56:37 -0500745 if (helper != null) {
746 // If it's a dialog, the callback needs to handle showing
747 // sub-menus. Either way, the callback is required for propagating
748 // selection to Context.onContextMenuItemSelected().
749 callback.setShowDialogForSubmenu(!isPopup);
750 helper.setPresenterCallback(callback);
751 }
Alan Viverette021627e2015-11-25 14:22:00 -0500752
753 mWindow.mContextMenuHelper = helper;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800754 return helper != null;
755 }
756
757 @Override
758 public ActionMode startActionModeForChild(View originalView,
759 ActionMode.Callback callback) {
760 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
761 }
762
763 @Override
764 public ActionMode startActionModeForChild(
765 View child, ActionMode.Callback callback, int type) {
766 return startActionMode(child, callback, type);
767 }
768
769 @Override
770 public ActionMode startActionMode(ActionMode.Callback callback) {
771 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
772 }
773
774 @Override
775 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
776 return startActionMode(this, callback, type);
777 }
778
779 private ActionMode startActionMode(
780 View originatingView, ActionMode.Callback callback, int type) {
781 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
782 ActionMode mode = null;
783 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
784 try {
785 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
786 } catch (AbstractMethodError ame) {
787 // Older apps might not implement the typed version of this method.
788 if (type == ActionMode.TYPE_PRIMARY) {
789 try {
790 mode = mWindow.getCallback().onWindowStartingActionMode(
791 wrappedCallback);
792 } catch (AbstractMethodError ame2) {
793 // Older apps might not implement this callback method at all.
794 }
795 }
796 }
797 }
798 if (mode != null) {
799 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
800 cleanupPrimaryActionMode();
801 mPrimaryActionMode = mode;
802 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
803 if (mFloatingActionMode != null) {
804 mFloatingActionMode.finish();
805 }
806 mFloatingActionMode = mode;
807 }
808 } else {
809 mode = createActionMode(type, wrappedCallback, originatingView);
810 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
811 setHandledActionMode(mode);
812 } else {
813 mode = null;
814 }
815 }
816 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
817 try {
818 mWindow.getCallback().onActionModeStarted(mode);
819 } catch (AbstractMethodError ame) {
820 // Older apps might not implement this callback method.
821 }
822 }
823 return mode;
824 }
825
826 private void cleanupPrimaryActionMode() {
827 if (mPrimaryActionMode != null) {
828 mPrimaryActionMode.finish();
829 mPrimaryActionMode = null;
830 }
831 if (mPrimaryActionModeView != null) {
832 mPrimaryActionModeView.killMode();
833 }
834 }
835
836 private void cleanupFloatingActionModeViews() {
837 if (mFloatingToolbar != null) {
838 mFloatingToolbar.dismiss();
839 mFloatingToolbar = null;
840 }
841 if (mFloatingActionModeOriginatingView != null) {
842 if (mFloatingToolbarPreDrawListener != null) {
843 mFloatingActionModeOriginatingView.getViewTreeObserver()
844 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
845 mFloatingToolbarPreDrawListener = null;
846 }
847 mFloatingActionModeOriginatingView = null;
848 }
849 }
850
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800851 void startChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800852 mChanging = true;
853 }
854
Wale Ogunwale62a91d62015-11-18 11:44:10 -0800855 void finishChanging() {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800856 mChanging = false;
857 drawableChanged();
858 }
859
860 public void setWindowBackground(Drawable drawable) {
861 if (getBackground() != drawable) {
862 setBackgroundDrawable(drawable);
863 if (drawable != null) {
864 drawable.getPadding(mBackgroundPadding);
865 } else {
866 mBackgroundPadding.setEmpty();
867 }
868 drawableChanged();
869 }
870 }
871
872 public void setWindowFrame(Drawable drawable) {
873 if (getForeground() != drawable) {
874 setForeground(drawable);
875 if (drawable != null) {
876 drawable.getPadding(mFramePadding);
877 } else {
878 mFramePadding.setEmpty();
879 }
880 drawableChanged();
881 }
882 }
883
884 @Override
885 public void onWindowSystemUiVisibilityChanged(int visible) {
886 updateColorViews(null /* insets */, true /* animate */);
887 }
888
889 @Override
890 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
Filip Gruszczynskiadf7b5e2016-01-27 16:23:05 -0800891 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
892 mFloatingInsets.setEmpty();
893 if ((attrs.flags & FLAG_FULLSCREEN) == 0) {
894 // For dialog windows we want to make sure they don't go over the status bar or nav bar.
895 // We consume the system insets and we will reuse them later during the measure phase.
896 // We allow the app to ignore this and handle insets itself by using FLAG_FULLSCREEN.
897 if (attrs.height == WindowManager.LayoutParams.WRAP_CONTENT) {
898 mFloatingInsets.top = insets.getSystemWindowInsetTop();
899 mFloatingInsets.bottom = insets.getSystemWindowInsetBottom();
900 insets = insets.replaceSystemWindowInsets(insets.getSystemWindowInsetLeft(), 0,
901 insets.getSystemWindowInsetRight(), 0);
902 }
903 if (mWindow.getAttributes().width == WindowManager.LayoutParams.WRAP_CONTENT) {
904 mFloatingInsets.left = insets.getSystemWindowInsetTop();
905 mFloatingInsets.right = insets.getSystemWindowInsetBottom();
906 insets = insets.replaceSystemWindowInsets(0, insets.getSystemWindowInsetTop(),
907 0, insets.getSystemWindowInsetBottom());
908 }
909 }
Wale Ogunwale8804af22015-11-17 09:18:15 -0800910 mFrameOffsets.set(insets.getSystemWindowInsets());
911 insets = updateColorViews(insets, true /* animate */);
912 insets = updateStatusGuard(insets);
913 updateNavigationGuard(insets);
914 if (getForeground() != null) {
915 drawableChanged();
916 }
917 return insets;
918 }
919
920 @Override
921 public boolean isTransitionGroup() {
922 return false;
923 }
924
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800925 static int getColorViewTopInset(int stableTop, int systemTop) {
926 return Math.min(stableTop, systemTop);
927 }
928
929 static int getColorViewBottomInset(int stableBottom, int systemBottom) {
930 return Math.min(stableBottom, systemBottom);
931 }
932
933 static int getColorViewRightInset(int stableRight, int systemRight) {
934 return Math.min(stableRight, systemRight);
935 }
936
937 static boolean isNavBarToRightEdge(int bottomInset, int rightInset) {
938 return bottomInset == 0 && rightInset > 0;
939 }
940
941 static int getNavBarSize(int bottomInset, int rightInset) {
942 return isNavBarToRightEdge(bottomInset, rightInset) ? rightInset : bottomInset;
943 }
944
Wale Ogunwale8804af22015-11-17 09:18:15 -0800945 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
946 WindowManager.LayoutParams attrs = mWindow.getAttributes();
947 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
948
949 if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
950 boolean disallowAnimate = !isLaidOut();
951 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
952 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
953 mLastWindowFlags = attrs.flags;
954
955 if (insets != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800956 mLastTopInset = getColorViewTopInset(insets.getStableInsetTop(),
Wale Ogunwale8804af22015-11-17 09:18:15 -0800957 insets.getSystemWindowInsetTop());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800958 mLastBottomInset = getColorViewBottomInset(insets.getStableInsetBottom(),
Wale Ogunwale8804af22015-11-17 09:18:15 -0800959 insets.getSystemWindowInsetBottom());
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800960 mLastRightInset = getColorViewRightInset(insets.getStableInsetRight(),
Wale Ogunwale8804af22015-11-17 09:18:15 -0800961 insets.getSystemWindowInsetRight());
962
963 // Don't animate if the presence of stable insets has changed, because that
964 // indicates that the window was either just added and received them for the
965 // first time, or the window size or position has changed.
966 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
967 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
968 mLastHasTopStableInset = hasTopStableInset;
969
970 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
971 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
972 mLastHasBottomStableInset = hasBottomStableInset;
973
974 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
975 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
976 mLastHasRightStableInset = hasRightStableInset;
977 }
978
Jorim Jaggi9511b0f2016-01-29 19:12:44 -0800979 boolean navBarToRightEdge = isNavBarToRightEdge(mLastBottomInset, mLastRightInset);
980 int navBarSize = getNavBarSize(mLastBottomInset, mLastRightInset);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800981 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
982 mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800983 0 /* rightInset */, animate && !disallowAnimate, false /* force */);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800984
985 boolean statusBarNeedsRightInset = navBarToRightEdge
986 && mNavigationColorViewState.present;
987 int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
Jorim Jaggi4fa78922015-11-30 17:13:56 -0800988 updateColorViewInt(mStatusColorViewState, sysUiVisibility,
989 calculateStatusBarColor(), mLastTopInset,
990 false /* matchVertical */, statusBarRightInset, animate && !disallowAnimate,
991 mForceWindowDrawsStatusBarBackground);
Wale Ogunwale8804af22015-11-17 09:18:15 -0800992 }
993
994 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
995 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
996 // explicitly asked for it.
997
998 boolean consumingNavBar =
999 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1000 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
1001 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
1002
1003 int consumedRight = consumingNavBar ? mLastRightInset : 0;
1004 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
1005
1006 if (mContentRoot != null
1007 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
1008 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
1009 if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
1010 lp.rightMargin = consumedRight;
1011 lp.bottomMargin = consumedBottom;
1012 mContentRoot.setLayoutParams(lp);
1013
1014 if (insets == null) {
1015 // The insets have changed, but we're not currently in the process
1016 // of dispatching them.
1017 requestApplyInsets();
1018 }
1019 }
1020 if (insets != null) {
1021 insets = insets.replaceSystemWindowInsets(
1022 insets.getSystemWindowInsetLeft(),
1023 insets.getSystemWindowInsetTop(),
1024 insets.getSystemWindowInsetRight() - consumedRight,
1025 insets.getSystemWindowInsetBottom() - consumedBottom);
1026 }
1027 }
1028
1029 if (insets != null) {
1030 insets = insets.consumeStableInsets();
1031 }
1032 return insets;
1033 }
1034
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001035 private int calculateStatusBarColor() {
1036 int flags = mWindow.getAttributes().flags;
1037 return (flags & FLAG_TRANSLUCENT_STATUS) != 0 ? mSemiTransparentStatusBarColor
1038 : (flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0 ? mWindow.mStatusBarColor
1039 : Color.BLACK;
1040 }
1041
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001042 private int getCurrentColor(ColorViewState state) {
1043 if (state.visible) {
1044 return state.color;
1045 } else {
1046 return 0;
1047 }
1048 }
1049
Wale Ogunwale8804af22015-11-17 09:18:15 -08001050 /**
1051 * Update a color view
1052 *
1053 * @param state the color view to update.
1054 * @param sysUiVis the current systemUiVisibility to apply.
1055 * @param color the current color to apply.
1056 * @param size the current size in the non-parent-matching dimension.
1057 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
1058 * horizontal edge,
1059 * @param rightMargin rightMargin for the color view.
1060 * @param animate if true, the change will be animated.
1061 */
1062 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001063 int size, boolean verticalBar, int rightMargin, boolean animate, boolean force) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001064 state.present = (sysUiVis & state.systemUiHideFlag) == 0
Wale Ogunwale8804af22015-11-17 09:18:15 -08001065 && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001066 && ((mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
1067 || force);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001068 boolean show = state.present
1069 && (color & Color.BLACK) != 0
Jorim Jaggi4fa78922015-11-30 17:13:56 -08001070 && ((mWindow.getAttributes().flags & state.translucentFlag) == 0 || force);
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001071 boolean showView = show && !isResizing() && size > 0;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001072
1073 boolean visibilityChanged = false;
1074 View view = state.view;
1075
1076 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
1077 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
1078 int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
1079
1080 if (view == null) {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001081 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001082 state.view = view = new View(mContext);
1083 view.setBackgroundColor(color);
1084 view.setTransitionName(state.transitionName);
1085 view.setId(state.id);
1086 visibilityChanged = true;
1087 view.setVisibility(INVISIBLE);
1088 state.targetVisibility = VISIBLE;
1089
1090 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
1091 resolvedGravity);
1092 lp.rightMargin = rightMargin;
1093 addView(view, lp);
1094 updateColorViewTranslations();
1095 }
1096 } else {
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001097 int vis = showView ? VISIBLE : INVISIBLE;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001098 visibilityChanged = state.targetVisibility != vis;
1099 state.targetVisibility = vis;
1100 LayoutParams lp = (LayoutParams) view.getLayoutParams();
1101 if (lp.height != resolvedHeight || lp.width != resolvedWidth
1102 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
1103 lp.height = resolvedHeight;
1104 lp.width = resolvedWidth;
1105 lp.gravity = resolvedGravity;
1106 lp.rightMargin = rightMargin;
1107 view.setLayoutParams(lp);
1108 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001109 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001110 view.setBackgroundColor(color);
1111 }
1112 }
1113 if (visibilityChanged) {
1114 view.animate().cancel();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001115 if (animate && !isResizing()) {
1116 if (showView) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001117 if (view.getVisibility() != VISIBLE) {
1118 view.setVisibility(VISIBLE);
1119 view.setAlpha(0.0f);
1120 }
1121 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1122 setDuration(mBarEnterExitDuration);
1123 } else {
1124 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1125 .setDuration(mBarEnterExitDuration)
1126 .withEndAction(new Runnable() {
1127 @Override
1128 public void run() {
1129 state.view.setAlpha(1.0f);
1130 state.view.setVisibility(INVISIBLE);
1131 }
1132 });
1133 }
1134 } else {
1135 view.setAlpha(1.0f);
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001136 view.setVisibility(showView ? VISIBLE : INVISIBLE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001137 }
1138 }
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001139 state.visible = show;
1140 state.color = color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001141 }
1142
1143 private void updateColorViewTranslations() {
1144 // Put the color views back in place when they get moved off the screen
1145 // due to the the ViewRootImpl panning.
1146 int rootScrollY = mRootScrollY;
1147 if (mStatusColorViewState.view != null) {
1148 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1149 }
1150 if (mNavigationColorViewState.view != null) {
1151 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1152 }
1153 }
1154
1155 private WindowInsets updateStatusGuard(WindowInsets insets) {
1156 boolean showStatusGuard = false;
1157 // Show the status guard when the non-overlay contextual action bar is showing
1158 if (mPrimaryActionModeView != null) {
1159 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1160 // Insets are magic!
1161 final MarginLayoutParams mlp = (MarginLayoutParams)
1162 mPrimaryActionModeView.getLayoutParams();
1163 boolean mlpChanged = false;
1164 if (mPrimaryActionModeView.isShown()) {
1165 if (mTempRect == null) {
1166 mTempRect = new Rect();
1167 }
1168 final Rect rect = mTempRect;
1169
1170 // If the parent doesn't consume the insets, manually
1171 // apply the default system window insets.
1172 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1173 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1174 if (mlp.topMargin != newMargin) {
1175 mlpChanged = true;
1176 mlp.topMargin = insets.getSystemWindowInsetTop();
1177
1178 if (mStatusGuard == null) {
1179 mStatusGuard = new View(mContext);
1180 mStatusGuard.setBackgroundColor(mContext.getColor(
1181 R.color.input_method_navigation_guard));
1182 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1183 new LayoutParams(LayoutParams.MATCH_PARENT,
1184 mlp.topMargin, Gravity.START | Gravity.TOP));
1185 } else {
1186 final LayoutParams lp = (LayoutParams)
1187 mStatusGuard.getLayoutParams();
1188 if (lp.height != mlp.topMargin) {
1189 lp.height = mlp.topMargin;
1190 mStatusGuard.setLayoutParams(lp);
1191 }
1192 }
1193 }
1194
1195 // The action mode's theme may differ from the app, so
1196 // always show the status guard above it if we have one.
1197 showStatusGuard = mStatusGuard != null;
1198
1199 // We only need to consume the insets if the action
1200 // mode is overlaid on the app content (e.g. it's
1201 // sitting in a FrameLayout, see
1202 // screen_simple_overlay_action_mode.xml).
1203 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1204 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1205 insets = insets.consumeSystemWindowInsets(
1206 false, nonOverlay && showStatusGuard /* top */, false, false);
1207 } else {
1208 // reset top margin
1209 if (mlp.topMargin != 0) {
1210 mlpChanged = true;
1211 mlp.topMargin = 0;
1212 }
1213 }
1214 if (mlpChanged) {
1215 mPrimaryActionModeView.setLayoutParams(mlp);
1216 }
1217 }
1218 }
1219 if (mStatusGuard != null) {
1220 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1221 }
1222 return insets;
1223 }
1224
1225 private void updateNavigationGuard(WindowInsets insets) {
1226 // IMEs lay out below the nav bar, but the content view must not (for back compat)
1227 if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
1228 // prevent the content view from including the nav bar height
1229 if (mWindow.mContentParent != null) {
1230 if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
1231 MarginLayoutParams mlp =
1232 (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
1233 mlp.bottomMargin = insets.getSystemWindowInsetBottom();
1234 mWindow.mContentParent.setLayoutParams(mlp);
1235 }
1236 }
1237 // position the navigation guard view, creating it if necessary
1238 if (mNavigationGuard == null) {
1239 mNavigationGuard = new View(mContext);
1240 mNavigationGuard.setBackgroundColor(mContext.getColor(
1241 R.color.input_method_navigation_guard));
1242 addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
1243 new LayoutParams(LayoutParams.MATCH_PARENT,
1244 insets.getSystemWindowInsetBottom(),
1245 Gravity.START | Gravity.BOTTOM));
1246 } else {
1247 LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
1248 lp.height = insets.getSystemWindowInsetBottom();
1249 mNavigationGuard.setLayoutParams(lp);
1250 }
Seigo Nonaka0a9d1ea2015-11-18 22:29:06 +09001251 updateNavigationGuardColor();
1252 }
1253 }
1254
1255 void updateNavigationGuardColor() {
1256 if (mNavigationGuard != null) {
1257 // Make navigation bar guard invisible if the transparent color is specified.
1258 // Only TRANSPARENT is sufficient for hiding the navigation bar if the no software
1259 // keyboard is shown by IMS.
1260 mNavigationGuard.setVisibility(mWindow.getNavigationBarColor() == Color.TRANSPARENT ?
1261 View.INVISIBLE : View.VISIBLE);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001262 }
1263 }
1264
1265 private void drawableChanged() {
1266 if (mChanging) {
1267 return;
1268 }
1269
1270 setPadding(mFramePadding.left + mBackgroundPadding.left,
1271 mFramePadding.top + mBackgroundPadding.top,
1272 mFramePadding.right + mBackgroundPadding.right,
1273 mFramePadding.bottom + mBackgroundPadding.bottom);
1274 requestLayout();
1275 invalidate();
1276
1277 int opacity = PixelFormat.OPAQUE;
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001278 if (StackId.hasWindowShadow(mStackId)) {
Wale Ogunwale8804af22015-11-17 09:18:15 -08001279 // If the window has a shadow, it must be translucent.
1280 opacity = PixelFormat.TRANSLUCENT;
1281 } else{
1282 // Note: If there is no background, we will assume opaque. The
1283 // common case seems to be that an application sets there to be
1284 // no background so it can draw everything itself. For that,
1285 // we would like to assume OPAQUE and let the app force it to
1286 // the slower TRANSLUCENT mode if that is really what it wants.
1287 Drawable bg = getBackground();
1288 Drawable fg = getForeground();
1289 if (bg != null) {
1290 if (fg == null) {
1291 opacity = bg.getOpacity();
1292 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1293 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1294 // If the frame padding is zero, then we can be opaque
1295 // if either the frame -or- the background is opaque.
1296 int fop = fg.getOpacity();
1297 int bop = bg.getOpacity();
1298 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001299 Log.v(mLogTag, "Background opacity: " + bop + ", Frame opacity: " + fop);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001300 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1301 opacity = PixelFormat.OPAQUE;
1302 } else if (fop == PixelFormat.UNKNOWN) {
1303 opacity = bop;
1304 } else if (bop == PixelFormat.UNKNOWN) {
1305 opacity = fop;
1306 } else {
1307 opacity = Drawable.resolveOpacity(fop, bop);
1308 }
1309 } else {
1310 // For now we have to assume translucent if there is a
1311 // frame with padding... there is no way to tell if the
1312 // frame and background together will draw all pixels.
1313 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001314 Log.v(mLogTag, "Padding: " + mFramePadding);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001315 opacity = PixelFormat.TRANSLUCENT;
1316 }
1317 }
1318 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001319 Log.v(mLogTag, "Background: " + bg + ", Frame: " + fg);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001320 }
1321
1322 if (false)
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001323 Log.v(mLogTag, "Selected default opacity: " + opacity);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001324
1325 mDefaultOpacity = opacity;
1326 if (mFeatureId < 0) {
1327 mWindow.setDefaultWindowFormat(opacity);
1328 }
1329 }
1330
1331 @Override
1332 public void onWindowFocusChanged(boolean hasWindowFocus) {
1333 super.onWindowFocusChanged(hasWindowFocus);
1334
1335 // If the user is chording a menu shortcut, release the chord since
1336 // this window lost focus
1337 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1338 && mWindow.mPanelChordingKey != 0) {
1339 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1340 }
1341
1342 final Window.Callback cb = mWindow.getCallback();
1343 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1344 cb.onWindowFocusChanged(hasWindowFocus);
1345 }
1346
1347 if (mPrimaryActionMode != null) {
1348 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1349 }
1350 if (mFloatingActionMode != null) {
1351 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1352 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001353
1354 updateElevation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001355 }
1356
1357 @Override
1358 protected void onAttachedToWindow() {
1359 super.onAttachedToWindow();
1360
1361 final Window.Callback cb = mWindow.getCallback();
1362 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1363 cb.onAttachedToWindow();
1364 }
1365
1366 if (mFeatureId == -1) {
1367 /*
1368 * The main window has been attached, try to restore any panels
1369 * that may have been open before. This is called in cases where
1370 * an activity is being killed for configuration change and the
1371 * menu was open. When the activity is recreated, the menu
1372 * should be shown again.
1373 */
1374 mWindow.openPanelsAfterRestore();
1375 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001376
1377 if (!mWindowResizeCallbacksAdded) {
1378 // If there is no window callback installed there was no window set before. Set it now.
1379 // Note that our ViewRootImpl object will not change.
1380 getViewRootImpl().addWindowCallbacks(this);
1381 mWindowResizeCallbacksAdded = true;
1382 } else if (mBackdropFrameRenderer != null) {
1383 // We are resizing and this call happened due to a configuration change. Tell the
1384 // renderer about it.
1385 mBackdropFrameRenderer.onConfigurationChange();
1386 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001387 }
1388
1389 @Override
1390 protected void onDetachedFromWindow() {
1391 super.onDetachedFromWindow();
1392
1393 final Window.Callback cb = mWindow.getCallback();
1394 if (cb != null && mFeatureId < 0) {
1395 cb.onDetachedFromWindow();
1396 }
1397
1398 if (mWindow.mDecorContentParent != null) {
1399 mWindow.mDecorContentParent.dismissPopups();
1400 }
1401
1402 if (mPrimaryActionModePopup != null) {
1403 removeCallbacks(mShowPrimaryActionModePopup);
1404 if (mPrimaryActionModePopup.isShowing()) {
1405 mPrimaryActionModePopup.dismiss();
1406 }
1407 mPrimaryActionModePopup = null;
1408 }
1409 if (mFloatingToolbar != null) {
1410 mFloatingToolbar.dismiss();
1411 mFloatingToolbar = null;
1412 }
1413
1414 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1415 if (st != null && st.menu != null && mFeatureId < 0) {
1416 st.menu.close();
1417 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001418
1419 if (mWindowResizeCallbacksAdded) {
1420 getViewRootImpl().removeWindowCallbacks(this);
1421 mWindowResizeCallbacksAdded = false;
1422 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001423 }
1424
1425 @Override
1426 public void onCloseSystemDialogs(String reason) {
1427 if (mFeatureId >= 0) {
1428 mWindow.closeAllPanels();
1429 }
1430 }
1431
1432 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1433 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1434 }
1435
1436 public InputQueue.Callback willYouTakeTheInputQueue() {
1437 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1438 }
1439
1440 public void setSurfaceType(int type) {
1441 mWindow.setType(type);
1442 }
1443
1444 public void setSurfaceFormat(int format) {
1445 mWindow.setFormat(format);
1446 }
1447
1448 public void setSurfaceKeepScreenOn(boolean keepOn) {
1449 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1450 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1451 }
1452
1453 @Override
1454 public void onRootViewScrollYChanged(int rootScrollY) {
1455 mRootScrollY = rootScrollY;
1456 updateColorViewTranslations();
1457 }
1458
1459 private ActionMode createActionMode(
1460 int type, ActionMode.Callback2 callback, View originatingView) {
1461 switch (type) {
1462 case ActionMode.TYPE_PRIMARY:
1463 default:
1464 return createStandaloneActionMode(callback);
1465 case ActionMode.TYPE_FLOATING:
1466 return createFloatingActionMode(originatingView, callback);
1467 }
1468 }
1469
1470 private void setHandledActionMode(ActionMode mode) {
1471 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1472 setHandledPrimaryActionMode(mode);
1473 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1474 setHandledFloatingActionMode(mode);
1475 }
1476 }
1477
1478 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1479 endOnGoingFadeAnimation();
1480 cleanupPrimaryActionMode();
1481 if (mPrimaryActionModeView == null) {
1482 if (mWindow.isFloating()) {
1483 // Use the action bar theme.
1484 final TypedValue outValue = new TypedValue();
1485 final Resources.Theme baseTheme = mContext.getTheme();
1486 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1487
1488 final Context actionBarContext;
1489 if (outValue.resourceId != 0) {
1490 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1491 actionBarTheme.setTo(baseTheme);
1492 actionBarTheme.applyStyle(outValue.resourceId, true);
1493
1494 actionBarContext = new ContextThemeWrapper(mContext, 0);
1495 actionBarContext.getTheme().setTo(actionBarTheme);
1496 } else {
1497 actionBarContext = mContext;
1498 }
1499
1500 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1501 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1502 R.attr.actionModePopupWindowStyle);
1503 mPrimaryActionModePopup.setWindowLayoutType(
1504 WindowManager.LayoutParams.TYPE_APPLICATION);
1505 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1506 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1507
1508 actionBarContext.getTheme().resolveAttribute(
1509 R.attr.actionBarSize, outValue, true);
1510 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1511 actionBarContext.getResources().getDisplayMetrics());
1512 mPrimaryActionModeView.setContentHeight(height);
1513 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1514 mShowPrimaryActionModePopup = new Runnable() {
1515 public void run() {
1516 mPrimaryActionModePopup.showAtLocation(
1517 mPrimaryActionModeView.getApplicationWindowToken(),
1518 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1519 endOnGoingFadeAnimation();
1520 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1521 0f, 1f);
1522 mFadeAnim.addListener(new Animator.AnimatorListener() {
1523 @Override
1524 public void onAnimationStart(Animator animation) {
1525 mPrimaryActionModeView.setVisibility(VISIBLE);
1526 }
1527
1528 @Override
1529 public void onAnimationEnd(Animator animation) {
1530 mPrimaryActionModeView.setAlpha(1f);
1531 mFadeAnim = null;
1532 }
1533
1534 @Override
1535 public void onAnimationCancel(Animator animation) {
1536
1537 }
1538
1539 @Override
1540 public void onAnimationRepeat(Animator animation) {
1541
1542 }
1543 });
1544 mFadeAnim.start();
1545 }
1546 };
1547 } else {
1548 ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
1549 if (stub != null) {
1550 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1551 }
1552 }
1553 }
1554 if (mPrimaryActionModeView != null) {
1555 mPrimaryActionModeView.killMode();
1556 ActionMode mode = new StandaloneActionMode(
1557 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1558 callback, mPrimaryActionModePopup == null);
1559 return mode;
1560 }
1561 return null;
1562 }
1563
1564 private void endOnGoingFadeAnimation() {
1565 if (mFadeAnim != null) {
1566 mFadeAnim.end();
1567 }
1568 }
1569
1570 private void setHandledPrimaryActionMode(ActionMode mode) {
1571 endOnGoingFadeAnimation();
1572 mPrimaryActionMode = mode;
1573 mPrimaryActionMode.invalidate();
1574 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1575 if (mPrimaryActionModePopup != null) {
1576 post(mShowPrimaryActionModePopup);
1577 } else {
1578 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1579 mFadeAnim.addListener(new Animator.AnimatorListener() {
1580 @Override
1581 public void onAnimationStart(Animator animation) {
1582 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1583 }
1584
1585 @Override
1586 public void onAnimationEnd(Animator animation) {
1587 mPrimaryActionModeView.setAlpha(1f);
1588 mFadeAnim = null;
1589 }
1590
1591 @Override
1592 public void onAnimationCancel(Animator animation) {
1593
1594 }
1595
1596 @Override
1597 public void onAnimationRepeat(Animator animation) {
1598
1599 }
1600 });
1601 mFadeAnim.start();
1602 }
1603 mPrimaryActionModeView.sendAccessibilityEvent(
1604 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1605 }
1606
1607 private ActionMode createFloatingActionMode(
1608 View originatingView, ActionMode.Callback2 callback) {
1609 if (mFloatingActionMode != null) {
1610 mFloatingActionMode.finish();
1611 }
1612 cleanupFloatingActionModeViews();
1613 final FloatingActionMode mode =
1614 new FloatingActionMode(mContext, callback, originatingView);
1615 mFloatingActionModeOriginatingView = originatingView;
1616 mFloatingToolbarPreDrawListener =
1617 new ViewTreeObserver.OnPreDrawListener() {
1618 @Override
1619 public boolean onPreDraw() {
1620 mode.updateViewLocationInWindow();
1621 return true;
1622 }
1623 };
1624 return mode;
1625 }
1626
1627 private void setHandledFloatingActionMode(ActionMode mode) {
1628 mFloatingActionMode = mode;
1629 mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
1630 ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
1631 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1632 mFloatingActionModeOriginatingView.getViewTreeObserver()
1633 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1634 }
1635
1636 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001637 * Informs the decor if the caption is attached and visible.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001638 * @param attachedAndVisible true when the decor is visible.
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001639 * Note that this will even be called if there is no caption.
Wale Ogunwale8804af22015-11-17 09:18:15 -08001640 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001641 void enableCaption(boolean attachedAndVisible) {
1642 if (mHasCaption != attachedAndVisible) {
1643 mHasCaption = attachedAndVisible;
Wale Ogunwale8804af22015-11-17 09:18:15 -08001644 if (getForeground() != null) {
1645 drawableChanged();
1646 }
1647 }
1648 }
1649
Wale Ogunwale8804af22015-11-17 09:18:15 -08001650 void setWindow(PhoneWindow phoneWindow) {
1651 mWindow = phoneWindow;
1652 Context context = getContext();
1653 if (context instanceof DecorContext) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001654 DecorContext decorContext = (DecorContext) context;
1655 decorContext.setPhoneWindow(mWindow);
1656 }
1657 }
1658
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08001659 @Override
1660 protected void onConfigurationChanged(Configuration newConfig) {
1661 super.onConfigurationChanged(newConfig);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001662 int workspaceId = getStackId();
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001663 if (mStackId != workspaceId) {
1664 mStackId = workspaceId;
1665 if (mDecorCaptionView == null && StackId.hasWindowDecor(mStackId)) {
1666 // Configuration now requires a caption.
1667 final LayoutInflater inflater = mWindow.getLayoutInflater();
1668 mDecorCaptionView = createDecorCaptionView(inflater);
1669 if (mDecorCaptionView != null) {
1670 if (mDecorCaptionView.getParent() == null) {
1671 addView(mDecorCaptionView, 0,
1672 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1673 }
1674 removeView(mContentRoot);
1675 mDecorCaptionView.addView(mContentRoot,
1676 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
1677 }
1678 } else if (mDecorCaptionView != null) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001679 // We might have to change the kind of surface before we do anything else.
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001680 mDecorCaptionView.onConfigurationChanged(StackId.hasWindowDecor(mStackId));
1681 enableCaption(StackId.hasWindowDecor(workspaceId));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001682 }
1683 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001684 updateAvailableWidth();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001685 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001686 }
1687
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001688 void onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001689 mStackId = getStackId();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001690
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001691 if (mBackdropFrameRenderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001692 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001693 mBackdropFrameRenderer.onResourcesLoaded(
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001694 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001695 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1696 getCurrentColor(mNavigationColorViewState));
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001697 }
1698
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001699 mDecorCaptionView = createDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001700 final View root = inflater.inflate(layoutResource, null);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001701 if (mDecorCaptionView != null) {
1702 if (mDecorCaptionView.getParent() == null) {
1703 addView(mDecorCaptionView,
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001704 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1705 }
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001706 mDecorCaptionView.addView(root,
Filip Gruszczynski63250652015-11-18 14:43:01 -08001707 new ViewGroup.MarginLayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001708 } else {
1709 addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1710 }
1711 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001712 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001713 }
1714
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001715 private void loadBackgroundDrawablesIfNeeded() {
1716 if (mResizingBackgroundDrawable == null) {
1717 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
1718 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
1719 }
1720 if (mCaptionBackgroundDrawable == null) {
1721 mCaptionBackgroundDrawable = getContext().getDrawable(
1722 R.drawable.decor_caption_title_focused);
1723 }
1724 }
1725
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001726 // Free floating overlapping windows require a caption.
1727 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001728 DecorCaptionView decorCaptionView = null;
1729 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001730 View view = getChildAt(i);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001731 if (view instanceof DecorCaptionView) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001732 // The decor was most likely saved from a relaunch - so reuse it.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001733 decorCaptionView = (DecorCaptionView) view;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001734 removeViewAt(i);
1735 }
1736 }
1737 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001738 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001739 attrs.type == TYPE_APPLICATION;
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001740 // Only a non floating application window on one of the allowed workspaces can get a caption
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001741 if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001742 // Dependent on the brightness of the used title we either use the
1743 // dark or the light button frame.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001744 if (decorCaptionView == null) {
1745 decorCaptionView = inflateDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001746 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001747 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001748 } else {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001749 decorCaptionView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001750 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001751
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001752 // Tell the decor if it has a visible caption.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001753 enableCaption(decorCaptionView != null);
1754 return decorCaptionView;
1755 }
1756
1757 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1758 final Context context = getContext();
1759 // We make a copy of the inflater, so it has the right context associated with it.
1760 inflater = inflater.from(context);
1761 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1762 null);
1763 setDecorCaptionShade(context, view);
1764 return view;
1765 }
1766
1767 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1768 final int shade = mWindow.getDecorCaptionShade();
1769 switch (shade) {
1770 case DECOR_CAPTION_SHADE_LIGHT:
1771 setLightDecorCaptionShade(view);
1772 break;
1773 case DECOR_CAPTION_SHADE_DARK:
1774 setDarkDecorCaptionShade(view);
1775 break;
1776 default: {
1777 TypedValue value = new TypedValue();
1778 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1779 // We invert the shade depending on brightness of the theme. Dark shade for light
1780 // theme and vice versa. Thanks to this the buttons should be visible on the
1781 // background.
1782 if (Color.luminance(value.data) < 0.5) {
1783 setLightDecorCaptionShade(view);
1784 } else {
1785 setDarkDecorCaptionShade(view);
1786 }
1787 break;
1788 }
1789 }
1790 }
1791
1792 void updateDecorCaptionShade() {
1793 if (mDecorCaptionView != null) {
1794 setDecorCaptionShade(getContext(), mDecorCaptionView);
1795 }
1796 }
1797
1798 private void setLightDecorCaptionShade(DecorCaptionView view) {
1799 view.findViewById(R.id.maximize_window).setBackgroundResource(
1800 R.drawable.decor_maximize_button_light);
1801 view.findViewById(R.id.close_window).setBackgroundResource(
1802 R.drawable.decor_close_button_light);
1803 }
1804
1805 private void setDarkDecorCaptionShade(DecorCaptionView view) {
1806 view.findViewById(R.id.maximize_window).setBackgroundResource(
1807 R.drawable.decor_maximize_button_dark);
1808 view.findViewById(R.id.close_window).setBackgroundResource(
1809 R.drawable.decor_close_button_dark);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001810 }
1811
1812 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001813 * Returns the color used to fill areas the app has not rendered content to yet when the
1814 * user is resizing the window of an activity in multi-window mode.
1815 */
1816 private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001817 final Context context = getContext();
1818
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001819 if (backgroundRes != 0) {
1820 final Drawable drawable = context.getDrawable(backgroundRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001821 if (drawable != null) {
1822 return drawable;
1823 }
1824 }
1825
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001826 if (backgroundFallbackRes != 0) {
1827 final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001828 if (fallbackDrawable != null) {
1829 return fallbackDrawable;
1830 }
1831 }
1832
1833 // We shouldn't really get here as the background fallback should be always available since
1834 // it is defaulted by the system.
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001835 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001836 return null;
1837 }
1838
1839 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001840 * Returns the Id of the stack which contains this window.
1841 * Note that if no stack can be determined - which usually means that it was not
1842 * created for an activity - the fullscreen stack ID will be returned.
1843 * @return Returns the stack id which contains this window.
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001844 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001845 private int getStackId() {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001846 int workspaceId = INVALID_STACK_ID;
1847 final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
1848 if (callback != null) {
1849 try {
1850 workspaceId = callback.getWindowStackId();
1851 } catch (RemoteException ex) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001852 Log.e(mLogTag, "Failed to get the workspace ID of a PhoneWindow.");
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001853 }
1854 }
1855 if (workspaceId == INVALID_STACK_ID) {
1856 return FULLSCREEN_WORKSPACE_STACK_ID;
1857 }
1858 return workspaceId;
1859 }
1860
1861 void clearContentView() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001862 if (mDecorCaptionView != null) {
Filip Gruszczynski63250652015-11-18 14:43:01 -08001863 mDecorCaptionView.removeContentView();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001864 } else {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08001865 // This window doesn't have caption, so we need to remove everything except our views
1866 // we might have added.
1867 for (int i = getChildCount() - 1; i >= 0; i--) {
1868 View v = getChildAt(i);
1869 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
1870 && v != mStatusGuard && v != mNavigationGuard) {
1871 removeViewAt(i);
1872 }
1873 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001874 }
1875 }
1876
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001877 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001878 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
1879 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001880 if (mBackdropFrameRenderer != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001881 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001882 }
1883 }
1884
1885 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001886 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
1887 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001888 if (mWindow.isDestroyed()) {
1889 // If the owner's window is gone, we should not be able to come here anymore.
1890 releaseThreadedRenderer();
1891 return;
1892 }
1893 if (mBackdropFrameRenderer != null) {
1894 return;
1895 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001896 final ThreadedRenderer renderer = getHardwareRenderer();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001897 if (renderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001898 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001899 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001900 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001901 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1902 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
1903 stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001904
1905 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
1906 // If we want to get the shadow shown while resizing, we would need to elevate a new
1907 // element which owns the caption and has the elevation.
1908 updateElevation();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001909
1910 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001911 }
1912 }
1913
1914 @Override
1915 public void onWindowDragResizeEnd() {
1916 releaseThreadedRenderer();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001917 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001918 }
1919
1920 @Override
1921 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
1922 if (mBackdropFrameRenderer == null) {
1923 return false;
1924 }
1925 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
1926 }
1927
1928 @Override
1929 public void onRequestDraw(boolean reportNextDraw) {
1930 if (mBackdropFrameRenderer != null) {
1931 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
1932 } else if (reportNextDraw) {
1933 // If render thread is gone, just report immediately.
1934 if (isAttachedToWindow()) {
1935 getViewRootImpl().reportDrawFinish();
1936 }
1937 }
1938 }
1939
1940 /** Release the renderer thread which is usually done when the user stops resizing. */
1941 private void releaseThreadedRenderer() {
1942 if (mBackdropFrameRenderer != null) {
1943 mBackdropFrameRenderer.releaseRenderer();
1944 mBackdropFrameRenderer = null;
1945 // Bring the shadow back.
1946 updateElevation();
1947 }
1948 }
1949
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001950 private boolean isResizing() {
1951 return mBackdropFrameRenderer != null;
1952 }
1953
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001954 /**
1955 * The elevation gets set for the first time and the framework needs to be informed that
1956 * the surface layer gets created with the shadow size in mind.
1957 */
1958 private void initializeElevation() {
1959 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
1960 mAllowUpdateElevation = false;
1961 updateElevation();
1962 }
1963
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001964 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001965 float elevation = 0;
1966 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
1967 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
1968 // since the shadow is bound to the content size and not the target size.
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001969 if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001970 elevation = hasWindowFocus() ?
1971 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
1972 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
1973 if (!mAllowUpdateElevation) {
1974 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
1975 }
1976 // Convert the DP elevation into physical pixels.
1977 elevation = dipToPx(elevation);
1978 mElevationAdjustedForStack = true;
1979 } else {
1980 mElevationAdjustedForStack = false;
1981 }
1982
1983 // Don't change the elevation if we didn't previously adjust it for the stack it was in
1984 // or it didn't change.
1985 if ((wasAdjustedForStack || mElevationAdjustedForStack)
1986 && getElevation() != elevation) {
1987 mWindow.setElevation(elevation);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001988 }
1989 }
1990
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001991 boolean isShowingCaption() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001992 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001993 }
1994
1995 int getCaptionHeight() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001996 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001997 }
1998
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001999 /**
2000 * Converts a DIP measure into physical pixels.
2001 * @param dip The dip value.
2002 * @return Returns the number of pixels.
2003 */
2004 private float dipToPx(float dip) {
2005 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2006 getResources().getDisplayMetrics());
2007 }
2008
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002009 /**
2010 * Provide an override of the caption background drawable.
2011 */
2012 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2013 mUserCaptionBackgroundDrawable = drawable;
2014 if (mBackdropFrameRenderer != null) {
2015 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2016 }
2017 }
2018
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002019 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2020 if (params == null) {
2021 return "";
2022 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002023 final String[] split = params.getTitle().toString().split("\\.");
2024 if (split.length > 0) {
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002025 return split[split.length - 1];
2026 } else {
2027 return "";
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002028 }
2029 }
2030
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002031 void updateLogTag(WindowManager.LayoutParams params) {
2032 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2033 }
2034
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002035 private void updateAvailableWidth() {
2036 Resources res = getResources();
2037 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2038 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2039 }
2040
Clara Bayarri75e09792015-07-29 16:20:40 +01002041 /**
2042 * @hide
2043 */
2044 @Override
2045 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) {
2046 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2047 if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
2048 try {
2049 mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu);
2050 } catch (AbstractMethodError e) {
2051 // We run into this if the app is using supportlib.
2052 }
2053 }
2054 }
2055
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002056 @Override
2057 public String toString() {
2058 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2059 + getTitleSuffix(mWindow.getAttributes()) + "]";
2060 }
2061
Wale Ogunwale8804af22015-11-17 09:18:15 -08002062 private static class ColorViewState {
2063 View view = null;
2064 int targetVisibility = View.INVISIBLE;
2065 boolean present = false;
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002066 boolean visible;
2067 int color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002068
2069 final int id;
2070 final int systemUiHideFlag;
2071 final int translucentFlag;
2072 final int verticalGravity;
2073 final int horizontalGravity;
2074 final String transitionName;
2075 final int hideWindowFlag;
2076
2077 ColorViewState(int systemUiHideFlag,
2078 int translucentFlag, int verticalGravity, int horizontalGravity,
2079 String transitionName, int id, int hideWindowFlag) {
2080 this.id = id;
2081 this.systemUiHideFlag = systemUiHideFlag;
2082 this.translucentFlag = translucentFlag;
2083 this.verticalGravity = verticalGravity;
2084 this.horizontalGravity = horizontalGravity;
2085 this.transitionName = transitionName;
2086 this.hideWindowFlag = hideWindowFlag;
2087 }
2088 }
2089
2090 /**
2091 * Clears out internal references when the action mode is destroyed.
2092 */
2093 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2094 private final ActionMode.Callback mWrapped;
2095
2096 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2097 mWrapped = wrapped;
2098 }
2099
2100 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2101 return mWrapped.onCreateActionMode(mode, menu);
2102 }
2103
2104 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2105 requestFitSystemWindows();
2106 return mWrapped.onPrepareActionMode(mode, menu);
2107 }
2108
2109 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2110 return mWrapped.onActionItemClicked(mode, item);
2111 }
2112
2113 public void onDestroyActionMode(ActionMode mode) {
2114 mWrapped.onDestroyActionMode(mode);
2115 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2116 >= Build.VERSION_CODES.M;
2117 final boolean isPrimary;
2118 final boolean isFloating;
2119 if (isMncApp) {
2120 isPrimary = mode == mPrimaryActionMode;
2121 isFloating = mode == mFloatingActionMode;
2122 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002123 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002124 + mode + " was not the current primary action mode! Expected "
2125 + mPrimaryActionMode);
2126 }
2127 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002128 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002129 + mode + " was not the current floating action mode! Expected "
2130 + mFloatingActionMode);
2131 }
2132 } else {
2133 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2134 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2135 }
2136 if (isPrimary) {
2137 if (mPrimaryActionModePopup != null) {
2138 removeCallbacks(mShowPrimaryActionModePopup);
2139 }
2140 if (mPrimaryActionModeView != null) {
2141 endOnGoingFadeAnimation();
2142 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2143 1f, 0f);
2144 mFadeAnim.addListener(new Animator.AnimatorListener() {
2145 @Override
2146 public void onAnimationStart(Animator animation) {
2147
2148 }
2149
2150 @Override
2151 public void onAnimationEnd(Animator animation) {
2152 mPrimaryActionModeView.setVisibility(GONE);
2153 if (mPrimaryActionModePopup != null) {
2154 mPrimaryActionModePopup.dismiss();
2155 }
2156 mPrimaryActionModeView.removeAllViews();
2157 mFadeAnim = null;
2158 }
2159
2160 @Override
2161 public void onAnimationCancel(Animator animation) {
2162
2163 }
2164
2165 @Override
2166 public void onAnimationRepeat(Animator animation) {
2167
2168 }
2169 });
2170 mFadeAnim.start();
2171 }
2172
2173 mPrimaryActionMode = null;
2174 } else if (isFloating) {
2175 cleanupFloatingActionModeViews();
2176 mFloatingActionMode = null;
2177 }
2178 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2179 try {
2180 mWindow.getCallback().onActionModeFinished(mode);
2181 } catch (AbstractMethodError ame) {
2182 // Older apps might not implement this callback method.
2183 }
2184 }
2185 requestFitSystemWindows();
2186 }
2187
2188 @Override
2189 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2190 if (mWrapped instanceof ActionMode.Callback2) {
2191 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2192 } else {
2193 super.onGetContentRect(mode, view, outRect);
2194 }
2195 }
2196 }
2197}