blob: b2ae835be358e2ad2be505ab2c40d757209802c3 [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 {
Jorim Jaggi0a13bfd2016-02-04 18:34:50 -08001709
1710 // Put it below the color views.
1711 addView(root, 0, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001712 }
1713 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001714 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001715 }
1716
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001717 private void loadBackgroundDrawablesIfNeeded() {
1718 if (mResizingBackgroundDrawable == null) {
1719 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
1720 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
1721 }
1722 if (mCaptionBackgroundDrawable == null) {
1723 mCaptionBackgroundDrawable = getContext().getDrawable(
1724 R.drawable.decor_caption_title_focused);
1725 }
1726 }
1727
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001728 // Free floating overlapping windows require a caption.
1729 private DecorCaptionView createDecorCaptionView(LayoutInflater inflater) {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001730 DecorCaptionView decorCaptionView = null;
1731 for (int i = getChildCount() - 1; i >= 0 && decorCaptionView == null; i--) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001732 View view = getChildAt(i);
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001733 if (view instanceof DecorCaptionView) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001734 // The decor was most likely saved from a relaunch - so reuse it.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001735 decorCaptionView = (DecorCaptionView) view;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001736 removeViewAt(i);
1737 }
1738 }
1739 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001740 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001741 attrs.type == TYPE_APPLICATION;
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001742 // Only a non floating application window on one of the allowed workspaces can get a caption
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001743 if (!mWindow.isFloating() && isApplication && StackId.hasWindowDecor(mStackId)) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001744 // Dependent on the brightness of the used title we either use the
1745 // dark or the light button frame.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001746 if (decorCaptionView == null) {
1747 decorCaptionView = inflateDecorCaptionView(inflater);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001748 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001749 decorCaptionView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001750 } else {
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001751 decorCaptionView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001752 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001753
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001754 // Tell the decor if it has a visible caption.
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001755 enableCaption(decorCaptionView != null);
1756 return decorCaptionView;
1757 }
1758
1759 private DecorCaptionView inflateDecorCaptionView(LayoutInflater inflater) {
1760 final Context context = getContext();
1761 // We make a copy of the inflater, so it has the right context associated with it.
1762 inflater = inflater.from(context);
1763 final DecorCaptionView view = (DecorCaptionView) inflater.inflate(R.layout.decor_caption,
1764 null);
1765 setDecorCaptionShade(context, view);
1766 return view;
1767 }
1768
1769 private void setDecorCaptionShade(Context context, DecorCaptionView view) {
1770 final int shade = mWindow.getDecorCaptionShade();
1771 switch (shade) {
1772 case DECOR_CAPTION_SHADE_LIGHT:
1773 setLightDecorCaptionShade(view);
1774 break;
1775 case DECOR_CAPTION_SHADE_DARK:
1776 setDarkDecorCaptionShade(view);
1777 break;
1778 default: {
1779 TypedValue value = new TypedValue();
1780 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1781 // We invert the shade depending on brightness of the theme. Dark shade for light
1782 // theme and vice versa. Thanks to this the buttons should be visible on the
1783 // background.
1784 if (Color.luminance(value.data) < 0.5) {
1785 setLightDecorCaptionShade(view);
1786 } else {
1787 setDarkDecorCaptionShade(view);
1788 }
1789 break;
1790 }
1791 }
1792 }
1793
1794 void updateDecorCaptionShade() {
1795 if (mDecorCaptionView != null) {
1796 setDecorCaptionShade(getContext(), mDecorCaptionView);
1797 }
1798 }
1799
1800 private void setLightDecorCaptionShade(DecorCaptionView view) {
1801 view.findViewById(R.id.maximize_window).setBackgroundResource(
1802 R.drawable.decor_maximize_button_light);
1803 view.findViewById(R.id.close_window).setBackgroundResource(
1804 R.drawable.decor_close_button_light);
1805 }
1806
1807 private void setDarkDecorCaptionShade(DecorCaptionView view) {
1808 view.findViewById(R.id.maximize_window).setBackgroundResource(
1809 R.drawable.decor_maximize_button_dark);
1810 view.findViewById(R.id.close_window).setBackgroundResource(
1811 R.drawable.decor_close_button_dark);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001812 }
1813
1814 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001815 * Returns the color used to fill areas the app has not rendered content to yet when the
1816 * user is resizing the window of an activity in multi-window mode.
1817 */
1818 private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001819 final Context context = getContext();
1820
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001821 if (backgroundRes != 0) {
1822 final Drawable drawable = context.getDrawable(backgroundRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001823 if (drawable != null) {
1824 return drawable;
1825 }
1826 }
1827
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001828 if (backgroundFallbackRes != 0) {
1829 final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001830 if (fallbackDrawable != null) {
1831 return fallbackDrawable;
1832 }
1833 }
1834
1835 // We shouldn't really get here as the background fallback should be always available since
1836 // it is defaulted by the system.
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001837 Log.w(mLogTag, "Failed to find background drawable for PhoneWindow=" + mWindow);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001838 return null;
1839 }
1840
1841 /**
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001842 * Returns the Id of the stack which contains this window.
1843 * Note that if no stack can be determined - which usually means that it was not
1844 * created for an activity - the fullscreen stack ID will be returned.
1845 * @return Returns the stack id which contains this window.
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001846 **/
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001847 private int getStackId() {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001848 int workspaceId = INVALID_STACK_ID;
1849 final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
1850 if (callback != null) {
1851 try {
1852 workspaceId = callback.getWindowStackId();
1853 } catch (RemoteException ex) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08001854 Log.e(mLogTag, "Failed to get the workspace ID of a PhoneWindow.");
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001855 }
1856 }
1857 if (workspaceId == INVALID_STACK_ID) {
1858 return FULLSCREEN_WORKSPACE_STACK_ID;
1859 }
1860 return workspaceId;
1861 }
1862
1863 void clearContentView() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001864 if (mDecorCaptionView != null) {
Filip Gruszczynski63250652015-11-18 14:43:01 -08001865 mDecorCaptionView.removeContentView();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001866 } else {
Jorim Jaggi6e0ce282015-12-01 15:19:49 -08001867 // This window doesn't have caption, so we need to remove everything except our views
1868 // we might have added.
1869 for (int i = getChildCount() - 1; i >= 0; i--) {
1870 View v = getChildAt(i);
1871 if (v != mStatusColorViewState.view && v != mNavigationColorViewState.view
1872 && v != mStatusGuard && v != mNavigationGuard) {
1873 removeViewAt(i);
1874 }
1875 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001876 }
1877 }
1878
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001879 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001880 public void onWindowSizeIsChanging(Rect newBounds, boolean fullscreen, Rect systemInsets,
1881 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001882 if (mBackdropFrameRenderer != null) {
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001883 mBackdropFrameRenderer.setTargetRect(newBounds, fullscreen, systemInsets, stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001884 }
1885 }
1886
1887 @Override
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001888 public void onWindowDragResizeStart(Rect initialBounds, boolean fullscreen, Rect systemInsets,
1889 Rect stableInsets) {
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001890 if (mWindow.isDestroyed()) {
1891 // If the owner's window is gone, we should not be able to come here anymore.
1892 releaseThreadedRenderer();
1893 return;
1894 }
1895 if (mBackdropFrameRenderer != null) {
1896 return;
1897 }
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08001898 final ThreadedRenderer renderer = getHardwareRenderer();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001899 if (renderer != null) {
Filip Gruszczynskia40fd092016-01-07 16:38:11 -08001900 loadBackgroundDrawablesIfNeeded();
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001901 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001902 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable,
Jorim Jaggi9511b0f2016-01-29 19:12:44 -08001903 mUserCaptionBackgroundDrawable, getCurrentColor(mStatusColorViewState),
1904 getCurrentColor(mNavigationColorViewState), fullscreen, systemInsets,
1905 stableInsets);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001906
1907 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
1908 // If we want to get the shadow shown while resizing, we would need to elevate a new
1909 // element which owns the caption and has the elevation.
1910 updateElevation();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001911
1912 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001913 }
1914 }
1915
1916 @Override
1917 public void onWindowDragResizeEnd() {
1918 releaseThreadedRenderer();
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001919 updateColorViews(null /* insets */, false);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001920 }
1921
1922 @Override
1923 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
1924 if (mBackdropFrameRenderer == null) {
1925 return false;
1926 }
1927 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
1928 }
1929
1930 @Override
1931 public void onRequestDraw(boolean reportNextDraw) {
1932 if (mBackdropFrameRenderer != null) {
1933 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
1934 } else if (reportNextDraw) {
1935 // If render thread is gone, just report immediately.
1936 if (isAttachedToWindow()) {
1937 getViewRootImpl().reportDrawFinish();
1938 }
1939 }
1940 }
1941
1942 /** Release the renderer thread which is usually done when the user stops resizing. */
1943 private void releaseThreadedRenderer() {
1944 if (mBackdropFrameRenderer != null) {
1945 mBackdropFrameRenderer.releaseRenderer();
1946 mBackdropFrameRenderer = null;
1947 // Bring the shadow back.
1948 updateElevation();
1949 }
1950 }
1951
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08001952 private boolean isResizing() {
1953 return mBackdropFrameRenderer != null;
1954 }
1955
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001956 /**
1957 * The elevation gets set for the first time and the framework needs to be informed that
1958 * the surface layer gets created with the shadow size in mind.
1959 */
1960 private void initializeElevation() {
1961 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
1962 mAllowUpdateElevation = false;
1963 updateElevation();
1964 }
1965
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001966 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001967 float elevation = 0;
1968 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
1969 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
1970 // since the shadow is bound to the content size and not the target size.
Wale Ogunwaleeb6722c2015-12-08 11:43:43 -08001971 if (StackId.hasWindowShadow(mStackId) && !isResizing()) {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001972 elevation = hasWindowFocus() ?
1973 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
1974 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
1975 if (!mAllowUpdateElevation) {
1976 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
1977 }
1978 // Convert the DP elevation into physical pixels.
1979 elevation = dipToPx(elevation);
1980 mElevationAdjustedForStack = true;
1981 } else {
1982 mElevationAdjustedForStack = false;
1983 }
1984
1985 // Don't change the elevation if we didn't previously adjust it for the stack it was in
1986 // or it didn't change.
1987 if ((wasAdjustedForStack || mElevationAdjustedForStack)
1988 && getElevation() != elevation) {
1989 mWindow.setElevation(elevation);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001990 }
1991 }
1992
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001993 boolean isShowingCaption() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001994 return mDecorCaptionView != null && mDecorCaptionView.isCaptionShowing();
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001995 }
1996
1997 int getCaptionHeight() {
Wale Ogunwale62a91d62015-11-18 11:44:10 -08001998 return isShowingCaption() ? mDecorCaptionView.getCaptionHeight() : 0;
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001999 }
2000
Wale Ogunwale2b547c32015-11-18 10:33:22 -08002001 /**
2002 * Converts a DIP measure into physical pixels.
2003 * @param dip The dip value.
2004 * @return Returns the number of pixels.
2005 */
2006 private float dipToPx(float dip) {
2007 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
2008 getResources().getDisplayMetrics());
2009 }
2010
Filip Gruszczynski3dec0812015-12-09 08:42:41 -08002011 /**
2012 * Provide an override of the caption background drawable.
2013 */
2014 void setUserCaptionBackgroundDrawable(Drawable drawable) {
2015 mUserCaptionBackgroundDrawable = drawable;
2016 if (mBackdropFrameRenderer != null) {
2017 mBackdropFrameRenderer.setUserCaptionBackgroundDrawable(drawable);
2018 }
2019 }
2020
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002021 private static String getTitleSuffix(WindowManager.LayoutParams params) {
2022 if (params == null) {
2023 return "";
2024 }
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002025 final String[] split = params.getTitle().toString().split("\\.");
2026 if (split.length > 0) {
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002027 return split[split.length - 1];
2028 } else {
2029 return "";
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002030 }
2031 }
2032
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002033 void updateLogTag(WindowManager.LayoutParams params) {
2034 mLogTag = TAG + "[" + getTitleSuffix(params) + "]";
2035 }
2036
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002037 private void updateAvailableWidth() {
2038 Resources res = getResources();
2039 mAvailableWidth = TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
2040 res.getConfiguration().screenWidthDp, res.getDisplayMetrics());
2041 }
2042
Clara Bayarri75e09792015-07-29 16:20:40 +01002043 /**
2044 * @hide
2045 */
2046 @Override
2047 public void requestKeyboardShortcuts(List<KeyboardShortcutGroup> list) {
2048 final PanelFeatureState st = mWindow.getPanelState(FEATURE_OPTIONS_PANEL, false);
2049 if (!mWindow.isDestroyed() && st != null && mWindow.getCallback() != null) {
2050 try {
2051 mWindow.getCallback().onProvideKeyboardShortcuts(list, st.menu);
2052 } catch (AbstractMethodError e) {
2053 // We run into this if the app is using supportlib.
2054 }
2055 }
2056 }
2057
Filip Gruszczynski1937a4c2016-01-19 16:17:13 -08002058 @Override
2059 public String toString() {
2060 return "DecorView@" + Integer.toHexString(this.hashCode()) + "["
2061 + getTitleSuffix(mWindow.getAttributes()) + "]";
2062 }
2063
Wale Ogunwale8804af22015-11-17 09:18:15 -08002064 private static class ColorViewState {
2065 View view = null;
2066 int targetVisibility = View.INVISIBLE;
2067 boolean present = false;
Jorim Jaggi04c2fbd2015-12-02 15:11:45 -08002068 boolean visible;
2069 int color;
Wale Ogunwale8804af22015-11-17 09:18:15 -08002070
2071 final int id;
2072 final int systemUiHideFlag;
2073 final int translucentFlag;
2074 final int verticalGravity;
2075 final int horizontalGravity;
2076 final String transitionName;
2077 final int hideWindowFlag;
2078
2079 ColorViewState(int systemUiHideFlag,
2080 int translucentFlag, int verticalGravity, int horizontalGravity,
2081 String transitionName, int id, int hideWindowFlag) {
2082 this.id = id;
2083 this.systemUiHideFlag = systemUiHideFlag;
2084 this.translucentFlag = translucentFlag;
2085 this.verticalGravity = verticalGravity;
2086 this.horizontalGravity = horizontalGravity;
2087 this.transitionName = transitionName;
2088 this.hideWindowFlag = hideWindowFlag;
2089 }
2090 }
2091
2092 /**
2093 * Clears out internal references when the action mode is destroyed.
2094 */
2095 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
2096 private final ActionMode.Callback mWrapped;
2097
2098 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
2099 mWrapped = wrapped;
2100 }
2101
2102 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
2103 return mWrapped.onCreateActionMode(mode, menu);
2104 }
2105
2106 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
2107 requestFitSystemWindows();
2108 return mWrapped.onPrepareActionMode(mode, menu);
2109 }
2110
2111 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
2112 return mWrapped.onActionItemClicked(mode, item);
2113 }
2114
2115 public void onDestroyActionMode(ActionMode mode) {
2116 mWrapped.onDestroyActionMode(mode);
2117 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
2118 >= Build.VERSION_CODES.M;
2119 final boolean isPrimary;
2120 final boolean isFloating;
2121 if (isMncApp) {
2122 isPrimary = mode == mPrimaryActionMode;
2123 isFloating = mode == mFloatingActionMode;
2124 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002125 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002126 + mode + " was not the current primary action mode! Expected "
2127 + mPrimaryActionMode);
2128 }
2129 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
Filip Gruszczynski34dab0b2015-12-22 08:29:07 -08002130 Log.e(mLogTag, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
Wale Ogunwale8804af22015-11-17 09:18:15 -08002131 + mode + " was not the current floating action mode! Expected "
2132 + mFloatingActionMode);
2133 }
2134 } else {
2135 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
2136 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
2137 }
2138 if (isPrimary) {
2139 if (mPrimaryActionModePopup != null) {
2140 removeCallbacks(mShowPrimaryActionModePopup);
2141 }
2142 if (mPrimaryActionModeView != null) {
2143 endOnGoingFadeAnimation();
2144 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
2145 1f, 0f);
2146 mFadeAnim.addListener(new Animator.AnimatorListener() {
2147 @Override
2148 public void onAnimationStart(Animator animation) {
2149
2150 }
2151
2152 @Override
2153 public void onAnimationEnd(Animator animation) {
2154 mPrimaryActionModeView.setVisibility(GONE);
2155 if (mPrimaryActionModePopup != null) {
2156 mPrimaryActionModePopup.dismiss();
2157 }
2158 mPrimaryActionModeView.removeAllViews();
2159 mFadeAnim = null;
2160 }
2161
2162 @Override
2163 public void onAnimationCancel(Animator animation) {
2164
2165 }
2166
2167 @Override
2168 public void onAnimationRepeat(Animator animation) {
2169
2170 }
2171 });
2172 mFadeAnim.start();
2173 }
2174
2175 mPrimaryActionMode = null;
2176 } else if (isFloating) {
2177 cleanupFloatingActionModeViews();
2178 mFloatingActionMode = null;
2179 }
2180 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
2181 try {
2182 mWindow.getCallback().onActionModeFinished(mode);
2183 } catch (AbstractMethodError ame) {
2184 // Older apps might not implement this callback method.
2185 }
2186 }
2187 requestFitSystemWindows();
2188 }
2189
2190 @Override
2191 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
2192 if (mWrapped instanceof ActionMode.Callback2) {
2193 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
2194 } else {
2195 super.onGetContentRect(mode, view, outRect);
2196 }
2197 }
2198 }
2199}