blob: 4b88dc96cc1f69b06c181cea9e5d056d34453ff3 [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;
20import com.android.internal.view.FloatingActionMode;
21import com.android.internal.view.RootViewSurfaceTaker;
22import com.android.internal.view.StandaloneActionMode;
23import com.android.internal.view.menu.ContextMenuBuilder;
24import com.android.internal.view.menu.MenuDialogHelper;
25import com.android.internal.view.menu.MenuPopupHelper;
26import com.android.internal.widget.ActionBarContextView;
27import com.android.internal.widget.BackgroundFallback;
28import com.android.internal.widget.FloatingToolbar;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080029import com.android.internal.widget.NonClientDecorView;
Wale Ogunwale8804af22015-11-17 09:18:15 -080030
31import android.animation.Animator;
32import android.animation.ObjectAnimator;
33import android.app.ActivityManager;
34import android.content.Context;
35import android.content.res.Resources;
36import android.graphics.Canvas;
37import android.graphics.Color;
38import android.graphics.PixelFormat;
39import android.graphics.Rect;
40import android.graphics.drawable.Drawable;
41import android.os.Build;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080042import android.os.RemoteException;
Wale Ogunwale8804af22015-11-17 09:18:15 -080043import android.util.DisplayMetrics;
44import android.util.Log;
45import android.util.TypedValue;
46import android.view.ActionMode;
47import android.view.ContextThemeWrapper;
48import android.view.Gravity;
49import android.view.InputQueue;
50import android.view.KeyEvent;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080051import android.view.LayoutInflater;
Wale Ogunwale8804af22015-11-17 09:18:15 -080052import android.view.Menu;
53import android.view.MenuItem;
54import android.view.MotionEvent;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080055import android.view.ThreadedRenderer;
Wale Ogunwale8804af22015-11-17 09:18:15 -080056import android.view.View;
57import android.view.ViewGroup;
58import android.view.ViewStub;
59import android.view.ViewTreeObserver;
60import android.view.Window;
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080061import android.view.WindowCallbacks;
Wale Ogunwale8804af22015-11-17 09:18:15 -080062import android.view.WindowInsets;
63import android.view.WindowManager;
64import android.view.accessibility.AccessibilityEvent;
65import android.view.accessibility.AccessibilityManager;
66import android.view.animation.AnimationUtils;
67import android.view.animation.Interpolator;
68import android.widget.FrameLayout;
69import android.widget.PopupWindow;
70
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080071import static android.app.ActivityManager.StackId.FULLSCREEN_WORKSPACE_STACK_ID;
72import static android.app.ActivityManager.StackId.INVALID_STACK_ID;
Wale Ogunwale8804af22015-11-17 09:18:15 -080073import static android.view.View.MeasureSpec.AT_MOST;
74import static android.view.View.MeasureSpec.EXACTLY;
75import static android.view.View.MeasureSpec.getMode;
76import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
77import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
78import static android.view.WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS;
79import static android.view.WindowManager.LayoutParams.FLAG_FULLSCREEN;
80import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION;
81import static android.view.WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -080082import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION;
83import static android.view.WindowManager.LayoutParams.TYPE_BASE_APPLICATION;
Wale Ogunwale8804af22015-11-17 09:18:15 -080084
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -080085/** @hide */
86public class DecorView extends FrameLayout implements RootViewSurfaceTaker, WindowCallbacks {
Wale Ogunwale8804af22015-11-17 09:18:15 -080087 private static final String TAG = "DecorView";
88
89 private static final boolean SWEEP_OPEN_MENU = false;
90
Wale Ogunwale2b547c32015-11-18 10:33:22 -080091 // The height of a window which has focus in DIP.
92 private final static int DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP = 20;
93 // The height of a window which has not in DIP.
94 private final static int DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP = 5;
95
96 // Cludge to address b/22668382: Set the shadow size to the maximum so that the layer
97 // size calculation takes the shadow size into account. We set the elevation currently
98 // to max until the first layout command has been executed.
99 private boolean mAllowUpdateElevation = false;
100
101 private boolean mElevationAdjustedForStack = false;
102
Wale Ogunwale8804af22015-11-17 09:18:15 -0800103 int mDefaultOpacity = PixelFormat.OPAQUE;
104
105 /** The feature ID of the panel, or -1 if this is the application's DecorView */
106 private final int mFeatureId;
107
108 private final Rect mDrawingBounds = new Rect();
109
110 private final Rect mBackgroundPadding = new Rect();
111
112 private final Rect mFramePadding = new Rect();
113
114 private final Rect mFrameOffsets = new Rect();
115
116 // True if a non client area decor exists.
117 private boolean mHasNonClientDecor = false;
118
119 private boolean mChanging;
120
121 private Drawable mMenuBackground;
122 private boolean mWatchingForMenu;
123 private int mDownY;
124
125 ActionMode mPrimaryActionMode;
126 private ActionMode mFloatingActionMode;
127 private ActionBarContextView mPrimaryActionModeView;
128 private PopupWindow mPrimaryActionModePopup;
129 private Runnable mShowPrimaryActionModePopup;
130 private ViewTreeObserver.OnPreDrawListener mFloatingToolbarPreDrawListener;
131 private View mFloatingActionModeOriginatingView;
132 private FloatingToolbar mFloatingToolbar;
133 private ObjectAnimator mFadeAnim;
134
135 // View added at runtime to draw under the status bar area
136 private View mStatusGuard;
137 // View added at runtime to draw under the navigation bar area
138 private View mNavigationGuard;
139
140 private final ColorViewState mStatusColorViewState = new ColorViewState(
141 SYSTEM_UI_FLAG_FULLSCREEN, FLAG_TRANSLUCENT_STATUS,
142 Gravity.TOP, Gravity.LEFT,
143 Window.STATUS_BAR_BACKGROUND_TRANSITION_NAME,
144 com.android.internal.R.id.statusBarBackground,
145 FLAG_FULLSCREEN);
146 private final ColorViewState mNavigationColorViewState = new ColorViewState(
147 SYSTEM_UI_FLAG_HIDE_NAVIGATION, FLAG_TRANSLUCENT_NAVIGATION,
148 Gravity.BOTTOM, Gravity.RIGHT,
149 Window.NAVIGATION_BAR_BACKGROUND_TRANSITION_NAME,
150 com.android.internal.R.id.navigationBarBackground,
151 0 /* hideWindowFlag */);
152
153 private final Interpolator mShowInterpolator;
154 private final Interpolator mHideInterpolator;
155 private final int mBarEnterExitDuration;
156
157 private final BackgroundFallback mBackgroundFallback = new BackgroundFallback();
158
159 private int mLastTopInset = 0;
160 private int mLastBottomInset = 0;
161 private int mLastRightInset = 0;
162 private boolean mLastHasTopStableInset = false;
163 private boolean mLastHasBottomStableInset = false;
164 private boolean mLastHasRightStableInset = false;
165 private int mLastWindowFlags = 0;
166
167 private int mRootScrollY = 0;
168
169 private PhoneWindow mWindow;
170
171 ViewGroup mContentRoot;
172
173 private Rect mTempRect;
174 private Rect mOutsets = new Rect();
175
Wale Ogunwale0d7e9122015-11-17 10:45:06 -0800176 // This is the non client decor view for the window, containing the caption and window control
177 // buttons. The visibility of this decor depends on the workspace and the window type.
178 // If the window type does not require such a view, this member might be null.
179 NonClientDecorView mNonClientDecorView;
180
181 // The non client decor needs to adapt to the used workspace. Since querying and changing the
182 // workspace is expensive, this is the workspace value the window is currently set up for.
183 int mWorkspaceId;
184
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -0800185 private boolean mWindowResizeCallbacksAdded = false;
186
187 public BackdropFrameRenderer mBackdropFrameRenderer = null;
188 private Drawable mResizingBackgroundDrawable;
189 private Drawable mCaptionBackgroundDrawable;
190
Wale Ogunwale8804af22015-11-17 09:18:15 -0800191 DecorView(Context context, int featureId, PhoneWindow window) {
192 super(context);
193 mFeatureId = featureId;
194
195 mShowInterpolator = AnimationUtils.loadInterpolator(context,
196 android.R.interpolator.linear_out_slow_in);
197 mHideInterpolator = AnimationUtils.loadInterpolator(context,
198 android.R.interpolator.fast_out_linear_in);
199
200 mBarEnterExitDuration = context.getResources().getInteger(
201 R.integer.dock_enter_exit_duration);
202
203 setWindow(window);
204 }
205
206 public void setBackgroundFallback(int resId) {
207 mBackgroundFallback.setDrawable(resId != 0 ? getContext().getDrawable(resId) : null);
208 setWillNotDraw(getBackground() == null && !mBackgroundFallback.hasFallback());
209 }
210
211 @Override
212 public void onDraw(Canvas c) {
213 super.onDraw(c);
214 mBackgroundFallback.draw(mContentRoot, c, mWindow.mContentParent);
215 }
216
217 @Override
218 public boolean dispatchKeyEvent(KeyEvent event) {
219 final int keyCode = event.getKeyCode();
220 final int action = event.getAction();
221 final boolean isDown = action == KeyEvent.ACTION_DOWN;
222
223 if (isDown && (event.getRepeatCount() == 0)) {
224 // First handle chording of panel key: if a panel key is held
225 // but not released, try to execute a shortcut in it.
226 if ((mWindow.mPanelChordingKey > 0) && (mWindow.mPanelChordingKey != keyCode)) {
227 boolean handled = dispatchKeyShortcutEvent(event);
228 if (handled) {
229 return true;
230 }
231 }
232
233 // If a panel is open, perform a shortcut on it without the
234 // chorded panel key
235 if ((mWindow.mPreparedPanel != null) && mWindow.mPreparedPanel.isOpen) {
236 if (mWindow.performPanelShortcut(mWindow.mPreparedPanel, keyCode, event, 0)) {
237 return true;
238 }
239 }
240 }
241
242 if (!mWindow.isDestroyed()) {
243 final Window.Callback cb = mWindow.getCallback();
244 final boolean handled = cb != null && mFeatureId < 0 ? cb.dispatchKeyEvent(event)
245 : super.dispatchKeyEvent(event);
246 if (handled) {
247 return true;
248 }
249 }
250
251 return isDown ? mWindow.onKeyDown(mFeatureId, event.getKeyCode(), event)
252 : mWindow.onKeyUp(mFeatureId, event.getKeyCode(), event);
253 }
254
255 @Override
256 public boolean dispatchKeyShortcutEvent(KeyEvent ev) {
257 // If the panel is already prepared, then perform the shortcut using it.
258 boolean handled;
259 if (mWindow.mPreparedPanel != null) {
260 handled = mWindow.performPanelShortcut(mWindow.mPreparedPanel, ev.getKeyCode(), ev,
261 Menu.FLAG_PERFORM_NO_CLOSE);
262 if (handled) {
263 if (mWindow.mPreparedPanel != null) {
264 mWindow.mPreparedPanel.isHandled = true;
265 }
266 return true;
267 }
268 }
269
270 // Shortcut not handled by the panel. Dispatch to the view hierarchy.
271 final Window.Callback cb = mWindow.getCallback();
272 handled = cb != null && !mWindow.isDestroyed() && mFeatureId < 0
273 ? cb.dispatchKeyShortcutEvent(ev) : super.dispatchKeyShortcutEvent(ev);
274 if (handled) {
275 return true;
276 }
277
278 // If the panel is not prepared, then we may be trying to handle a shortcut key
279 // combination such as Control+C. Temporarily prepare the panel then mark it
280 // unprepared again when finished to ensure that the panel will again be prepared
281 // the next time it is shown for real.
282 PhoneWindow.PanelFeatureState st =
283 mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
284 if (st != null && mWindow.mPreparedPanel == null) {
285 mWindow.preparePanel(st, ev);
286 handled = mWindow.performPanelShortcut(st, ev.getKeyCode(), ev,
287 Menu.FLAG_PERFORM_NO_CLOSE);
288 st.isPrepared = false;
289 if (handled) {
290 return true;
291 }
292 }
293 return false;
294 }
295
296 @Override
297 public boolean dispatchTouchEvent(MotionEvent ev) {
298 final Window.Callback cb = mWindow.getCallback();
299 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
300 ? cb.dispatchTouchEvent(ev) : super.dispatchTouchEvent(ev);
301 }
302
303 @Override
304 public boolean dispatchTrackballEvent(MotionEvent ev) {
305 final Window.Callback cb = mWindow.getCallback();
306 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
307 ? cb.dispatchTrackballEvent(ev) : super.dispatchTrackballEvent(ev);
308 }
309
310 @Override
311 public boolean dispatchGenericMotionEvent(MotionEvent ev) {
312 final Window.Callback cb = mWindow.getCallback();
313 return cb != null && !mWindow.isDestroyed() && mFeatureId < 0
314 ? cb.dispatchGenericMotionEvent(ev) : super.dispatchGenericMotionEvent(ev);
315 }
316
317 public boolean superDispatchKeyEvent(KeyEvent event) {
318 // Give priority to closing action modes if applicable.
319 if (event.getKeyCode() == KeyEvent.KEYCODE_BACK) {
320 final int action = event.getAction();
321 // Back cancels action modes first.
322 if (mPrimaryActionMode != null) {
323 if (action == KeyEvent.ACTION_UP) {
324 mPrimaryActionMode.finish();
325 }
326 return true;
327 }
328 }
329
330 return super.dispatchKeyEvent(event);
331 }
332
333 public boolean superDispatchKeyShortcutEvent(KeyEvent event) {
334 return super.dispatchKeyShortcutEvent(event);
335 }
336
337 public boolean superDispatchTouchEvent(MotionEvent event) {
338 return super.dispatchTouchEvent(event);
339 }
340
341 public boolean superDispatchTrackballEvent(MotionEvent event) {
342 return super.dispatchTrackballEvent(event);
343 }
344
345 public boolean superDispatchGenericMotionEvent(MotionEvent event) {
346 return super.dispatchGenericMotionEvent(event);
347 }
348
349 @Override
350 public boolean onTouchEvent(MotionEvent event) {
351 return onInterceptTouchEvent(event);
352 }
353
354 private boolean isOutOfInnerBounds(int x, int y) {
355 return x < 0 || y < 0 || x > getWidth() || y > getHeight();
356 }
357
358 private boolean isOutOfBounds(int x, int y) {
359 return x < -5 || y < -5 || x > (getWidth() + 5)
360 || y > (getHeight() + 5);
361 }
362
363 @Override
364 public boolean onInterceptTouchEvent(MotionEvent event) {
365 int action = event.getAction();
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800366 if (mHasNonClientDecor && isShowingCaption()) {
Wale Ogunwale8804af22015-11-17 09:18:15 -0800367 // Don't dispatch ACTION_DOWN to the non client decor if the window is
368 // resizable and the event was (starting) outside the window.
369 // Window resizing events should be handled by WindowManager.
370 // TODO: Investigate how to handle the outside touch in window manager
371 // without generating these events.
372 // Currently we receive these because we need to enlarge the window's
373 // touch region so that the monitor channel receives the events
374 // in the outside touch area.
375 if (action == MotionEvent.ACTION_DOWN) {
376 final int x = (int) event.getX();
377 final int y = (int) event.getY();
378 if (isOutOfInnerBounds(x, y)) {
379 return true;
380 }
381 }
382 }
383
384 if (mFeatureId >= 0) {
385 if (action == MotionEvent.ACTION_DOWN) {
386 int x = (int)event.getX();
387 int y = (int)event.getY();
388 if (isOutOfBounds(x, y)) {
389 mWindow.closePanel(mFeatureId);
390 return true;
391 }
392 }
393 }
394
395 if (!SWEEP_OPEN_MENU) {
396 return false;
397 }
398
399 if (mFeatureId >= 0) {
400 if (action == MotionEvent.ACTION_DOWN) {
401 Log.i(TAG, "Watchiing!");
402 mWatchingForMenu = true;
403 mDownY = (int) event.getY();
404 return false;
405 }
406
407 if (!mWatchingForMenu) {
408 return false;
409 }
410
411 int y = (int)event.getY();
412 if (action == MotionEvent.ACTION_MOVE) {
413 if (y > (mDownY+30)) {
414 Log.i(TAG, "Closing!");
415 mWindow.closePanel(mFeatureId);
416 mWatchingForMenu = false;
417 return true;
418 }
419 } else if (action == MotionEvent.ACTION_UP) {
420 mWatchingForMenu = false;
421 }
422
423 return false;
424 }
425
426 //Log.i(TAG, "Intercept: action=" + action + " y=" + event.getY()
427 // + " (in " + getHeight() + ")");
428
429 if (action == MotionEvent.ACTION_DOWN) {
430 int y = (int)event.getY();
431 if (y >= (getHeight()-5) && !mWindow.hasChildren()) {
432 Log.i(TAG, "Watching!");
433 mWatchingForMenu = true;
434 }
435 return false;
436 }
437
438 if (!mWatchingForMenu) {
439 return false;
440 }
441
442 int y = (int)event.getY();
443 if (action == MotionEvent.ACTION_MOVE) {
444 if (y < (getHeight()-30)) {
445 Log.i(TAG, "Opening!");
446 mWindow.openPanel(Window.FEATURE_OPTIONS_PANEL, new KeyEvent(
447 KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_MENU));
448 mWatchingForMenu = false;
449 return true;
450 }
451 } else if (action == MotionEvent.ACTION_UP) {
452 mWatchingForMenu = false;
453 }
454
455 return false;
456 }
457
458 @Override
459 public void sendAccessibilityEvent(int eventType) {
460 if (!AccessibilityManager.getInstance(mContext).isEnabled()) {
461 return;
462 }
463
464 // if we are showing a feature that should be announced and one child
465 // make this child the event source since this is the feature itself
466 // otherwise the callback will take over and announce its client
467 if ((mFeatureId == Window.FEATURE_OPTIONS_PANEL ||
468 mFeatureId == Window.FEATURE_CONTEXT_MENU ||
469 mFeatureId == Window.FEATURE_PROGRESS ||
470 mFeatureId == Window.FEATURE_INDETERMINATE_PROGRESS)
471 && getChildCount() == 1) {
472 getChildAt(0).sendAccessibilityEvent(eventType);
473 } else {
474 super.sendAccessibilityEvent(eventType);
475 }
476 }
477
478 @Override
479 public boolean dispatchPopulateAccessibilityEventInternal(AccessibilityEvent event) {
480 final Window.Callback cb = mWindow.getCallback();
481 if (cb != null && !mWindow.isDestroyed()) {
482 if (cb.dispatchPopulateAccessibilityEvent(event)) {
483 return true;
484 }
485 }
486 return super.dispatchPopulateAccessibilityEventInternal(event);
487 }
488
489 @Override
490 protected boolean setFrame(int l, int t, int r, int b) {
491 boolean changed = super.setFrame(l, t, r, b);
492 if (changed) {
493 final Rect drawingBounds = mDrawingBounds;
494 getDrawingRect(drawingBounds);
495
496 Drawable fg = getForeground();
497 if (fg != null) {
498 final Rect frameOffsets = mFrameOffsets;
499 drawingBounds.left += frameOffsets.left;
500 drawingBounds.top += frameOffsets.top;
501 drawingBounds.right -= frameOffsets.right;
502 drawingBounds.bottom -= frameOffsets.bottom;
503 fg.setBounds(drawingBounds);
504 final Rect framePadding = mFramePadding;
505 drawingBounds.left += framePadding.left - frameOffsets.left;
506 drawingBounds.top += framePadding.top - frameOffsets.top;
507 drawingBounds.right -= framePadding.right - frameOffsets.right;
508 drawingBounds.bottom -= framePadding.bottom - frameOffsets.bottom;
509 }
510
511 Drawable bg = getBackground();
512 if (bg != null) {
513 bg.setBounds(drawingBounds);
514 }
515
516 if (SWEEP_OPEN_MENU) {
517 if (mMenuBackground == null && mFeatureId < 0
518 && mWindow.getAttributes().height
519 == WindowManager.LayoutParams.MATCH_PARENT) {
520 mMenuBackground = getContext().getDrawable(
521 R.drawable.menu_background);
522 }
523 if (mMenuBackground != null) {
524 mMenuBackground.setBounds(drawingBounds.left,
525 drawingBounds.bottom-6, drawingBounds.right,
526 drawingBounds.bottom+20);
527 }
528 }
529 }
530 return changed;
531 }
532
533 @Override
534 protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
535 final DisplayMetrics metrics = getContext().getResources().getDisplayMetrics();
536 final boolean isPortrait = metrics.widthPixels < metrics.heightPixels;
537
538 final int widthMode = getMode(widthMeasureSpec);
539 final int heightMode = getMode(heightMeasureSpec);
540
541 boolean fixedWidth = false;
542 if (widthMode == AT_MOST) {
543 final TypedValue tvw = isPortrait ? mWindow.mFixedWidthMinor
544 : mWindow.mFixedWidthMajor;
545 if (tvw != null && tvw.type != TypedValue.TYPE_NULL) {
546 final int w;
547 if (tvw.type == TypedValue.TYPE_DIMENSION) {
548 w = (int) tvw.getDimension(metrics);
549 } else if (tvw.type == TypedValue.TYPE_FRACTION) {
550 w = (int) tvw.getFraction(metrics.widthPixels, metrics.widthPixels);
551 } else {
552 w = 0;
553 }
554
555 if (w > 0) {
556 final int widthSize = MeasureSpec.getSize(widthMeasureSpec);
557 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
558 Math.min(w, widthSize), EXACTLY);
559 fixedWidth = true;
560 }
561 }
562 }
563
564 if (heightMode == AT_MOST) {
565 final TypedValue tvh = isPortrait ? mWindow.mFixedHeightMajor
566 : mWindow.mFixedHeightMinor;
567 if (tvh != null && tvh.type != TypedValue.TYPE_NULL) {
568 final int h;
569 if (tvh.type == TypedValue.TYPE_DIMENSION) {
570 h = (int) tvh.getDimension(metrics);
571 } else if (tvh.type == TypedValue.TYPE_FRACTION) {
572 h = (int) tvh.getFraction(metrics.heightPixels, metrics.heightPixels);
573 } else {
574 h = 0;
575 }
576 if (h > 0) {
577 final int heightSize = MeasureSpec.getSize(heightMeasureSpec);
578 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
579 Math.min(h, heightSize), EXACTLY);
580 }
581 }
582 }
583
584 getOutsets(mOutsets);
585 if (mOutsets.top > 0 || mOutsets.bottom > 0) {
586 int mode = MeasureSpec.getMode(heightMeasureSpec);
587 if (mode != MeasureSpec.UNSPECIFIED) {
588 int height = MeasureSpec.getSize(heightMeasureSpec);
589 heightMeasureSpec = MeasureSpec.makeMeasureSpec(
590 height + mOutsets.top + mOutsets.bottom, mode);
591 }
592 }
593 if (mOutsets.left > 0 || mOutsets.right > 0) {
594 int mode = MeasureSpec.getMode(widthMeasureSpec);
595 if (mode != MeasureSpec.UNSPECIFIED) {
596 int width = MeasureSpec.getSize(widthMeasureSpec);
597 widthMeasureSpec = MeasureSpec.makeMeasureSpec(
598 width + mOutsets.left + mOutsets.right, mode);
599 }
600 }
601
602 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
603
604 int width = getMeasuredWidth();
605 boolean measure = false;
606
607 widthMeasureSpec = MeasureSpec.makeMeasureSpec(width, EXACTLY);
608
609 if (!fixedWidth && widthMode == AT_MOST) {
610 final TypedValue tv = isPortrait ? mWindow.mMinWidthMinor : mWindow.mMinWidthMajor;
611 if (tv.type != TypedValue.TYPE_NULL) {
612 final int min;
613 if (tv.type == TypedValue.TYPE_DIMENSION) {
614 min = (int)tv.getDimension(metrics);
615 } else if (tv.type == TypedValue.TYPE_FRACTION) {
616 min = (int)tv.getFraction(metrics.widthPixels, metrics.widthPixels);
617 } else {
618 min = 0;
619 }
620
621 if (width < min) {
622 widthMeasureSpec = MeasureSpec.makeMeasureSpec(min, EXACTLY);
623 measure = true;
624 }
625 }
626 }
627
628 // TODO: Support height?
629
630 if (measure) {
631 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
632 }
633 }
634
635 @Override
636 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
637 super.onLayout(changed, left, top, right, bottom);
638 getOutsets(mOutsets);
639 if (mOutsets.left > 0) {
640 offsetLeftAndRight(-mOutsets.left);
641 }
642 if (mOutsets.top > 0) {
643 offsetTopAndBottom(-mOutsets.top);
644 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -0800645
646 // If the application changed its SystemUI metrics, we might also have to adapt
647 // our shadow elevation.
648 updateElevation();
649 mAllowUpdateElevation = true;
Wale Ogunwale8804af22015-11-17 09:18:15 -0800650 }
651
652 @Override
653 public void draw(Canvas canvas) {
654 super.draw(canvas);
655
656 if (mMenuBackground != null) {
657 mMenuBackground.draw(canvas);
658 }
659 }
660
661 @Override
662 public boolean showContextMenuForChild(View originalView) {
663 // Reuse the context menu builder
664 if (mWindow.mContextMenu == null) {
665 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
666 mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
667 } else {
668 mWindow.mContextMenu.clearAll();
669 }
670
671 final MenuDialogHelper helper = mWindow.mContextMenu.show(originalView,
672 originalView.getWindowToken());
673 if (helper != null) {
674 helper.setPresenterCallback(mWindow.mContextMenuCallback);
675 } else if (mWindow.mContextMenuHelper != null) {
676 // No menu to show, but if we have a menu currently showing it just became blank.
677 // Close it.
678 mWindow.mContextMenuHelper.dismiss();
679 }
680 mWindow.mContextMenuHelper = helper;
681 return helper != null;
682 }
683
684 @Override
685 public boolean showContextMenuForChild(View originalView, float x, float y) {
686 // Reuse the context menu builder
687 if (mWindow.mContextMenu == null) {
688 mWindow.mContextMenu = new ContextMenuBuilder(getContext());
689 mWindow.mContextMenu.setCallback(mWindow.mContextMenuCallback);
690 } else {
691 mWindow.mContextMenu.clearAll();
692 }
693
694 final MenuPopupHelper helper = mWindow.mContextMenu.showPopup(
695 getContext(), originalView, x, y);
696 if (helper != null) {
697 helper.setCallback(mWindow.mContextMenuCallback);
698 } else if (mWindow.mContextMenuPopupHelper != null) {
699 // No menu to show, but if we have a menu currently showing it just became blank.
700 // Close it.
701 mWindow.mContextMenuPopupHelper.dismiss();
702 }
703 mWindow.mContextMenuPopupHelper = helper;
704 return helper != null;
705 }
706
707 @Override
708 public ActionMode startActionModeForChild(View originalView,
709 ActionMode.Callback callback) {
710 return startActionModeForChild(originalView, callback, ActionMode.TYPE_PRIMARY);
711 }
712
713 @Override
714 public ActionMode startActionModeForChild(
715 View child, ActionMode.Callback callback, int type) {
716 return startActionMode(child, callback, type);
717 }
718
719 @Override
720 public ActionMode startActionMode(ActionMode.Callback callback) {
721 return startActionMode(callback, ActionMode.TYPE_PRIMARY);
722 }
723
724 @Override
725 public ActionMode startActionMode(ActionMode.Callback callback, int type) {
726 return startActionMode(this, callback, type);
727 }
728
729 private ActionMode startActionMode(
730 View originatingView, ActionMode.Callback callback, int type) {
731 ActionMode.Callback2 wrappedCallback = new ActionModeCallback2Wrapper(callback);
732 ActionMode mode = null;
733 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
734 try {
735 mode = mWindow.getCallback().onWindowStartingActionMode(wrappedCallback, type);
736 } catch (AbstractMethodError ame) {
737 // Older apps might not implement the typed version of this method.
738 if (type == ActionMode.TYPE_PRIMARY) {
739 try {
740 mode = mWindow.getCallback().onWindowStartingActionMode(
741 wrappedCallback);
742 } catch (AbstractMethodError ame2) {
743 // Older apps might not implement this callback method at all.
744 }
745 }
746 }
747 }
748 if (mode != null) {
749 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
750 cleanupPrimaryActionMode();
751 mPrimaryActionMode = mode;
752 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
753 if (mFloatingActionMode != null) {
754 mFloatingActionMode.finish();
755 }
756 mFloatingActionMode = mode;
757 }
758 } else {
759 mode = createActionMode(type, wrappedCallback, originatingView);
760 if (mode != null && wrappedCallback.onCreateActionMode(mode, mode.getMenu())) {
761 setHandledActionMode(mode);
762 } else {
763 mode = null;
764 }
765 }
766 if (mode != null && mWindow.getCallback() != null && !mWindow.isDestroyed()) {
767 try {
768 mWindow.getCallback().onActionModeStarted(mode);
769 } catch (AbstractMethodError ame) {
770 // Older apps might not implement this callback method.
771 }
772 }
773 return mode;
774 }
775
776 private void cleanupPrimaryActionMode() {
777 if (mPrimaryActionMode != null) {
778 mPrimaryActionMode.finish();
779 mPrimaryActionMode = null;
780 }
781 if (mPrimaryActionModeView != null) {
782 mPrimaryActionModeView.killMode();
783 }
784 }
785
786 private void cleanupFloatingActionModeViews() {
787 if (mFloatingToolbar != null) {
788 mFloatingToolbar.dismiss();
789 mFloatingToolbar = null;
790 }
791 if (mFloatingActionModeOriginatingView != null) {
792 if (mFloatingToolbarPreDrawListener != null) {
793 mFloatingActionModeOriginatingView.getViewTreeObserver()
794 .removeOnPreDrawListener(mFloatingToolbarPreDrawListener);
795 mFloatingToolbarPreDrawListener = null;
796 }
797 mFloatingActionModeOriginatingView = null;
798 }
799 }
800
801 public void startChanging() {
802 mChanging = true;
803 }
804
805 public void finishChanging() {
806 mChanging = false;
807 drawableChanged();
808 }
809
810 public void setWindowBackground(Drawable drawable) {
811 if (getBackground() != drawable) {
812 setBackgroundDrawable(drawable);
813 if (drawable != null) {
814 drawable.getPadding(mBackgroundPadding);
815 } else {
816 mBackgroundPadding.setEmpty();
817 }
818 drawableChanged();
819 }
820 }
821
822 public void setWindowFrame(Drawable drawable) {
823 if (getForeground() != drawable) {
824 setForeground(drawable);
825 if (drawable != null) {
826 drawable.getPadding(mFramePadding);
827 } else {
828 mFramePadding.setEmpty();
829 }
830 drawableChanged();
831 }
832 }
833
834 @Override
835 public void onWindowSystemUiVisibilityChanged(int visible) {
836 updateColorViews(null /* insets */, true /* animate */);
837 }
838
839 @Override
840 public WindowInsets onApplyWindowInsets(WindowInsets insets) {
841 mFrameOffsets.set(insets.getSystemWindowInsets());
842 insets = updateColorViews(insets, true /* animate */);
843 insets = updateStatusGuard(insets);
844 updateNavigationGuard(insets);
845 if (getForeground() != null) {
846 drawableChanged();
847 }
848 return insets;
849 }
850
851 @Override
852 public boolean isTransitionGroup() {
853 return false;
854 }
855
856 WindowInsets updateColorViews(WindowInsets insets, boolean animate) {
857 WindowManager.LayoutParams attrs = mWindow.getAttributes();
858 int sysUiVisibility = attrs.systemUiVisibility | getWindowSystemUiVisibility();
859
860 if (!mWindow.mIsFloating && ActivityManager.isHighEndGfx()) {
861 boolean disallowAnimate = !isLaidOut();
862 disallowAnimate |= ((mLastWindowFlags ^ attrs.flags)
863 & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
864 mLastWindowFlags = attrs.flags;
865
866 if (insets != null) {
867 mLastTopInset = Math.min(insets.getStableInsetTop(),
868 insets.getSystemWindowInsetTop());
869 mLastBottomInset = Math.min(insets.getStableInsetBottom(),
870 insets.getSystemWindowInsetBottom());
871 mLastRightInset = Math.min(insets.getStableInsetRight(),
872 insets.getSystemWindowInsetRight());
873
874 // Don't animate if the presence of stable insets has changed, because that
875 // indicates that the window was either just added and received them for the
876 // first time, or the window size or position has changed.
877 boolean hasTopStableInset = insets.getStableInsetTop() != 0;
878 disallowAnimate |= (hasTopStableInset != mLastHasTopStableInset);
879 mLastHasTopStableInset = hasTopStableInset;
880
881 boolean hasBottomStableInset = insets.getStableInsetBottom() != 0;
882 disallowAnimate |= (hasBottomStableInset != mLastHasBottomStableInset);
883 mLastHasBottomStableInset = hasBottomStableInset;
884
885 boolean hasRightStableInset = insets.getStableInsetRight() != 0;
886 disallowAnimate |= (hasRightStableInset != mLastHasRightStableInset);
887 mLastHasRightStableInset = hasRightStableInset;
888 }
889
890 boolean navBarToRightEdge = mLastBottomInset == 0 && mLastRightInset > 0;
891 int navBarSize = navBarToRightEdge ? mLastRightInset : mLastBottomInset;
892 updateColorViewInt(mNavigationColorViewState, sysUiVisibility,
893 mWindow.mNavigationBarColor, navBarSize, navBarToRightEdge,
894 0 /* rightInset */, animate && !disallowAnimate);
895
896 boolean statusBarNeedsRightInset = navBarToRightEdge
897 && mNavigationColorViewState.present;
898 int statusBarRightInset = statusBarNeedsRightInset ? mLastRightInset : 0;
899 updateColorViewInt(mStatusColorViewState, sysUiVisibility, mWindow.mStatusBarColor,
900 mLastTopInset, false /* matchVertical */, statusBarRightInset,
901 animate && !disallowAnimate);
902 }
903
904 // When we expand the window with FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS, we still need
905 // to ensure that the rest of the view hierarchy doesn't notice it, unless they've
906 // explicitly asked for it.
907
908 boolean consumingNavBar =
909 (attrs.flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0
910 && (sysUiVisibility & SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION) == 0
911 && (sysUiVisibility & SYSTEM_UI_FLAG_HIDE_NAVIGATION) == 0;
912
913 int consumedRight = consumingNavBar ? mLastRightInset : 0;
914 int consumedBottom = consumingNavBar ? mLastBottomInset : 0;
915
916 if (mContentRoot != null
917 && mContentRoot.getLayoutParams() instanceof MarginLayoutParams) {
918 MarginLayoutParams lp = (MarginLayoutParams) mContentRoot.getLayoutParams();
919 if (lp.rightMargin != consumedRight || lp.bottomMargin != consumedBottom) {
920 lp.rightMargin = consumedRight;
921 lp.bottomMargin = consumedBottom;
922 mContentRoot.setLayoutParams(lp);
923
924 if (insets == null) {
925 // The insets have changed, but we're not currently in the process
926 // of dispatching them.
927 requestApplyInsets();
928 }
929 }
930 if (insets != null) {
931 insets = insets.replaceSystemWindowInsets(
932 insets.getSystemWindowInsetLeft(),
933 insets.getSystemWindowInsetTop(),
934 insets.getSystemWindowInsetRight() - consumedRight,
935 insets.getSystemWindowInsetBottom() - consumedBottom);
936 }
937 }
938
939 if (insets != null) {
940 insets = insets.consumeStableInsets();
941 }
942 return insets;
943 }
944
945 /**
946 * Update a color view
947 *
948 * @param state the color view to update.
949 * @param sysUiVis the current systemUiVisibility to apply.
950 * @param color the current color to apply.
951 * @param size the current size in the non-parent-matching dimension.
952 * @param verticalBar if true the view is attached to a vertical edge, otherwise to a
953 * horizontal edge,
954 * @param rightMargin rightMargin for the color view.
955 * @param animate if true, the change will be animated.
956 */
957 private void updateColorViewInt(final ColorViewState state, int sysUiVis, int color,
958 int size, boolean verticalBar, int rightMargin, boolean animate) {
959 state.present = size > 0 && (sysUiVis & state.systemUiHideFlag) == 0
960 && (mWindow.getAttributes().flags & state.hideWindowFlag) == 0
961 && (mWindow.getAttributes().flags & FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS) != 0;
962 boolean show = state.present
963 && (color & Color.BLACK) != 0
964 && (mWindow.getAttributes().flags & state.translucentFlag) == 0;
965
966 boolean visibilityChanged = false;
967 View view = state.view;
968
969 int resolvedHeight = verticalBar ? LayoutParams.MATCH_PARENT : size;
970 int resolvedWidth = verticalBar ? size : LayoutParams.MATCH_PARENT;
971 int resolvedGravity = verticalBar ? state.horizontalGravity : state.verticalGravity;
972
973 if (view == null) {
974 if (show) {
975 state.view = view = new View(mContext);
976 view.setBackgroundColor(color);
977 view.setTransitionName(state.transitionName);
978 view.setId(state.id);
979 visibilityChanged = true;
980 view.setVisibility(INVISIBLE);
981 state.targetVisibility = VISIBLE;
982
983 LayoutParams lp = new LayoutParams(resolvedWidth, resolvedHeight,
984 resolvedGravity);
985 lp.rightMargin = rightMargin;
986 addView(view, lp);
987 updateColorViewTranslations();
988 }
989 } else {
990 int vis = show ? VISIBLE : INVISIBLE;
991 visibilityChanged = state.targetVisibility != vis;
992 state.targetVisibility = vis;
993 LayoutParams lp = (LayoutParams) view.getLayoutParams();
994 if (lp.height != resolvedHeight || lp.width != resolvedWidth
995 || lp.gravity != resolvedGravity || lp.rightMargin != rightMargin) {
996 lp.height = resolvedHeight;
997 lp.width = resolvedWidth;
998 lp.gravity = resolvedGravity;
999 lp.rightMargin = rightMargin;
1000 view.setLayoutParams(lp);
1001 }
1002 if (show) {
1003 view.setBackgroundColor(color);
1004 }
1005 }
1006 if (visibilityChanged) {
1007 view.animate().cancel();
1008 if (animate) {
1009 if (show) {
1010 if (view.getVisibility() != VISIBLE) {
1011 view.setVisibility(VISIBLE);
1012 view.setAlpha(0.0f);
1013 }
1014 view.animate().alpha(1.0f).setInterpolator(mShowInterpolator).
1015 setDuration(mBarEnterExitDuration);
1016 } else {
1017 view.animate().alpha(0.0f).setInterpolator(mHideInterpolator)
1018 .setDuration(mBarEnterExitDuration)
1019 .withEndAction(new Runnable() {
1020 @Override
1021 public void run() {
1022 state.view.setAlpha(1.0f);
1023 state.view.setVisibility(INVISIBLE);
1024 }
1025 });
1026 }
1027 } else {
1028 view.setAlpha(1.0f);
1029 view.setVisibility(show ? VISIBLE : INVISIBLE);
1030 }
1031 }
1032 }
1033
1034 private void updateColorViewTranslations() {
1035 // Put the color views back in place when they get moved off the screen
1036 // due to the the ViewRootImpl panning.
1037 int rootScrollY = mRootScrollY;
1038 if (mStatusColorViewState.view != null) {
1039 mStatusColorViewState.view.setTranslationY(rootScrollY > 0 ? rootScrollY : 0);
1040 }
1041 if (mNavigationColorViewState.view != null) {
1042 mNavigationColorViewState.view.setTranslationY(rootScrollY < 0 ? rootScrollY : 0);
1043 }
1044 }
1045
1046 private WindowInsets updateStatusGuard(WindowInsets insets) {
1047 boolean showStatusGuard = false;
1048 // Show the status guard when the non-overlay contextual action bar is showing
1049 if (mPrimaryActionModeView != null) {
1050 if (mPrimaryActionModeView.getLayoutParams() instanceof MarginLayoutParams) {
1051 // Insets are magic!
1052 final MarginLayoutParams mlp = (MarginLayoutParams)
1053 mPrimaryActionModeView.getLayoutParams();
1054 boolean mlpChanged = false;
1055 if (mPrimaryActionModeView.isShown()) {
1056 if (mTempRect == null) {
1057 mTempRect = new Rect();
1058 }
1059 final Rect rect = mTempRect;
1060
1061 // If the parent doesn't consume the insets, manually
1062 // apply the default system window insets.
1063 mWindow.mContentParent.computeSystemWindowInsets(insets, rect);
1064 final int newMargin = rect.top == 0 ? insets.getSystemWindowInsetTop() : 0;
1065 if (mlp.topMargin != newMargin) {
1066 mlpChanged = true;
1067 mlp.topMargin = insets.getSystemWindowInsetTop();
1068
1069 if (mStatusGuard == null) {
1070 mStatusGuard = new View(mContext);
1071 mStatusGuard.setBackgroundColor(mContext.getColor(
1072 R.color.input_method_navigation_guard));
1073 addView(mStatusGuard, indexOfChild(mStatusColorViewState.view),
1074 new LayoutParams(LayoutParams.MATCH_PARENT,
1075 mlp.topMargin, Gravity.START | Gravity.TOP));
1076 } else {
1077 final LayoutParams lp = (LayoutParams)
1078 mStatusGuard.getLayoutParams();
1079 if (lp.height != mlp.topMargin) {
1080 lp.height = mlp.topMargin;
1081 mStatusGuard.setLayoutParams(lp);
1082 }
1083 }
1084 }
1085
1086 // The action mode's theme may differ from the app, so
1087 // always show the status guard above it if we have one.
1088 showStatusGuard = mStatusGuard != null;
1089
1090 // We only need to consume the insets if the action
1091 // mode is overlaid on the app content (e.g. it's
1092 // sitting in a FrameLayout, see
1093 // screen_simple_overlay_action_mode.xml).
1094 final boolean nonOverlay = (mWindow.getLocalFeaturesPrivate()
1095 & (1 << Window.FEATURE_ACTION_MODE_OVERLAY)) == 0;
1096 insets = insets.consumeSystemWindowInsets(
1097 false, nonOverlay && showStatusGuard /* top */, false, false);
1098 } else {
1099 // reset top margin
1100 if (mlp.topMargin != 0) {
1101 mlpChanged = true;
1102 mlp.topMargin = 0;
1103 }
1104 }
1105 if (mlpChanged) {
1106 mPrimaryActionModeView.setLayoutParams(mlp);
1107 }
1108 }
1109 }
1110 if (mStatusGuard != null) {
1111 mStatusGuard.setVisibility(showStatusGuard ? View.VISIBLE : View.GONE);
1112 }
1113 return insets;
1114 }
1115
1116 private void updateNavigationGuard(WindowInsets insets) {
1117 // IMEs lay out below the nav bar, but the content view must not (for back compat)
1118 if (mWindow.getAttributes().type == WindowManager.LayoutParams.TYPE_INPUT_METHOD) {
1119 // prevent the content view from including the nav bar height
1120 if (mWindow.mContentParent != null) {
1121 if (mWindow.mContentParent.getLayoutParams() instanceof MarginLayoutParams) {
1122 MarginLayoutParams mlp =
1123 (MarginLayoutParams) mWindow.mContentParent.getLayoutParams();
1124 mlp.bottomMargin = insets.getSystemWindowInsetBottom();
1125 mWindow.mContentParent.setLayoutParams(mlp);
1126 }
1127 }
1128 // position the navigation guard view, creating it if necessary
1129 if (mNavigationGuard == null) {
1130 mNavigationGuard = new View(mContext);
1131 mNavigationGuard.setBackgroundColor(mContext.getColor(
1132 R.color.input_method_navigation_guard));
1133 addView(mNavigationGuard, indexOfChild(mNavigationColorViewState.view),
1134 new LayoutParams(LayoutParams.MATCH_PARENT,
1135 insets.getSystemWindowInsetBottom(),
1136 Gravity.START | Gravity.BOTTOM));
1137 } else {
1138 LayoutParams lp = (LayoutParams) mNavigationGuard.getLayoutParams();
1139 lp.height = insets.getSystemWindowInsetBottom();
1140 mNavigationGuard.setLayoutParams(lp);
1141 }
1142 }
1143 }
1144
1145 private void drawableChanged() {
1146 if (mChanging) {
1147 return;
1148 }
1149
1150 setPadding(mFramePadding.left + mBackgroundPadding.left,
1151 mFramePadding.top + mBackgroundPadding.top,
1152 mFramePadding.right + mBackgroundPadding.right,
1153 mFramePadding.bottom + mBackgroundPadding.bottom);
1154 requestLayout();
1155 invalidate();
1156
1157 int opacity = PixelFormat.OPAQUE;
1158 if (windowHasShadow()) {
1159 // If the window has a shadow, it must be translucent.
1160 opacity = PixelFormat.TRANSLUCENT;
1161 } else{
1162 // Note: If there is no background, we will assume opaque. The
1163 // common case seems to be that an application sets there to be
1164 // no background so it can draw everything itself. For that,
1165 // we would like to assume OPAQUE and let the app force it to
1166 // the slower TRANSLUCENT mode if that is really what it wants.
1167 Drawable bg = getBackground();
1168 Drawable fg = getForeground();
1169 if (bg != null) {
1170 if (fg == null) {
1171 opacity = bg.getOpacity();
1172 } else if (mFramePadding.left <= 0 && mFramePadding.top <= 0
1173 && mFramePadding.right <= 0 && mFramePadding.bottom <= 0) {
1174 // If the frame padding is zero, then we can be opaque
1175 // if either the frame -or- the background is opaque.
1176 int fop = fg.getOpacity();
1177 int bop = bg.getOpacity();
1178 if (false)
1179 Log.v(TAG, "Background opacity: " + bop + ", Frame opacity: " + fop);
1180 if (fop == PixelFormat.OPAQUE || bop == PixelFormat.OPAQUE) {
1181 opacity = PixelFormat.OPAQUE;
1182 } else if (fop == PixelFormat.UNKNOWN) {
1183 opacity = bop;
1184 } else if (bop == PixelFormat.UNKNOWN) {
1185 opacity = fop;
1186 } else {
1187 opacity = Drawable.resolveOpacity(fop, bop);
1188 }
1189 } else {
1190 // For now we have to assume translucent if there is a
1191 // frame with padding... there is no way to tell if the
1192 // frame and background together will draw all pixels.
1193 if (false)
1194 Log.v(TAG, "Padding: " + mFramePadding);
1195 opacity = PixelFormat.TRANSLUCENT;
1196 }
1197 }
1198 if (false)
1199 Log.v(TAG, "Background: " + bg + ", Frame: " + fg);
1200 }
1201
1202 if (false)
1203 Log.v(TAG, "Selected default opacity: " + opacity);
1204
1205 mDefaultOpacity = opacity;
1206 if (mFeatureId < 0) {
1207 mWindow.setDefaultWindowFormat(opacity);
1208 }
1209 }
1210
1211 @Override
1212 public void onWindowFocusChanged(boolean hasWindowFocus) {
1213 super.onWindowFocusChanged(hasWindowFocus);
1214
1215 // If the user is chording a menu shortcut, release the chord since
1216 // this window lost focus
1217 if (mWindow.hasFeature(Window.FEATURE_OPTIONS_PANEL) && !hasWindowFocus
1218 && mWindow.mPanelChordingKey != 0) {
1219 mWindow.closePanel(Window.FEATURE_OPTIONS_PANEL);
1220 }
1221
1222 final Window.Callback cb = mWindow.getCallback();
1223 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1224 cb.onWindowFocusChanged(hasWindowFocus);
1225 }
1226
1227 if (mPrimaryActionMode != null) {
1228 mPrimaryActionMode.onWindowFocusChanged(hasWindowFocus);
1229 }
1230 if (mFloatingActionMode != null) {
1231 mFloatingActionMode.onWindowFocusChanged(hasWindowFocus);
1232 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001233
1234 updateElevation();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001235 }
1236
1237 @Override
1238 protected void onAttachedToWindow() {
1239 super.onAttachedToWindow();
1240
1241 final Window.Callback cb = mWindow.getCallback();
1242 if (cb != null && !mWindow.isDestroyed() && mFeatureId < 0) {
1243 cb.onAttachedToWindow();
1244 }
1245
1246 if (mFeatureId == -1) {
1247 /*
1248 * The main window has been attached, try to restore any panels
1249 * that may have been open before. This is called in cases where
1250 * an activity is being killed for configuration change and the
1251 * menu was open. When the activity is recreated, the menu
1252 * should be shown again.
1253 */
1254 mWindow.openPanelsAfterRestore();
1255 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001256
1257 if (!mWindowResizeCallbacksAdded) {
1258 // If there is no window callback installed there was no window set before. Set it now.
1259 // Note that our ViewRootImpl object will not change.
1260 getViewRootImpl().addWindowCallbacks(this);
1261 mWindowResizeCallbacksAdded = true;
1262 } else if (mBackdropFrameRenderer != null) {
1263 // We are resizing and this call happened due to a configuration change. Tell the
1264 // renderer about it.
1265 mBackdropFrameRenderer.onConfigurationChange();
1266 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001267 }
1268
1269 @Override
1270 protected void onDetachedFromWindow() {
1271 super.onDetachedFromWindow();
1272
1273 final Window.Callback cb = mWindow.getCallback();
1274 if (cb != null && mFeatureId < 0) {
1275 cb.onDetachedFromWindow();
1276 }
1277
1278 if (mWindow.mDecorContentParent != null) {
1279 mWindow.mDecorContentParent.dismissPopups();
1280 }
1281
1282 if (mPrimaryActionModePopup != null) {
1283 removeCallbacks(mShowPrimaryActionModePopup);
1284 if (mPrimaryActionModePopup.isShowing()) {
1285 mPrimaryActionModePopup.dismiss();
1286 }
1287 mPrimaryActionModePopup = null;
1288 }
1289 if (mFloatingToolbar != null) {
1290 mFloatingToolbar.dismiss();
1291 mFloatingToolbar = null;
1292 }
1293
1294 PhoneWindow.PanelFeatureState st = mWindow.getPanelState(Window.FEATURE_OPTIONS_PANEL, false);
1295 if (st != null && st.menu != null && mFeatureId < 0) {
1296 st.menu.close();
1297 }
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001298
1299 if (mWindowResizeCallbacksAdded) {
1300 getViewRootImpl().removeWindowCallbacks(this);
1301 mWindowResizeCallbacksAdded = false;
1302 }
Wale Ogunwale8804af22015-11-17 09:18:15 -08001303 }
1304
1305 @Override
1306 public void onCloseSystemDialogs(String reason) {
1307 if (mFeatureId >= 0) {
1308 mWindow.closeAllPanels();
1309 }
1310 }
1311
1312 public android.view.SurfaceHolder.Callback2 willYouTakeTheSurface() {
1313 return mFeatureId < 0 ? mWindow.mTakeSurfaceCallback : null;
1314 }
1315
1316 public InputQueue.Callback willYouTakeTheInputQueue() {
1317 return mFeatureId < 0 ? mWindow.mTakeInputQueueCallback : null;
1318 }
1319
1320 public void setSurfaceType(int type) {
1321 mWindow.setType(type);
1322 }
1323
1324 public void setSurfaceFormat(int format) {
1325 mWindow.setFormat(format);
1326 }
1327
1328 public void setSurfaceKeepScreenOn(boolean keepOn) {
1329 if (keepOn) mWindow.addFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1330 else mWindow.clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON);
1331 }
1332
1333 @Override
1334 public void onRootViewScrollYChanged(int rootScrollY) {
1335 mRootScrollY = rootScrollY;
1336 updateColorViewTranslations();
1337 }
1338
1339 private ActionMode createActionMode(
1340 int type, ActionMode.Callback2 callback, View originatingView) {
1341 switch (type) {
1342 case ActionMode.TYPE_PRIMARY:
1343 default:
1344 return createStandaloneActionMode(callback);
1345 case ActionMode.TYPE_FLOATING:
1346 return createFloatingActionMode(originatingView, callback);
1347 }
1348 }
1349
1350 private void setHandledActionMode(ActionMode mode) {
1351 if (mode.getType() == ActionMode.TYPE_PRIMARY) {
1352 setHandledPrimaryActionMode(mode);
1353 } else if (mode.getType() == ActionMode.TYPE_FLOATING) {
1354 setHandledFloatingActionMode(mode);
1355 }
1356 }
1357
1358 private ActionMode createStandaloneActionMode(ActionMode.Callback callback) {
1359 endOnGoingFadeAnimation();
1360 cleanupPrimaryActionMode();
1361 if (mPrimaryActionModeView == null) {
1362 if (mWindow.isFloating()) {
1363 // Use the action bar theme.
1364 final TypedValue outValue = new TypedValue();
1365 final Resources.Theme baseTheme = mContext.getTheme();
1366 baseTheme.resolveAttribute(R.attr.actionBarTheme, outValue, true);
1367
1368 final Context actionBarContext;
1369 if (outValue.resourceId != 0) {
1370 final Resources.Theme actionBarTheme = mContext.getResources().newTheme();
1371 actionBarTheme.setTo(baseTheme);
1372 actionBarTheme.applyStyle(outValue.resourceId, true);
1373
1374 actionBarContext = new ContextThemeWrapper(mContext, 0);
1375 actionBarContext.getTheme().setTo(actionBarTheme);
1376 } else {
1377 actionBarContext = mContext;
1378 }
1379
1380 mPrimaryActionModeView = new ActionBarContextView(actionBarContext);
1381 mPrimaryActionModePopup = new PopupWindow(actionBarContext, null,
1382 R.attr.actionModePopupWindowStyle);
1383 mPrimaryActionModePopup.setWindowLayoutType(
1384 WindowManager.LayoutParams.TYPE_APPLICATION);
1385 mPrimaryActionModePopup.setContentView(mPrimaryActionModeView);
1386 mPrimaryActionModePopup.setWidth(MATCH_PARENT);
1387
1388 actionBarContext.getTheme().resolveAttribute(
1389 R.attr.actionBarSize, outValue, true);
1390 final int height = TypedValue.complexToDimensionPixelSize(outValue.data,
1391 actionBarContext.getResources().getDisplayMetrics());
1392 mPrimaryActionModeView.setContentHeight(height);
1393 mPrimaryActionModePopup.setHeight(WRAP_CONTENT);
1394 mShowPrimaryActionModePopup = new Runnable() {
1395 public void run() {
1396 mPrimaryActionModePopup.showAtLocation(
1397 mPrimaryActionModeView.getApplicationWindowToken(),
1398 Gravity.TOP | Gravity.FILL_HORIZONTAL, 0, 0);
1399 endOnGoingFadeAnimation();
1400 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1401 0f, 1f);
1402 mFadeAnim.addListener(new Animator.AnimatorListener() {
1403 @Override
1404 public void onAnimationStart(Animator animation) {
1405 mPrimaryActionModeView.setVisibility(VISIBLE);
1406 }
1407
1408 @Override
1409 public void onAnimationEnd(Animator animation) {
1410 mPrimaryActionModeView.setAlpha(1f);
1411 mFadeAnim = null;
1412 }
1413
1414 @Override
1415 public void onAnimationCancel(Animator animation) {
1416
1417 }
1418
1419 @Override
1420 public void onAnimationRepeat(Animator animation) {
1421
1422 }
1423 });
1424 mFadeAnim.start();
1425 }
1426 };
1427 } else {
1428 ViewStub stub = (ViewStub) findViewById(R.id.action_mode_bar_stub);
1429 if (stub != null) {
1430 mPrimaryActionModeView = (ActionBarContextView) stub.inflate();
1431 }
1432 }
1433 }
1434 if (mPrimaryActionModeView != null) {
1435 mPrimaryActionModeView.killMode();
1436 ActionMode mode = new StandaloneActionMode(
1437 mPrimaryActionModeView.getContext(), mPrimaryActionModeView,
1438 callback, mPrimaryActionModePopup == null);
1439 return mode;
1440 }
1441 return null;
1442 }
1443
1444 private void endOnGoingFadeAnimation() {
1445 if (mFadeAnim != null) {
1446 mFadeAnim.end();
1447 }
1448 }
1449
1450 private void setHandledPrimaryActionMode(ActionMode mode) {
1451 endOnGoingFadeAnimation();
1452 mPrimaryActionMode = mode;
1453 mPrimaryActionMode.invalidate();
1454 mPrimaryActionModeView.initForMode(mPrimaryActionMode);
1455 if (mPrimaryActionModePopup != null) {
1456 post(mShowPrimaryActionModePopup);
1457 } else {
1458 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA, 0f, 1f);
1459 mFadeAnim.addListener(new Animator.AnimatorListener() {
1460 @Override
1461 public void onAnimationStart(Animator animation) {
1462 mPrimaryActionModeView.setVisibility(View.VISIBLE);
1463 }
1464
1465 @Override
1466 public void onAnimationEnd(Animator animation) {
1467 mPrimaryActionModeView.setAlpha(1f);
1468 mFadeAnim = null;
1469 }
1470
1471 @Override
1472 public void onAnimationCancel(Animator animation) {
1473
1474 }
1475
1476 @Override
1477 public void onAnimationRepeat(Animator animation) {
1478
1479 }
1480 });
1481 mFadeAnim.start();
1482 }
1483 mPrimaryActionModeView.sendAccessibilityEvent(
1484 AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED);
1485 }
1486
1487 private ActionMode createFloatingActionMode(
1488 View originatingView, ActionMode.Callback2 callback) {
1489 if (mFloatingActionMode != null) {
1490 mFloatingActionMode.finish();
1491 }
1492 cleanupFloatingActionModeViews();
1493 final FloatingActionMode mode =
1494 new FloatingActionMode(mContext, callback, originatingView);
1495 mFloatingActionModeOriginatingView = originatingView;
1496 mFloatingToolbarPreDrawListener =
1497 new ViewTreeObserver.OnPreDrawListener() {
1498 @Override
1499 public boolean onPreDraw() {
1500 mode.updateViewLocationInWindow();
1501 return true;
1502 }
1503 };
1504 return mode;
1505 }
1506
1507 private void setHandledFloatingActionMode(ActionMode mode) {
1508 mFloatingActionMode = mode;
1509 mFloatingToolbar = new FloatingToolbar(mContext, mWindow);
1510 ((FloatingActionMode) mFloatingActionMode).setFloatingToolbar(mFloatingToolbar);
1511 mFloatingActionMode.invalidate(); // Will show the floating toolbar if necessary.
1512 mFloatingActionModeOriginatingView.getViewTreeObserver()
1513 .addOnPreDrawListener(mFloatingToolbarPreDrawListener);
1514 }
1515
1516 /**
1517 * Informs the decor if a non client decor is attached and visible.
1518 * @param attachedAndVisible true when the decor is visible.
1519 * Note that this will even be called if there is no non client decor.
1520 **/
1521 void enableNonClientDecor(boolean attachedAndVisible) {
1522 if (mHasNonClientDecor != attachedAndVisible) {
1523 mHasNonClientDecor = attachedAndVisible;
1524 if (getForeground() != null) {
1525 drawableChanged();
1526 }
1527 }
1528 }
1529
1530 /**
1531 * Returns true if the window has a non client decor.
1532 * @return If there is a non client decor - even if it is not visible.
1533 **/
1534 private boolean windowHasNonClientDecor() {
1535 return mHasNonClientDecor;
1536 }
1537
1538 /**
1539 * Returns true if the Window is free floating and has a shadow (although at some times
1540 * it might not be displaying it, e.g. during a resize). Note that non overlapping windows
1541 * do not have a shadow since it could not be seen anyways (a small screen / tablet
1542 * "tiles" the windows side by side but does not overlap them).
1543 * @return Returns true when the window has a shadow created by the non client decor.
1544 **/
1545 private boolean windowHasShadow() {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001546 return windowHasNonClientDecor() && ActivityManager.StackId.hasWindowShadow(mWorkspaceId);
Wale Ogunwale8804af22015-11-17 09:18:15 -08001547 }
1548
1549 void setWindow(PhoneWindow phoneWindow) {
1550 mWindow = phoneWindow;
1551 Context context = getContext();
1552 if (context instanceof DecorContext) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001553 DecorContext decorContext = (DecorContext) context;
1554 decorContext.setPhoneWindow(mWindow);
1555 }
1556 }
1557
1558 void onConfigurationChanged() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001559 int workspaceId = getWorkspaceId();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001560 if (mNonClientDecorView != null) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001561 if (mWorkspaceId != workspaceId) {
1562 mWorkspaceId = workspaceId;
1563 // We might have to change the kind of surface before we do anything else.
1564 mNonClientDecorView.onConfigurationChanged(
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001565 ActivityManager.StackId.hasWindowDecor(mWorkspaceId));
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001566 enableNonClientDecor(ActivityManager.StackId.hasWindowDecor(workspaceId));
1567 }
1568 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001569 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001570 }
1571
1572 View onResourcesLoaded(LayoutInflater inflater, int layoutResource) {
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001573 mWorkspaceId = getWorkspaceId();
1574
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001575 mResizingBackgroundDrawable = getResizingBackgroundDrawable(
1576 mWindow.mBackgroundResource, mWindow.mBackgroundFallbackResource);
1577 mCaptionBackgroundDrawable =
1578 getContext().getDrawable(R.drawable.non_client_decor_title_focused);
1579
1580 if (mBackdropFrameRenderer != null) {
1581 mBackdropFrameRenderer.onResourcesLoaded(
1582 this, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);
1583 }
1584
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001585 mNonClientDecorView = createNonClientDecorView(inflater);
1586 final View root = inflater.inflate(layoutResource, null);
1587 if (mNonClientDecorView != null) {
1588 if (mNonClientDecorView.getParent() == null) {
1589 addView(mNonClientDecorView,
1590 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1591 }
1592 mNonClientDecorView.addView(root,
1593 new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1594 } else {
1595 addView(root, new ViewGroup.LayoutParams(MATCH_PARENT, MATCH_PARENT));
1596 }
1597 mContentRoot = (ViewGroup) root;
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001598 initializeElevation();
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001599 return root;
1600 }
1601
1602 // Free floating overlapping windows require a non client decor with a caption and shadow..
1603 private NonClientDecorView createNonClientDecorView(LayoutInflater inflater) {
1604 NonClientDecorView nonClientDecorView = null;
1605 for (int i = getChildCount() - 1; i >= 0 && nonClientDecorView == null; i--) {
1606 View view = getChildAt(i);
1607 if (view instanceof NonClientDecorView) {
1608 // The decor was most likely saved from a relaunch - so reuse it.
1609 nonClientDecorView = (NonClientDecorView) view;
1610 removeViewAt(i);
1611 }
1612 }
1613 final WindowManager.LayoutParams attrs = mWindow.getAttributes();
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001614 final boolean isApplication = attrs.type == TYPE_BASE_APPLICATION ||
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001615 attrs.type == TYPE_APPLICATION;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001616 // Only a non floating application window on one of the allowed workspaces can get a non
1617 // client decor.
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001618 if (!mWindow.isFloating() && isApplication
1619 && ActivityManager.StackId.hasWindowDecor(mWorkspaceId)) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001620 // Dependent on the brightness of the used title we either use the
1621 // dark or the light button frame.
1622 if (nonClientDecorView == null) {
1623 Context context = getContext();
1624 TypedValue value = new TypedValue();
1625 context.getTheme().resolveAttribute(R.attr.colorPrimary, value, true);
1626 inflater = inflater.from(context);
1627 if (Color.luminance(value.data) < 0.5) {
1628 nonClientDecorView = (NonClientDecorView) inflater.inflate(
1629 R.layout.non_client_decor_dark, null);
1630 } else {
1631 nonClientDecorView = (NonClientDecorView) inflater.inflate(
1632 R.layout.non_client_decor_light, null);
1633 }
1634 }
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001635 nonClientDecorView.setPhoneWindow(mWindow, true /*showDecor*/);
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001636 } else {
1637 nonClientDecorView = null;
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001638 }
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001639
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001640 // Tell the decor if it has a visible non client decor.
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001641 enableNonClientDecor(nonClientDecorView != null);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001642 return nonClientDecorView;
1643 }
1644
1645 /**
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001646 * Returns the color used to fill areas the app has not rendered content to yet when the
1647 * user is resizing the window of an activity in multi-window mode.
1648 */
1649 private Drawable getResizingBackgroundDrawable(int backgroundRes, int backgroundFallbackRes) {
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001650 final Context context = getContext();
1651
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001652 if (backgroundRes != 0) {
1653 final Drawable drawable = context.getDrawable(backgroundRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001654 if (drawable != null) {
1655 return drawable;
1656 }
1657 }
1658
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001659 if (backgroundFallbackRes != 0) {
1660 final Drawable fallbackDrawable = context.getDrawable(backgroundFallbackRes);
Wale Ogunwale0d7e9122015-11-17 10:45:06 -08001661 if (fallbackDrawable != null) {
1662 return fallbackDrawable;
1663 }
1664 }
1665
1666 // We shouldn't really get here as the background fallback should be always available since
1667 // it is defaulted by the system.
1668 Log.w(TAG, "Failed to find background drawable for PhoneWindow=" + mWindow);
1669 return null;
1670 }
1671
1672 /**
1673 * Returns the Id of the workspace which contains this window.
1674 * Note that if no workspace can be determined - which usually means that it was not
1675 * created for an activity - the fullscreen workspace ID will be returned.
1676 * @return Returns the workspace stack id which contains this window.
1677 **/
1678 private int getWorkspaceId() {
1679 int workspaceId = INVALID_STACK_ID;
1680 final Window.WindowControllerCallback callback = mWindow.getWindowControllerCallback();
1681 if (callback != null) {
1682 try {
1683 workspaceId = callback.getWindowStackId();
1684 } catch (RemoteException ex) {
1685 Log.e(TAG, "Failed to get the workspace ID of a PhoneWindow.");
1686 }
1687 }
1688 if (workspaceId == INVALID_STACK_ID) {
1689 return FULLSCREEN_WORKSPACE_STACK_ID;
1690 }
1691 return workspaceId;
1692 }
1693
1694 void clearContentView() {
1695 if (mNonClientDecorView != null) {
1696 if (mNonClientDecorView.getChildCount() > 1) {
1697 mNonClientDecorView.removeViewAt(1);
1698 }
1699 } else {
1700 // This window doesn't have non client decor, so we need to just remove the
1701 // children of the decor view.
1702 removeAllViews();
Wale Ogunwale8804af22015-11-17 09:18:15 -08001703 }
1704 }
1705
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001706 @Override
1707 public void onWindowSizeIsChanging(Rect newBounds) {
1708 if (mBackdropFrameRenderer != null) {
1709 mBackdropFrameRenderer.setTargetRect(newBounds);
1710 }
1711 }
1712
1713 @Override
1714 public void onWindowDragResizeStart(Rect initialBounds) {
1715 if (mWindow.isDestroyed()) {
1716 // If the owner's window is gone, we should not be able to come here anymore.
1717 releaseThreadedRenderer();
1718 return;
1719 }
1720 if (mBackdropFrameRenderer != null) {
1721 return;
1722 }
1723 final ThreadedRenderer renderer = (ThreadedRenderer) getHardwareRenderer();
1724 if (renderer != null) {
1725 mBackdropFrameRenderer = new BackdropFrameRenderer(this, renderer,
1726 initialBounds, mResizingBackgroundDrawable, mCaptionBackgroundDrawable);
1727
1728 // Get rid of the shadow while we are resizing. Shadow drawing takes considerable time.
1729 // If we want to get the shadow shown while resizing, we would need to elevate a new
1730 // element which owns the caption and has the elevation.
1731 updateElevation();
1732 }
1733 }
1734
1735 @Override
1736 public void onWindowDragResizeEnd() {
1737 releaseThreadedRenderer();
1738 }
1739
1740 @Override
1741 public boolean onContentDrawn(int offsetX, int offsetY, int sizeX, int sizeY) {
1742 if (mBackdropFrameRenderer == null) {
1743 return false;
1744 }
1745 return mBackdropFrameRenderer.onContentDrawn(offsetX, offsetY, sizeX, sizeY);
1746 }
1747
1748 @Override
1749 public void onRequestDraw(boolean reportNextDraw) {
1750 if (mBackdropFrameRenderer != null) {
1751 mBackdropFrameRenderer.onRequestDraw(reportNextDraw);
1752 } else if (reportNextDraw) {
1753 // If render thread is gone, just report immediately.
1754 if (isAttachedToWindow()) {
1755 getViewRootImpl().reportDrawFinish();
1756 }
1757 }
1758 }
1759
1760 /** Release the renderer thread which is usually done when the user stops resizing. */
1761 private void releaseThreadedRenderer() {
1762 if (mBackdropFrameRenderer != null) {
1763 mBackdropFrameRenderer.releaseRenderer();
1764 mBackdropFrameRenderer = null;
1765 // Bring the shadow back.
1766 updateElevation();
1767 }
1768 }
1769
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001770 /**
1771 * The elevation gets set for the first time and the framework needs to be informed that
1772 * the surface layer gets created with the shadow size in mind.
1773 */
1774 private void initializeElevation() {
1775 // TODO(skuhne): Call setMaxElevation here accordingly after b/22668382 got fixed.
1776 mAllowUpdateElevation = false;
1777 updateElevation();
1778 }
1779
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001780 private void updateElevation() {
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001781 float elevation = 0;
1782 final boolean wasAdjustedForStack = mElevationAdjustedForStack;
1783 // Do not use a shadow when we are in resizing mode (mBackdropFrameRenderer not null)
1784 // since the shadow is bound to the content size and not the target size.
1785 if (ActivityManager.StackId.hasWindowShadow(mWorkspaceId)
1786 && mBackdropFrameRenderer == null) {
1787 elevation = hasWindowFocus() ?
1788 DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP : DECOR_SHADOW_UNFOCUSED_HEIGHT_IN_DIP;
1789 // TODO(skuhne): Remove this if clause once b/22668382 got fixed.
1790 if (!mAllowUpdateElevation) {
1791 elevation = DECOR_SHADOW_FOCUSED_HEIGHT_IN_DIP;
1792 }
1793 // Convert the DP elevation into physical pixels.
1794 elevation = dipToPx(elevation);
1795 mElevationAdjustedForStack = true;
1796 } else {
1797 mElevationAdjustedForStack = false;
1798 }
1799
1800 // Don't change the elevation if we didn't previously adjust it for the stack it was in
1801 // or it didn't change.
1802 if ((wasAdjustedForStack || mElevationAdjustedForStack)
1803 && getElevation() != elevation) {
1804 mWindow.setElevation(elevation);
Wale Ogunwalebf9eefc2015-11-17 14:47:52 -08001805 }
1806 }
1807
Wale Ogunwale8cc5a742015-11-17 15:41:05 -08001808 boolean isShowingCaption() {
1809 return mNonClientDecorView != null && mNonClientDecorView.isShowingDecor();
1810 }
1811
1812 int getCaptionHeight() {
1813 return isShowingCaption() ? mNonClientDecorView.getDecorCaptionHeight() : 0;
1814 }
1815
Wale Ogunwale2b547c32015-11-18 10:33:22 -08001816 /**
1817 * Converts a DIP measure into physical pixels.
1818 * @param dip The dip value.
1819 * @return Returns the number of pixels.
1820 */
1821 private float dipToPx(float dip) {
1822 return TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP, dip,
1823 getResources().getDisplayMetrics());
1824 }
1825
Wale Ogunwale8804af22015-11-17 09:18:15 -08001826 private static class ColorViewState {
1827 View view = null;
1828 int targetVisibility = View.INVISIBLE;
1829 boolean present = false;
1830
1831 final int id;
1832 final int systemUiHideFlag;
1833 final int translucentFlag;
1834 final int verticalGravity;
1835 final int horizontalGravity;
1836 final String transitionName;
1837 final int hideWindowFlag;
1838
1839 ColorViewState(int systemUiHideFlag,
1840 int translucentFlag, int verticalGravity, int horizontalGravity,
1841 String transitionName, int id, int hideWindowFlag) {
1842 this.id = id;
1843 this.systemUiHideFlag = systemUiHideFlag;
1844 this.translucentFlag = translucentFlag;
1845 this.verticalGravity = verticalGravity;
1846 this.horizontalGravity = horizontalGravity;
1847 this.transitionName = transitionName;
1848 this.hideWindowFlag = hideWindowFlag;
1849 }
1850 }
1851
1852 /**
1853 * Clears out internal references when the action mode is destroyed.
1854 */
1855 private class ActionModeCallback2Wrapper extends ActionMode.Callback2 {
1856 private final ActionMode.Callback mWrapped;
1857
1858 public ActionModeCallback2Wrapper(ActionMode.Callback wrapped) {
1859 mWrapped = wrapped;
1860 }
1861
1862 public boolean onCreateActionMode(ActionMode mode, Menu menu) {
1863 return mWrapped.onCreateActionMode(mode, menu);
1864 }
1865
1866 public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
1867 requestFitSystemWindows();
1868 return mWrapped.onPrepareActionMode(mode, menu);
1869 }
1870
1871 public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
1872 return mWrapped.onActionItemClicked(mode, item);
1873 }
1874
1875 public void onDestroyActionMode(ActionMode mode) {
1876 mWrapped.onDestroyActionMode(mode);
1877 final boolean isMncApp = mContext.getApplicationInfo().targetSdkVersion
1878 >= Build.VERSION_CODES.M;
1879 final boolean isPrimary;
1880 final boolean isFloating;
1881 if (isMncApp) {
1882 isPrimary = mode == mPrimaryActionMode;
1883 isFloating = mode == mFloatingActionMode;
1884 if (!isPrimary && mode.getType() == ActionMode.TYPE_PRIMARY) {
1885 Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_PRIMARY; "
1886 + mode + " was not the current primary action mode! Expected "
1887 + mPrimaryActionMode);
1888 }
1889 if (!isFloating && mode.getType() == ActionMode.TYPE_FLOATING) {
1890 Log.e(TAG, "Destroying unexpected ActionMode instance of TYPE_FLOATING; "
1891 + mode + " was not the current floating action mode! Expected "
1892 + mFloatingActionMode);
1893 }
1894 } else {
1895 isPrimary = mode.getType() == ActionMode.TYPE_PRIMARY;
1896 isFloating = mode.getType() == ActionMode.TYPE_FLOATING;
1897 }
1898 if (isPrimary) {
1899 if (mPrimaryActionModePopup != null) {
1900 removeCallbacks(mShowPrimaryActionModePopup);
1901 }
1902 if (mPrimaryActionModeView != null) {
1903 endOnGoingFadeAnimation();
1904 mFadeAnim = ObjectAnimator.ofFloat(mPrimaryActionModeView, View.ALPHA,
1905 1f, 0f);
1906 mFadeAnim.addListener(new Animator.AnimatorListener() {
1907 @Override
1908 public void onAnimationStart(Animator animation) {
1909
1910 }
1911
1912 @Override
1913 public void onAnimationEnd(Animator animation) {
1914 mPrimaryActionModeView.setVisibility(GONE);
1915 if (mPrimaryActionModePopup != null) {
1916 mPrimaryActionModePopup.dismiss();
1917 }
1918 mPrimaryActionModeView.removeAllViews();
1919 mFadeAnim = null;
1920 }
1921
1922 @Override
1923 public void onAnimationCancel(Animator animation) {
1924
1925 }
1926
1927 @Override
1928 public void onAnimationRepeat(Animator animation) {
1929
1930 }
1931 });
1932 mFadeAnim.start();
1933 }
1934
1935 mPrimaryActionMode = null;
1936 } else if (isFloating) {
1937 cleanupFloatingActionModeViews();
1938 mFloatingActionMode = null;
1939 }
1940 if (mWindow.getCallback() != null && !mWindow.isDestroyed()) {
1941 try {
1942 mWindow.getCallback().onActionModeFinished(mode);
1943 } catch (AbstractMethodError ame) {
1944 // Older apps might not implement this callback method.
1945 }
1946 }
1947 requestFitSystemWindows();
1948 }
1949
1950 @Override
1951 public void onGetContentRect(ActionMode mode, View view, Rect outRect) {
1952 if (mWrapped instanceof ActionMode.Callback2) {
1953 ((ActionMode.Callback2) mWrapped).onGetContentRect(mode, view, outRect);
1954 } else {
1955 super.onGetContentRect(mode, view, outRect);
1956 }
1957 }
1958 }
1959}