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