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