author | Wes Johnston <wjohnston@mozilla.com> |
Thu, 26 Sep 2013 22:57:57 -0700 | |
changeset 149089 | 9433c19bf735b27e4dbcdea5aed5d08982915033 |
parent 148919 | e4cd2242cc7d85e240c585f68730753348fd1a9e |
child 149090 | 72a469699ec39b75f8557e7862745a83fe8ab6b0 |
push id | 34439 |
push user | kwierso@gmail.com |
push date | Sat, 28 Sep 2013 03:39:12 +0000 |
treeherder | mozilla-inbound@caec8c0c4963 [default view] [failures only] |
perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
reviewers | kats |
bugs | 918079 |
milestone | 27.0a1 |
first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
--- a/mobile/android/base/Makefile.in +++ b/mobile/android/base/Makefile.in @@ -189,16 +189,17 @@ FENNEC_JAVA_FILES = \ gfx/IntSize.java \ gfx/JavaPanZoomController.java \ gfx/Layer.java \ gfx/LayerMarginsAnimator.java \ gfx/LayerRenderer.java \ gfx/LayerView.java \ gfx/NativePanZoomController.java \ gfx/NinePatchTileLayer.java \ + gfx/Overscroll.java \ gfx/PanningPerfAPI.java \ gfx/PanZoomController.java \ gfx/PanZoomTarget.java \ gfx/PluginLayer.java \ gfx/PointUtils.java \ gfx/ProgressiveUpdateData.java \ gfx/RectUtils.java \ gfx/RenderTask.java \
--- a/mobile/android/base/gfx/Axis.java +++ b/mobile/android/base/gfx/Axis.java @@ -149,16 +149,20 @@ abstract class Axis { protected abstract boolean marginsHidden(); Axis(SubdocumentScrollHelper subscroller) { mSubscroller = subscroller; mOverscrollMode = View.OVER_SCROLL_IF_CONTENT_SCROLLS; mRecentVelocities = new float[FLING_VELOCITY_POINTS]; } + // Implementors can override these to show effects when the axis overscrolls + protected void overscrollFling(float velocity) { } + protected void overscrollPan(float displacement) { } + public void setOverScrollMode(int overscrollMode) { mOverscrollMode = overscrollMode; } public int getOverScrollMode() { return mOverscrollMode; } @@ -374,22 +378,32 @@ abstract class Axis { else mDisplacement += mVelocity * getEdgeResistance(false); // if overscroll is disabled and we're trying to overscroll, reset the displacement // to remove any excess. Using getExcess alone isn't enough here since it relies on // getOverscroll which doesn't take into account any new displacment being applied. // If we using a subscroller, we don't want to alter the scrolling being done if (getOverScrollMode() == View.OVER_SCROLL_NEVER && !mSubscroller.scrolling()) { + float originalDisplacement = mDisplacement; + if (mDisplacement + getOrigin() < getPageStart() - getMarginStart()) { mDisplacement = getPageStart() - getMarginStart() - getOrigin(); - stopFling(); } else if (mDisplacement + getViewportEnd() > getPageEnd() + getMarginEnd()) { mDisplacement = getPageEnd() - getMarginEnd() - getViewportEnd(); - stopFling(); + } + + // Return the amount of overscroll so that the overscroll controller can draw it for us + if (originalDisplacement != mDisplacement) { + if (mFlingState == FlingStates.FLINGING) { + overscrollFling(mVelocity / MS_PER_FRAME * 1000); + stopFling(); + } else if (mFlingState == FlingStates.PANNING) { + overscrollPan(originalDisplacement - mDisplacement); + } } } } float resetDisplacement() { float d = mDisplacement; mDisplacement = 0.0f; return d;
--- a/mobile/android/base/gfx/GeckoLayerClient.java +++ b/mobile/android/base/gfx/GeckoLayerClient.java @@ -127,16 +127,20 @@ public class GeckoLayerClient implements mPanZoomController = PanZoomController.Factory.create(this, view, eventDispatcher); mMarginsAnimator = new LayerMarginsAnimator(this, view); mView = view; mView.setListener(this); mContentDocumentIsDisplayed = true; } + public void setOverscrollHandler(final Overscroll listener) { + mPanZoomController.setOverscrollHandler(listener); + } + /** Attaches to root layer so that Gecko appears. */ public void notifyGeckoReady() { mGeckoIsReady = true; mRootLayer = new VirtualLayer(new IntSize(mView.getWidth(), mView.getHeight())); mLayerRenderer = mView.getRenderer(); sendResizeEventIfNecessary(true);
--- a/mobile/android/base/gfx/JavaPanZoomController.java +++ b/mobile/android/base/gfx/JavaPanZoomController.java @@ -124,16 +124,19 @@ class JavaPanZoomController private float mAutonavZoomDelta; /* The user selected panning mode */ private AxisLockMode mMode; /* A medium-length tap/press is happening */ private boolean mMediumPress; /* Used to change the scrollY direction */ private boolean mNegateWheelScrollY; + // Handler to be notified when overscroll occurs + private Overscroll mOverscroll; + public JavaPanZoomController(PanZoomTarget target, View view, EventDispatcher eventDispatcher) { mTarget = target; mSubscroller = new SubdocumentScrollHelper(eventDispatcher); mX = new AxisX(mSubscroller); mY = new AxisY(mSubscroller); mTouchEventHandler = new TouchEventHandler(view.getContext(), view, this); checkMainThread(); @@ -1108,16 +1111,28 @@ class JavaPanZoomController @Override protected float getPageLength() { return getMetrics().getPageWidthWithMargins(); } @Override protected boolean marginsHidden() { ImmutableViewportMetrics metrics = getMetrics(); RectF maxMargins = mTarget.getMaxMargins(); return (metrics.marginLeft < maxMargins.left || metrics.marginRight < maxMargins.right); } + @Override + protected void overscrollFling(final float velocity) { + if (mOverscroll != null) { + mOverscroll.setVelocity(velocity, Overscroll.Axis.X); + } + } + @Override + protected void overscrollPan(final float distance) { + if (mOverscroll != null) { + mOverscroll.setDistance(distance, Overscroll.Axis.X); + } + } } private class AxisY extends Axis { AxisY(SubdocumentScrollHelper subscroller) { super(subscroller); } @Override public float getOrigin() { return getMetrics().viewportRectTop; } @Override protected float getViewportLength() { return getMetrics().getHeight(); } @@ -1130,16 +1145,28 @@ class JavaPanZoomController @Override protected float getMarginEnd() { return mTarget.getMaxMargins().bottom - getMetrics().marginBottom; } @Override protected boolean marginsHidden() { ImmutableViewportMetrics metrics = getMetrics(); RectF maxMargins = mTarget.getMaxMargins(); return (metrics.marginTop < maxMargins.top || metrics.marginBottom < maxMargins.bottom); } + @Override + protected void overscrollFling(final float velocity) { + if (mOverscroll != null) { + mOverscroll.setVelocity(velocity, Overscroll.Axis.Y); + } + } + @Override + protected void overscrollPan(final float distance) { + if (mOverscroll != null) { + mOverscroll.setDistance(distance, Overscroll.Axis.Y); + } + } } /* * Zooming */ @Override public boolean onScaleBegin(SimpleScaleGestureDetector detector) { if (mState == PanZoomState.ANIMATED_ZOOM) @@ -1429,9 +1456,14 @@ class JavaPanZoomController public int getOverScrollMode() { return mX.getOverScrollMode(); } @Override public void updateScrollOffset(float cssX, float cssY) { // Nothing to update, this class doesn't store the scroll offset locally. } + + @Override + public void setOverscrollHandler(final Overscroll handler) { + mOverscroll = handler; + } }
--- a/mobile/android/base/gfx/LayerView.java +++ b/mobile/android/base/gfx/LayerView.java @@ -13,16 +13,17 @@ import org.mozilla.gecko.R; import org.mozilla.gecko.TouchEventInterceptor; import org.mozilla.gecko.ZoomConstraints; import org.mozilla.gecko.mozglue.GeneratableAndroidBridgeTarget; import org.mozilla.gecko.util.EventDispatcher; import android.content.Context; import android.graphics.Bitmap; import android.graphics.BitmapFactory; +import android.graphics.Canvas; import android.graphics.Color; import android.graphics.PixelFormat; import android.graphics.PointF; import android.graphics.Rect; import android.graphics.SurfaceTexture; import android.os.Build; import android.os.Handler; import android.util.AttributeSet; @@ -62,16 +63,17 @@ public class LayerView extends FrameLayo private SurfaceView mSurfaceView; private TextureView mTextureView; private Listener mListener; /* This should only be modified on the Java UI thread. */ private final ArrayList<TouchEventInterceptor> mTouchInterceptors; + private final Overscroll mOverscroll; /* Flags used to determine when to show the painted surface. */ public static final int PAINT_START = 0; public static final int PAINT_BEFORE_FIRST = 1; public static final int PAINT_AFTER_FIRST = 2; public boolean shouldUseTextureView() { // Disable TextureView support for now as it causes panning/zooming @@ -99,20 +101,23 @@ public class LayerView extends FrameLayo public LayerView(Context context, AttributeSet attrs) { super(context, attrs); mGLController = GLController.getInstance(this); mPaintState = PAINT_START; mBackgroundColor = Color.WHITE; mTouchInterceptors = new ArrayList<TouchEventInterceptor>(); + mOverscroll = new Overscroll(this); } public void initializeView(EventDispatcher eventDispatcher) { mLayerClient = new GeckoLayerClient(getContext(), this, eventDispatcher); + mLayerClient.setOverscrollHandler(mOverscroll); + mPanZoomController = mLayerClient.getPanZoomController(); mMarginsAnimator = mLayerClient.getLayerMarginsAnimator(); mRenderer = new LayerRenderer(this); mInputConnectionHandler = null; setFocusable(true); setFocusableInTouchMode(true); @@ -214,16 +219,26 @@ public class LayerView extends FrameLayo result |= i.onInterceptTouchEvent(this, event); } } return result; } @Override + public void dispatchDraw(final Canvas canvas) { + super.dispatchDraw(canvas); + + // We must have a layer client to get valid viewport metrics + if (mLayerClient != null) { + mOverscroll.draw(canvas, getViewportMetrics()); + } + } + + @Override public boolean onTouchEvent(MotionEvent event) { if (event.getActionMasked() == MotionEvent.ACTION_DOWN) { requestFocus(); } if (runTouchInterceptors(event, false)) { return true; } @@ -482,24 +497,28 @@ public class LayerView extends FrameLayo if (!mGLController.hasValidSurface() || mSurfaceView == null) { surfaceChanged(width, height); return; } if (mListener != null) { mListener.sizeChanged(width, height); } + + mOverscroll.setSize(width, height); } private void surfaceChanged(int width, int height) { mGLController.surfaceChanged(width, height); if (mListener != null) { mListener.surfaceChanged(width, height); } + + mOverscroll.setSize(width, height); } private void onDestroyed() { mGLController.surfaceDestroyed(); } public Object getNativeWindow() { if (mSurfaceView != null)
--- a/mobile/android/base/gfx/NativePanZoomController.java +++ b/mobile/android/base/gfx/NativePanZoomController.java @@ -99,9 +99,12 @@ class NativePanZoomController implements long nextDelay = runDelayedCallback(); if (nextDelay >= 0) { mTarget.postDelayed(this, nextDelay); } } } public native void updateScrollOffset(float cssX, float cssY); + + public void setOverscrollHandler(final Overscroll listener) { + } }
new file mode 100644 --- /dev/null +++ b/mobile/android/base/gfx/Overscroll.java @@ -0,0 +1,127 @@ +/* -*- 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 android.content.Context; +import android.graphics.Canvas; +import android.support.v4.widget.EdgeEffectCompat; +import android.support.v4.view.ViewCompat; +import android.view.View; + +public class Overscroll { + // Used to index particular edges in the edges array + private static final int TOP = 0; + private static final int BOTTOM = 1; + private static final int LEFT = 2; + private static final int RIGHT = 3; + + // All four edges of the screen + private final EdgeEffectCompat[] mEdges = new EdgeEffectCompat[4]; + + // The view we're showing this overscroll on. + private final View mView; + + // The axis to show overscroll on. + public enum Axis { + X, + Y, + }; + + public Overscroll(final View v) { + mView = v; + Context context = v.getContext(); + for (int i = 0; i < 4; i++) { + mEdges[i] = new EdgeEffectCompat(context); + } + } + + public void setSize(final int width, final int height) { + mEdges[LEFT].setSize(height, width); + mEdges[RIGHT].setSize(height, width); + mEdges[TOP].setSize(width, height); + mEdges[BOTTOM].setSize(width, height); + } + + private EdgeEffectCompat getEdgeForAxisAndSide(final Axis axis, final float side) { + if (axis == Axis.Y) { + if (side < 0) { + return mEdges[TOP]; + } else { + return mEdges[BOTTOM]; + } + } else { + if (side < 0) { + return mEdges[LEFT]; + } else { + return mEdges[RIGHT]; + } + } + } + + public void setVelocity(final float velocity, final Axis axis) { + final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, velocity); + + // If we're showing overscroll already, start fading it out. + if (!edge.isFinished()) { + edge.onRelease(); + } else { + // Otherwise, show an absorb effect + edge.onAbsorb((int)velocity); + } + + ViewCompat.postInvalidateOnAnimation(mView); + } + + public void setDistance(final float distance, final Axis axis) { + // The first overscroll event often has zero distance. Throw it out + if (distance == 0.0f) { + return; + } + + final EdgeEffectCompat edge = getEdgeForAxisAndSide(axis, (int)distance); + edge.onPull(distance / (axis == Axis.X ? mView.getWidth() : mView.getHeight())); + ViewCompat.postInvalidateOnAnimation(mView); + } + + public void draw(final Canvas canvas, final ImmutableViewportMetrics metrics) { + if (metrics == null) { + return; + } + + // If we're pulling an edge, or fading it out, draw! + boolean invalidate = false; + if (!mEdges[TOP].isFinished()) { + invalidate |= draw(mEdges[TOP], canvas, metrics.marginLeft, metrics.marginTop, 0); + } + + if (!mEdges[BOTTOM].isFinished()) { + invalidate |= draw(mEdges[BOTTOM], canvas, mView.getWidth(), mView.getHeight(), 180); + } + + if (!mEdges[LEFT].isFinished()) { + invalidate |= draw(mEdges[LEFT], canvas, metrics.marginLeft, mView.getHeight(), 270); + } + + if (!mEdges[RIGHT].isFinished()) { + invalidate |= draw(mEdges[RIGHT], canvas, mView.getWidth(), metrics.marginTop, 90); + } + + // If the edge effect is animating off screen, invalidate. + if (invalidate) { + ViewCompat.postInvalidateOnAnimation(mView); + } + } + + public boolean draw(final EdgeEffectCompat edge, final Canvas canvas, final float translateX, final float translateY, final float rotation) { + final int state = canvas.save(); + canvas.translate(translateX, translateY); + canvas.rotate(rotation); + boolean invalidate = edge.draw(canvas); + canvas.restoreToCount(state); + + return invalidate; + } +}
--- a/mobile/android/base/gfx/PanZoomController.java +++ b/mobile/android/base/gfx/PanZoomController.java @@ -37,9 +37,11 @@ public interface PanZoomController { public void pageRectUpdated(); public void abortPanning(); public void abortAnimation(); public void setOverScrollMode(int overscrollMode); public int getOverScrollMode(); public void updateScrollOffset(float cssX, float cssY); + + public void setOverscrollHandler(final Overscroll controller); }