Bug 1180295 - Introduce the skeleton of a DynamicToolbarAnimator class alongside LayerMarginsAnimator. r=rbarker

This commit is contained in:
Kartikaya Gupta 2015-08-18 14:27:18 -04:00
parent 17f8044764
commit c2db0abc64
4 changed files with 243 additions and 0 deletions

View File

@ -0,0 +1,226 @@
/* -*- Mode: Java; c-basic-offset: 4; tab-width: 20; indent-tabs-mode: nil; -*-
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
package org.mozilla.gecko.gfx;
import org.mozilla.gecko.PrefsHelper;
import org.mozilla.gecko.util.FloatUtils;
import org.mozilla.gecko.util.ThreadUtils;
import android.util.Log;
import android.view.animation.DecelerateInterpolator;
import java.util.ArrayList;
import java.util.List;
public class DynamicToolbarAnimator {
private static final String LOGTAG = "GeckoDynamicToolbarAnimator";
private static final String PREF_SCROLL_TOOLBAR_THRESHOLD = "browser.ui.scroll-toolbar-threshold";
// The duration of the animation in ns
private static final long ANIMATION_DURATION = 250000000;
private final GeckoLayerClient mTarget;
private final List<LayerView.DynamicToolbarListener> mListeners;
/* The translation to be applied to the toolbar UI view. This is the
* distance from the default/initial location (at the top of the screen,
* visible to the user) to where we want it to be. This variable should
* always be between 0 (toolbar fully visible) and the height of the toolbar
* (toolbar fully hidden), inclusive.
*/
private float mToolbarTranslation;
/* The translation to be applied to the LayerView. This is the distance from
* the default/initial location (just below the toolbar, with the bottom
* extending past the bottom of the screen) to where we want it to be.
* This variable should always be between 0 and the height of the toolbar,
* inclusive.
*/
private float mLayerViewTranslation;
/* This stores the maximum translation that can be applied to the toolbar
* and layerview when scrolling. This is populated with the height of the
* toolbar. */
private float mMaxTranslation;
/* If this boolean is true, scroll changes will not affect translation */
private boolean mPinned;
/* This interpolator is used for the above mentioned animation */
private DecelerateInterpolator mInterpolator;
/* This is the proportion of the viewport rect that needs to be travelled
* while scrolling before the translation will start taking effect.
*/
private float SCROLL_TOOLBAR_THRESHOLD = 0.20f;
/* The ID of the prefs listener for the scroll-toolbar threshold */
private Integer mPrefObserverId;
/* The task that handles showing/hiding toolbar */
private DynamicToolbarAnimationTask mAnimationTask;
public DynamicToolbarAnimator(GeckoLayerClient aTarget) {
mTarget = aTarget;
mListeners = new ArrayList<LayerView.DynamicToolbarListener>();
mInterpolator = new DecelerateInterpolator();
// Listen to the dynamic toolbar pref
mPrefObserverId = PrefsHelper.getPref(PREF_SCROLL_TOOLBAR_THRESHOLD, new PrefsHelper.PrefHandlerBase() {
@Override
public void prefValue(String pref, int value) {
SCROLL_TOOLBAR_THRESHOLD = value / 100.0f;
}
@Override
public boolean isObserver() {
return true;
}
});
}
public void destroy() {
if (mPrefObserverId != null) {
PrefsHelper.removeObserver(mPrefObserverId);
mPrefObserverId = null;
}
}
public void addTranslationListener(LayerView.DynamicToolbarListener aListener) {
mListeners.add(aListener);
}
public void removeTranslationListener(LayerView.DynamicToolbarListener aListener) {
mListeners.remove(aListener);
}
private void fireListeners() {
for (LayerView.DynamicToolbarListener listener : mListeners) {
listener.onTranslationChanged(mToolbarTranslation, mLayerViewTranslation);
}
}
public void setMaxTranslation(float maxTranslation) {
ThreadUtils.assertOnUiThread();
if (maxTranslation < 0) {
Log.e(LOGTAG, "Got a negative max-translation value: " + maxTranslation + "; clamping to zero");
mMaxTranslation = 0;
} else {
mMaxTranslation = maxTranslation;
}
}
public void setPinned(boolean pinned) {
mPinned = pinned;
}
public boolean isPinned() {
return mPinned;
}
public void showToolbar(boolean immediately) {
animateToolbar(0, immediately);
}
public void hideToolbar(boolean immediately) {
animateToolbar(mMaxTranslation, immediately);
}
private void animateToolbar(final float translation, boolean immediately) {
ThreadUtils.assertOnUiThread();
if (mAnimationTask != null) {
mTarget.getView().removeRenderTask(mAnimationTask);
mAnimationTask = null;
}
Log.v(LOGTAG, "Requested " + (immediately ? "immedate " : "") + "toolbar animation to translation " + translation);
if (FloatUtils.fuzzyEquals(mToolbarTranslation, translation)) {
// If we're already pretty much in the desired position, don't bother
// with a full animation; do an immediate jump
immediately = true;
Log.v(LOGTAG, "Changing animation to immediate jump");
}
if (immediately) {
mToolbarTranslation = translation;
fireListeners();
resizeViewport();
mTarget.getView().requestRender();
return;
}
Log.v(LOGTAG, "Kicking off animation...");
mAnimationTask = new DynamicToolbarAnimationTask(false, translation);
mTarget.getView().postRenderTask(mAnimationTask);
}
private void resizeViewport() {
ThreadUtils.assertOnUiThread();
// The animation is done, now we need to tell gecko to resize to the
// proper steady-state layout.
synchronized (mTarget.getLock()) {
int viewWidth = mTarget.getView().getWidth();
int viewHeight = mTarget.getView().getHeight();
int viewHeightVisible = viewHeight - Math.round(mMaxTranslation - mToolbarTranslation);
Log.v(LOGTAG, "Resize viewport to dimensions " + viewWidth + "x" + viewHeightVisible);
mTarget.setViewportSize(viewWidth, viewHeightVisible);
}
}
class DynamicToolbarAnimationTask extends RenderTask {
private final float mStartTranslation;
private final float mEndTranslation;
private boolean mContinueAnimation;
public DynamicToolbarAnimationTask(boolean aRunAfter, float aTranslation) {
super(aRunAfter);
mContinueAnimation = true;
mStartTranslation = mToolbarTranslation;
mEndTranslation = aTranslation;
}
@Override
public boolean internalRun(long timeDelta, long currentFrameStartTime) {
if (!mContinueAnimation) {
return false;
}
// Calculate the progress (between 0 and 1)
final float progress = mInterpolator.getInterpolation(
Math.min(1.0f, (System.nanoTime() - getStartTime())
/ (float)ANIMATION_DURATION));
// This runs on the compositor thread, so we need to post the
// actual work to the UI thread.
ThreadUtils.assertNotOnUiThread();
ThreadUtils.postToUiThread(new Runnable() {
@Override
public void run() {
// Move the toolbar as per the animation
mToolbarTranslation = FloatUtils.interpolate(mStartTranslation, mEndTranslation, progress);
// Move the LayerView so that there is never a gap between the toolbar
// and the LayerView, because there we will draw garbage.
mLayerViewTranslation = Math.max(mLayerViewTranslation, mToolbarTranslation);
fireListeners();
if (progress >= 1.0f) {
resizeViewport();
}
}
});
mTarget.getView().requestRender();
if (progress >= 1.0f) {
mContinueAnimation = false;
}
return mContinueAnimation;
}
}
}

View File

@ -96,6 +96,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
private final PanZoomController mPanZoomController;
private final LayerMarginsAnimator mMarginsAnimator;
private final DynamicToolbarAnimator mToolbarAnimator;
private final LayerView mView;
/* This flag is true from the time that browser.js detects a first-paint is about to start,
@ -138,6 +139,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
mDrawListeners = new ArrayList<DrawListener>();
mPanZoomController = PanZoomController.Factory.create(this, view, eventDispatcher);
mMarginsAnimator = new LayerMarginsAnimator(this, view);
mToolbarAnimator = new DynamicToolbarAnimator(this);
mView = view;
mView.setListener(this);
mContentDocumentIsDisplayed = true;
@ -174,6 +176,7 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
public void destroy() {
mPanZoomController.destroy();
mMarginsAnimator.destroy();
mToolbarAnimator.destroy();
mDrawListeners.clear();
}
@ -238,6 +241,10 @@ class GeckoLayerClient implements LayerView.Listener, PanZoomTarget
return mMarginsAnimator;
}
DynamicToolbarAnimator getDynamicToolbarAnimator() {
return mToolbarAnimator;
}
/* Informs Gecko that the screen size has changed. */
private void sendResizeEventIfNecessary(boolean force) {
DisplayMetrics metrics = mContext.getResources().getDisplayMetrics();

View File

@ -55,6 +55,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
private GeckoLayerClient mLayerClient;
private PanZoomController mPanZoomController;
private LayerMarginsAnimator mMarginsAnimator;
private DynamicToolbarAnimator mToolbarAnimator;
private final GLController mGLController;
private InputConnectionHandler mInputConnectionHandler;
private LayerRenderer mRenderer;
@ -130,6 +131,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
mPanZoomController = mLayerClient.getPanZoomController();
mMarginsAnimator = mLayerClient.getLayerMarginsAnimator();
mToolbarAnimator = mLayerClient.getDynamicToolbarAnimator();
mRenderer = new LayerRenderer(this);
mInputConnectionHandler = null;
@ -301,6 +303,7 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
public PanZoomController getPanZoomController() { return mPanZoomController; }
public LayerMarginsAnimator getLayerMarginsAnimator() { return mMarginsAnimator; }
public DynamicToolbarAnimator getDynamicToolbarAnimator() { return mToolbarAnimator; }
public ImmutableViewportMetrics getViewportMetrics() {
return mLayerClient.getViewportMetrics();
@ -665,6 +668,12 @@ public class LayerView extends FrameLayout implements Tabs.OnTabsChangedListener
}
}
// Public hooks for dynamic toolbar translation
public interface DynamicToolbarListener {
public void onTranslationChanged(float aToolbarTranslation, float aLayerViewTranslation);
}
// Public hooks for listening to metrics changing
public interface OnMetricsChangedListener {

View File

@ -268,6 +268,7 @@ gbjar.sources += [
'gfx/DisplayPortCalculator.java',
'gfx/DisplayPortMetrics.java',
'gfx/DrawTimingQueue.java',
'gfx/DynamicToolbarAnimator.java',
'gfx/FloatSize.java',
'gfx/FullScreenState.java',
'gfx/GeckoLayerClient.java',