Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(972)

Side by Side Diff: chrome/android/java/src/org/chromium/chrome/browser/fullscreen/ChromeFullscreenManager.java

Issue 810853003: Upstream FullscreenManager (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 6 years ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4
5 package org.chromium.chrome.browser.fullscreen;
6
7 import android.animation.Animator;
8 import android.animation.AnimatorListenerAdapter;
9 import android.animation.ObjectAnimator;
10 import android.app.Activity;
11 import android.content.res.Resources;
12 import android.os.Build;
13 import android.os.Handler;
14 import android.os.Message;
15 import android.os.SystemClock;
16 import android.util.Property;
17 import android.view.Gravity;
18 import android.view.MotionEvent;
19 import android.view.View;
20 import android.view.ViewGroup;
21 import android.view.ViewGroup.LayoutParams;
22 import android.view.Window;
23 import android.widget.FrameLayout;
24
25 import org.chromium.base.ActivityState;
26 import org.chromium.base.ApiCompatibilityUtils;
27 import org.chromium.base.ApplicationStatus;
28 import org.chromium.base.ApplicationStatus.ActivityStateListener;
29 import org.chromium.base.BaseChromiumApplication;
30 import org.chromium.base.BaseChromiumApplication.WindowFocusChangedListener;
31 import org.chromium.base.TraceEvent;
32 import org.chromium.base.VisibleForTesting;
33 import org.chromium.chrome.browser.Tab;
34 import org.chromium.chrome.browser.fullscreen.FullscreenHtmlApiHandler.Fullscree nHtmlApiDelegate;
35 import org.chromium.chrome.browser.tabmodel.TabModelSelector;
36 import org.chromium.content.browser.ContentViewCore;
37
38 import java.util.ArrayList;
39 import java.util.HashSet;
40
41 /**
42 * A class that manages control and content views to create the fullscreen mode.
43 */
44 public class ChromeFullscreenManager
45 extends FullscreenManager implements ActivityStateListener, WindowFocusC hangedListener {
46 // Minimum showtime of the toolbar (in ms).
47 private static final long MINIMUM_SHOW_DURATION_MS = 3000;
48
49 // Maximum length of the slide in/out animation of the toolbar (in ms).
50 private static final long MAX_ANIMATION_DURATION_MS = 500;
51
52 private static final int MSG_ID_CONTROLS_REQUEST_LAYOUT = 1;
53 private static final int MSG_ID_HIDE_CONTROLS = 2;
54
55 private final HashSet<Integer> mPersistentControlTokens = new HashSet<Intege r>();
56
57 private final Activity mActivity;
58 private final Window mWindow;
59 private final Handler mHandler;
60 private final int mControlContainerHeight;
61
62 private View mControlContainer;
63
64 private long mMinShowNotificationMs = MINIMUM_SHOW_DURATION_MS;
65 private long mMaxAnimationDurationMs = MAX_ANIMATION_DURATION_MS;
66
67 private final boolean mEnabled;
68
69 private float mBrowserControlOffset = Float.NaN;
70 private float mRendererControlOffset = Float.NaN;
71 private float mRendererContentOffset;
72 private float mPreviousContentOffset;
73 private float mControlOffset;
74 private float mPreviousControlOffset;
75 private boolean mIsEnteringPersistentModeState;
76
77 private boolean mInGesture;
78 private boolean mContentViewScrolling;
79
80 private int mPersistentControlsCurrentToken;
81 private long mCurrentShowTime;
82
83 private ObjectAnimator mControlAnimation;
84 private boolean mCurrentAnimationIsShowing;
85
86 private boolean mDisableBrowserOverride;
87
88 private boolean mTopControlsPermanentlyHidden;
89 private boolean mTopControlsAndroidViewHidden;
90
91 private final ArrayList<FullscreenListener> mListeners = new ArrayList<Fulls creenListener>();
92
93 /**
94 * A listener that gets notified of changes to the fullscreen state.
95 */
96 public interface FullscreenListener {
97 /**
98 * Called whenever the content's offset changes.
99 * @param offset The new offset of the content from the top of the scree n.
100 */
101 public void onContentOffsetChanged(float offset);
102
103 /**
104 * Called whenever the content's visible offset changes.
105 * @param offset The new offset of the visible content from the top of t he screen.
106 */
107 public void onVisibleContentOffsetChanged(float offset);
108
109 /**
110 * Called when a ContentVideoView is created/destroyed.
111 * @param enabled Whether to enter or leave overlay video mode.
112 */
113 public void onToggleOverlayVideoMode(boolean enabled);
114 }
115
116 private class ControlsOffsetProperty extends Property<ChromeFullscreenManage r, Float> {
117 public ControlsOffsetProperty() {
118 super(Float.class, "controlsOffset");
119 }
120
121 @Override
122 public Float get(ChromeFullscreenManager object) {
123 return getControlOffset();
124 }
125
126 @Override
127 public void set(ChromeFullscreenManager manager, Float offset) {
128 if (mDisableBrowserOverride) return;
129 float browserOffset = offset.floatValue();
130 if (Float.compare(mBrowserControlOffset, browserOffset) == 0) return ;
131 mBrowserControlOffset = browserOffset;
132 manager.updateControlOffset();
133 manager.updateVisuals();
134 }
135 }
136
137 private final Runnable mUpdateVisibilityRunnable = new Runnable() {
138 @Override
139 public void run() {
140 int visibility = shouldShowAndroidControls() ? View.VISIBLE : View.I NVISIBLE;
141 if (mControlContainer.getVisibility() == visibility) return;
142 // requestLayout is required to trigger a new gatherTransparentRegio n(), which
143 // only occurs together with a layout and let's SurfaceFlinger trim overlays.
144 // This may be almost equivalent to using View.GONE, but we still us e View.INVISIBLE
145 // since drawing caches etc. won't be destroyed, and the layout may be less expensive.
146 mControlContainer.setVisibility(visibility);
147 mControlContainer.requestLayout();
148 }
149 };
150
151 /**
152 * Creates an instance of the fullscreen mode manager.
153 * @param activity The activity that supports fullscreen.
154 * @param controlContainer Container holding the controls (Toolbar).
155 * @param enabled Whether fullscreen is globally enabled.
156 * @param modelSelector The model selector providing access to the current t ab.
157 * @param fullscreenApiNotification A string for fullscreen api notification .
158 */
159 public ChromeFullscreenManager(Activity activity, View controlContainer, boo lean enabled,
160 boolean persistentFullscreenSupported, TabModelSelector modelSelecto r,
161 int resControlContainerHeight, String fullscreenApiNotification) {
Ted C 2014/12/17 17:52:00 Actually...one question I had was why we need to p
Jaekyun Seok (inactive) 2014/12/17 22:03:33 Done.
162 super(activity.getWindow(), modelSelector, enabled, persistentFullscreen Supported,
163 fullscreenApiNotification);
164
165 mActivity = activity;
166 ApplicationStatus.registerStateListenerForActivity(this, activity);
167 ((BaseChromiumApplication) activity.getApplication())
168 .registerWindowFocusChangedListener(this);
169
170 mWindow = activity.getWindow();
171 mHandler = new Handler() {
172 @Override
173 public void handleMessage(Message msg) {
174 if (msg == null) return;
175 switch (msg.what) {
176 case MSG_ID_CONTROLS_REQUEST_LAYOUT:
177 mControlContainer.requestLayout();
178 break;
179 case MSG_ID_HIDE_CONTROLS:
180 update(false);
181 break;
182 default:
183 assert false : "Unexpected message for ID: " + msg.what;
184 break;
185 }
186 }
187 };
188 setControlContainer(controlContainer);
189 Resources resources = mWindow.getContext().getResources();
190 mControlContainerHeight = resources.getDimensionPixelSize(resControlCont ainerHeight);
191 mRendererContentOffset = mControlContainerHeight;
192 mEnabled = enabled;
193 updateControlOffset();
194 }
195
196 /**
197 * @return Whether or not fullscreen is enabled.
198 */
199 public boolean isEnabled() {
200 return mEnabled;
201 }
202
203 /**
204 * Set the control container that is being hidden and shown when manipulatin g the fullscreen
205 * state.
206 * @param controlContainer The container at the top of the screen that conta ins the controls.
207 */
208 public void setControlContainer(View controlContainer) {
209 assert controlContainer != null;
210 mControlContainer = controlContainer;
211 }
212
213 @Override
214 public void onActivityStateChange(Activity activity, int newState) {
215 if (newState == ActivityState.STOPPED) {
216 // Exit fullscreen in onStop to ensure the system UI flags are set c orrectly when
217 // showing again (on JB MR2+ builds, the omnibox would be covered by the
218 // notification bar when this was done in onStart()).
219 setPersistentFullscreenMode(false);
220 } else if (newState == ActivityState.STARTED) {
221 showControlsTransient();
222 } else if (newState == ActivityState.DESTROYED) {
223 ApplicationStatus.unregisterActivityStateListener(this);
224 ((BaseChromiumApplication) mWindow.getContext().getApplicationContex t())
225 .unregisterWindowFocusChangedListener(this);
226 }
227 }
228
229 @Override
230 public void onWindowFocusChanged(Activity activity, boolean hasFocus) {
231 if (mActivity == activity) onWindowFocusChanged(hasFocus);
232 }
233
234 @Override
235 protected FullscreenHtmlApiDelegate createApiDelegate() {
236 return new FullscreenHtmlApiDelegate() {
237 @Override
238 public View getNotificationAnchorView() {
239 return mControlContainer;
240 }
241
242 @Override
243 public int getNotificationOffsetY() {
244 return (int) getControlOffset();
245 }
246
247 @Override
248 public void onEnterFullscreen() {
249 mIsEnteringPersistentModeState = true;
250 }
251
252 @Override
253 public boolean cancelPendingEnterFullscreen() {
254 boolean wasPending = mIsEnteringPersistentModeState;
255 mIsEnteringPersistentModeState = false;
256 return wasPending;
257 }
258
259 @Override
260 public void onFullscreenExited(ContentViewCore contentViewCore) {
261 contentViewCore.getWebContents().updateTopControlsState(false, t rue, true);
262 contentViewCore.getWebContents().updateTopControlsState(true, tr ue, false);
263 }
264
265 @Override
266 public boolean shouldShowNotificationBubble() {
267 return !isOverlayVideoMode();
268 }
269 };
270 }
271
272 /**
273 * Disables the ability for the browser to override the renderer provided to p controls
274 * position for testing.
275 */
276 @VisibleForTesting
277 public void disableBrowserOverrideForTest() {
278 mDisableBrowserOverride = true;
279 mPersistentControlTokens.clear();
280 mHandler.removeMessages(MSG_ID_HIDE_CONTROLS);
281 if (mControlAnimation != null) {
282 mControlAnimation.cancel();
283 mControlAnimation = null;
284 }
285 mBrowserControlOffset = Float.NaN;
286 updateVisuals();
287 }
288
289 /**
290 * Allows tests to override the animation durations for faster tests.
291 * @param minShowDuration The minimum time the controls must be shown.
292 * @param maxAnimationDuration The maximum animation time to show/hide the c ontrols.
293 */
294 @VisibleForTesting
295 public void setAnimationDurationsForTest(long minShowDuration, long maxAnima tionDuration) {
296 mMinShowNotificationMs = minShowDuration;
297 mMaxAnimationDurationMs = maxAnimationDuration;
298 }
299
300 @Override
301 public void showControlsTransient() {
302 if (mPersistentControlTokens.isEmpty()) update(true);
303 }
304
305 @Override
306 public int showControlsPersistent() {
307 int token = mPersistentControlsCurrentToken++;
308 mPersistentControlTokens.add(token);
309 if (mPersistentControlTokens.size() == 1) update(true);
310 return token;
311 }
312
313 @Override
314 public int showControlsPersistentAndClearOldToken(int oldToken) {
315 if (oldToken != INVALID_TOKEN) mPersistentControlTokens.remove(oldToken) ;
316 return showControlsPersistent();
317 }
318
319 @Override
320 public void hideControlsPersistent(int token) {
321 if (mPersistentControlTokens.remove(token) && mPersistentControlTokens.i sEmpty()) {
322 update(false);
323 }
324 }
325
326 /**
327 * @param remove Whether or not to forcefully remove the toolbar.
328 */
329 public void setTopControlsPermamentlyHidden(boolean remove) {
330 if (remove == mTopControlsPermanentlyHidden) return;
331 mTopControlsPermanentlyHidden = remove;
332 updateVisuals();
333 }
334
335 /**
336 * @return Whether or not the toolbar is forcefully being removed.
337 */
338 public boolean areTopControlsPermanentlyHidden() {
339 return mTopControlsPermanentlyHidden;
340 }
341
342 /**
343 * @return Whether the top controls should be drawn as a texture.
344 */
345 public boolean drawControlsAsTexture() {
346 return getControlOffset() > -mControlContainerHeight;
347 }
348
349 /**
350 * @return The height of the top controls in pixels.
351 */
352 public int getTopControlsHeight() {
353 return mEnabled ? mControlContainerHeight : 0;
354 }
355
356 @Override
357 public float getContentOffset() {
358 if (!mEnabled || mTopControlsPermanentlyHidden) return 0;
359 return rendererContentOffset();
360 }
361
362 /**
363 * @return The offset of the controls from the top of the screen.
364 */
365 public float getControlOffset() {
366 if (!mEnabled) return 0;
367 if (mTopControlsPermanentlyHidden) return -getTopControlsHeight();
368 return mControlOffset;
369 }
370
371 @SuppressWarnings("SelfEquality")
372 private void updateControlOffset() {
373 float offset = 0;
374 // Inline Float.isNan with "x != x":
375 final boolean isNaNBrowserControlOffset = mBrowserControlOffset != mBrow serControlOffset;
376 final float rendererControlOffset = rendererControlOffset();
377 final boolean isNaNRendererControlOffset = rendererControlOffset != rend ererControlOffset;
378 if (!isNaNBrowserControlOffset || !isNaNRendererControlOffset) {
379 offset = Math.max(
380 isNaNBrowserControlOffset ? -mControlContainerHeight : mBrow serControlOffset,
381 isNaNRendererControlOffset ? -mControlContainerHeight : rend ererControlOffset);
382 }
383 mControlOffset = offset;
384 }
385
386 @Override
387 public void setOverlayVideoMode(boolean enabled) {
388 super.setOverlayVideoMode(enabled);
389
390 for (int i = 0; i < mListeners.size(); i++) {
391 mListeners.get(i).onToggleOverlayVideoMode(enabled);
392 }
393 }
394
395 /**
396 * @return Whether the browser has a control offset override.
397 */
398 @VisibleForTesting
399 public boolean hasBrowserControlOffsetOverride() {
400 return !Float.isNaN(mBrowserControlOffset) || mControlAnimation != null
401 || !mPersistentControlTokens.isEmpty();
402 }
403
404 /**
405 * Returns how tall the opaque portion of the control container is.
406 */
407 public float controlContainerHeight() {
408 return mControlContainerHeight;
409 }
410
411 private float rendererContentOffset() {
412 if (!mEnabled) return mControlContainerHeight;
413 return mRendererContentOffset;
414 }
415
416 private float rendererControlOffset() {
417 if (!mEnabled) return 0;
418 return mRendererControlOffset;
419 }
420
421 /**
422 * @return The visible offset of the content from the top of the screen.
423 */
424 public float getVisibleContentOffset() {
425 if (!mEnabled) return 0;
426 return mControlContainerHeight + getControlOffset();
427 }
428
429 /**
430 * @param listener The {@link FullscreenListener} to be notified of fullscre en changes.
431 */
432 public void addListener(FullscreenListener listener) {
433 if (!mListeners.contains(listener)) mListeners.add(listener);
434 }
435
436 /**
437 * @param listener The {@link FullscreenListener} to no longer be notified o f fullscreen
438 * changes.
439 */
440 public void removeListener(FullscreenListener listener) {
441 mListeners.remove(listener);
442 }
443
444 /**
445 * Updates the content view's viewport size to have it render the content co rrectly.
446 *
447 * @param viewCore The ContentViewCore to update.
448 */
449 public void updateContentViewViewportSize(ContentViewCore viewCore) {
450 if (!mEnabled || viewCore == null) return;
451 if (mInGesture || mContentViewScrolling) return;
452
453 // Update content viewport size only when the top controls are not anima ting.
454 int contentOffset = (int) rendererContentOffset();
455 if (contentOffset != 0 && contentOffset != mControlContainerHeight) retu rn;
456 viewCore.setTopControlsHeight(mControlContainerHeight, contentOffset > 0 );
457 }
458
459 @Override
460 public void updateContentViewChildrenState() {
461 ContentViewCore contentViewCore = getActiveContentViewCore();
462 if (contentViewCore == null || !mEnabled) return;
463 ViewGroup view = contentViewCore.getContainerView();
464
465 float topViewsTranslation = (getControlOffset() + mControlContainerHeigh t);
466 applyTranslationToTopChildViews(view, topViewsTranslation);
467 applyMarginToFullChildViews(view, topViewsTranslation);
468 updateContentViewViewportSize(contentViewCore);
469 }
470
471 /**
472 * Utility routine for ensuring visibility updates are synchronized with
473 * animation, preventing message loop stalls due to untimely invalidation.
474 */
475 private void scheduleVisibilityUpdate() {
476 final int desiredVisibility = shouldShowAndroidControls() ? View.VISIBLE : View.INVISIBLE;
477 if (mControlContainer.getVisibility() == desiredVisibility) return;
478 mControlContainer.removeCallbacks(mUpdateVisibilityRunnable);
479 ApiCompatibilityUtils.postOnAnimation(mControlContainer, mUpdateVisibili tyRunnable);
480 }
481
482 private void updateVisuals() {
483 if (!mEnabled) return;
484
485 TraceEvent.begin("FullscreenManager:updateVisuals");
486
487 float offset = getControlOffset();
488 if (Float.compare(mPreviousControlOffset, offset) != 0) {
489 mPreviousControlOffset = offset;
490 getHtmlApiHandler().updateBubblePosition();
491
492 scheduleVisibilityUpdate();
493 if (shouldShowAndroidControls()) mControlContainer.setTranslationY(g etControlOffset());
494
495 // In ICS, the toolbar can appear clipped when compositor content is not being drawn
496 // beneath it (at the top of the page, during side swipe). Requesti ng a layout clears
497 // up the issue (see crbug.com/172631).
498 if (Build.VERSION.SDK_INT < Build.VERSION_CODES.JELLY_BEAN) {
499 if (!mHandler.hasMessages(MSG_ID_CONTROLS_REQUEST_LAYOUT)) {
500 mHandler.sendEmptyMessage(MSG_ID_CONTROLS_REQUEST_LAYOUT);
501 }
502 }
503 for (int i = 0; i < mListeners.size(); i++) {
504 mListeners.get(i).onVisibleContentOffsetChanged(getVisibleConten tOffset());
505 }
506 }
507
508 final ContentViewCore contentViewCore = getActiveContentViewCore();
509 if (contentViewCore != null && offset == -mControlContainerHeight
510 && mIsEnteringPersistentModeState) {
511 getHtmlApiHandler().enterFullscreen(contentViewCore);
512 mIsEnteringPersistentModeState = false;
513 }
514
515 updateContentViewChildrenState();
516
517 float contentOffset = getContentOffset();
518 if (Float.compare(mPreviousContentOffset, contentOffset) != 0) {
519 for (int i = 0; i < mListeners.size(); i++) {
520 mListeners.get(i).onContentOffsetChanged(contentOffset);
521 }
522 mPreviousContentOffset = contentOffset;
523 }
524
525 TraceEvent.end("FullscreenManager:updateVisuals");
526 }
527
528 /**
529 * @param hide Whether or not to force the top controls Android view to hide . If this is
530 * {@code false} the top controls Android view will show/hide ba sed on position, if
531 * it is {@code true} the top controls Android view will always be hidden.
532 */
533 public void setHideTopControlsAndroidView(boolean hide) {
534 if (mTopControlsAndroidViewHidden == hide) return;
535 mTopControlsAndroidViewHidden = hide;
536 scheduleVisibilityUpdate();
537 }
538
539 private boolean shouldShowAndroidControls() {
540 if (mTopControlsAndroidViewHidden) return false;
541
542 boolean showControls = getControlOffset() == 0;
543 ContentViewCore contentViewCore = getActiveContentViewCore();
544 if (contentViewCore == null) return showControls;
545 ViewGroup contentView = contentViewCore.getContainerView();
546
547 for (int i = 0; i < contentView.getChildCount(); i++) {
548 View child = contentView.getChildAt(i);
549 if (!(child.getLayoutParams() instanceof FrameLayout.LayoutParams)) continue;
550
551 FrameLayout.LayoutParams layoutParams =
552 (FrameLayout.LayoutParams) child.getLayoutParams();
553 if (Gravity.TOP == (layoutParams.gravity & Gravity.FILL_VERTICAL)) {
554 showControls = true;
555 break;
556 }
557 }
558
559 showControls |= !mPersistentControlTokens.isEmpty();
560
561 return showControls;
562 }
563
564 private void applyMarginToFullChildViews(ViewGroup contentView, float margin ) {
565 for (int i = 0; i < contentView.getChildCount(); i++) {
566 View child = contentView.getChildAt(i);
567 if (!(child.getLayoutParams() instanceof FrameLayout.LayoutParams)) continue;
568 FrameLayout.LayoutParams layoutParams =
569 (FrameLayout.LayoutParams) child.getLayoutParams();
570
571 if (layoutParams.height == LayoutParams.MATCH_PARENT
572 && layoutParams.topMargin != (int) margin) {
573 layoutParams.topMargin = (int) margin;
574 child.requestLayout();
575 TraceEvent.instant("FullscreenManager:child.requestLayout()");
576 }
577 }
578 }
579
580 private void applyTranslationToTopChildViews(ViewGroup contentView, float tr anslation) {
581 for (int i = 0; i < contentView.getChildCount(); i++) {
582 View child = contentView.getChildAt(i);
583 if (!(child.getLayoutParams() instanceof FrameLayout.LayoutParams)) continue;
584
585 FrameLayout.LayoutParams layoutParams =
586 (FrameLayout.LayoutParams) child.getLayoutParams();
587 if (Gravity.TOP == (layoutParams.gravity & Gravity.FILL_VERTICAL)) {
588 child.setTranslationY(translation);
589 TraceEvent.instant("FullscreenManager:child.setTranslationY()");
590 }
591 }
592 }
593
594 private ContentViewCore getActiveContentViewCore() {
595 Tab tab = getTabModelSelector().getCurrentTab();
596 return tab != null ? tab.getContentViewCore() : null;
597 }
598
599 @Override
600 public void setPositionsForTabToNonFullscreen() {
601 setPositionsForTab(0, mControlContainerHeight);
602 }
603
604 @Override
605 public void setPositionsForTab(float controlsOffset, float contentOffset) {
606 if (!mEnabled) return;
607 float rendererControlOffset =
608 Math.round(Math.max(controlsOffset, -mControlContainerHeight));
609 float rendererContentOffset = Math.min(
610 Math.round(contentOffset), rendererControlOffset + mControlConta inerHeight);
611
612 if (Float.compare(rendererControlOffset, mRendererControlOffset) == 0
613 && Float.compare(rendererContentOffset, mRendererContentOffset) == 0) {
614 return;
615 }
616
617 mRendererControlOffset = rendererControlOffset;
618 mRendererContentOffset = rendererContentOffset;
619 updateControlOffset();
620
621 if (mControlAnimation == null) updateVisuals();
622 }
623
624 /**
625 * @param e The dispatched motion event
626 * @return Whether or not this motion event is in the top control container area and should be
627 * consumed.
628 */
629 public boolean onInterceptMotionEvent(MotionEvent e) {
630 return mEnabled && e.getY() < getControlOffset() + mControlContainerHeig ht
631 && !mTopControlsAndroidViewHidden;
632 }
633
634 /**
635 * Notifies the fullscreen manager that a motion event has occurred.
636 * @param e The dispatched motion event.
637 */
638 public void onMotionEvent(MotionEvent e) {
639 if (!mEnabled) return;
640 int eventAction = e.getActionMasked();
641 if (eventAction == MotionEvent.ACTION_DOWN
642 || eventAction == MotionEvent.ACTION_POINTER_DOWN) {
643 mInGesture = true;
644 getHtmlApiHandler().hideNotificationBubble();
645 } else if (eventAction == MotionEvent.ACTION_CANCEL
646 || eventAction == MotionEvent.ACTION_UP) {
647 mInGesture = false;
648 updateVisuals();
649 }
650 }
651
652 private void update(boolean show) {
653 // On forced show/hide, reset the flags that may suppress ContentView re size.
654 // As this method is also called when tab is switched, this also cleanup the scrolling
655 // flag set based on the previous ContentView's scrolling state.
656 mInGesture = false;
657 mContentViewScrolling = false;
658
659 if (!mEnabled) return;
660
661 if (show) mCurrentShowTime = SystemClock.uptimeMillis();
662
663 boolean postHideMessage = false;
664 if (!show) {
665 if (mControlAnimation != null && mCurrentAnimationIsShowing) {
666 postHideMessage = true;
667 } else {
668 long timeDelta = SystemClock.uptimeMillis() - mCurrentShowTime;
669 animateIfNecessary(false, Math.max(mMinShowNotificationMs - time Delta, 0));
670 }
671 } else {
672 animateIfNecessary(true, 0);
673 if (mPersistentControlTokens.isEmpty()) postHideMessage = true;
674 }
675
676 mHandler.removeMessages(MSG_ID_HIDE_CONTROLS);
677 if (postHideMessage) {
678 long timeDelta = SystemClock.uptimeMillis() - mCurrentShowTime;
679 mHandler.sendEmptyMessageDelayed(
680 MSG_ID_HIDE_CONTROLS, Math.max(mMinShowNotificationMs - time Delta, 0));
681 }
682 }
683
684 private void animateIfNecessary(final boolean show, long startDelay) {
685 if (mControlAnimation != null) {
686 if (!mControlAnimation.isRunning() || mCurrentAnimationIsShowing != show) {
687 mControlAnimation.cancel();
688 mControlAnimation = null;
689 } else {
690 return;
691 }
692 }
693
694 float destination = show ? 0 : -mControlContainerHeight;
695 long duration = (long) (mMaxAnimationDurationMs
696 * Math.abs((destination - getControlOffset()) / mControlContaine rHeight));
697 mControlAnimation = ObjectAnimator.ofFloat(this, new ControlsOffsetPrope rty(), destination);
698 mControlAnimation.addListener(new AnimatorListenerAdapter() {
699 private boolean mCanceled = false;
700
701 @Override
702 public void onAnimationCancel(Animator anim) {
703 mCanceled = true;
704 }
705
706 @Override
707 public void onAnimationEnd(Animator animation) {
708 if (!show && !mCanceled) mBrowserControlOffset = Float.NaN;
709 mControlAnimation = null;
710 }
711 });
712 mControlAnimation.setStartDelay(startDelay);
713 mControlAnimation.setDuration(duration);
714 mControlAnimation.start();
715 mCurrentAnimationIsShowing = show;
716 }
717
718 @Override
719 public void onContentViewScrollingStateChanged(boolean scrolling) {
720 mContentViewScrolling = scrolling;
721 if (!scrolling) updateVisuals();
722 }
723 }
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698