6package org.mozilla.gecko.gfx;
8import android.util.Log;
9import android.view.View;
24 private static final String PREF_SCROLLING_FRICTION_SLOW =
"ui.scrolling.friction_slow";
25 private static final String PREF_SCROLLING_FRICTION_FAST =
"ui.scrolling.friction_fast";
26 private static final String PREF_SCROLLING_MAX_EVENT_ACCELERATION =
"ui.scrolling.max_event_acceleration";
27 private static final String PREF_SCROLLING_OVERSCROLL_DECEL_RATE =
"ui.scrolling.overscroll_decel_rate";
28 private static final String PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT =
"ui.scrolling.overscroll_snap_limit";
29 private static final String PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE =
"ui.scrolling.min_scrollable_distance";
32 private static float FRICTION_SLOW;
34 private static float FRICTION_FAST;
37 private static float VELOCITY_THRESHOLD;
40 private static float MAX_EVENT_ACCELERATION;
43 private static float OVERSCROLL_DECEL_RATE;
45 private static float SNAP_LIMIT;
49 private static float MIN_SCROLLABLE_DISTANCE;
51 private static float getFloatPref(Map<String, Integer> prefs,
String prefName,
int defaultValue) {
52 Integer value = (prefs ==
null ? null : prefs.get(prefName));
53 return (
float)(
value ==
null ||
value < 0 ? defaultValue :
value) / 1000f;
56 private static int getIntPref(Map<String, Integer> prefs,
String prefName,
int defaultValue) {
57 Integer value = (prefs ==
null ? null : prefs.get(prefName));
61 static final float MS_PER_FRAME = 4.0f;
62 private static final float FRAMERATE_MULTIPLIER = (1000f/60f) / MS_PER_FRAME;
67 static float getFrameAdjustedFriction(
float baseFriction) {
68 return (
float)
Math.pow(
Math.E, (
Math.log(baseFriction) / FRAMERATE_MULTIPLIER));
71 static void setPrefs(Map<String, Integer> prefs) {
72 FRICTION_SLOW = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_SLOW, 850));
73 FRICTION_FAST = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_FRICTION_FAST, 970));
74 VELOCITY_THRESHOLD = 10 / FRAMERATE_MULTIPLIER;
75 MAX_EVENT_ACCELERATION = getFloatPref(prefs, PREF_SCROLLING_MAX_EVENT_ACCELERATION, 12);
76 OVERSCROLL_DECEL_RATE = getFrameAdjustedFriction(getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_DECEL_RATE, 40));
77 SNAP_LIMIT = getFloatPref(prefs, PREF_SCROLLING_OVERSCROLL_SNAP_LIMIT, 300);
78 MIN_SCROLLABLE_DISTANCE = getFloatPref(prefs, PREF_SCROLLING_MIN_SCROLLABLE_DISTANCE, 500);
79 Log.i(
LOGTAG,
"Prefs: " + FRICTION_SLOW +
"," + FRICTION_FAST +
"," + VELOCITY_THRESHOLD +
","
80 + MAX_EVENT_ACCELERATION +
"," + OVERSCROLL_DECEL_RATE +
"," + SNAP_LIMIT +
"," + MIN_SCROLLABLE_DISTANCE);
101 private final SubdocumentScrollHelper mSubscroller;
103 private int mOverscrollMode;
104 private float mFirstTouchPos;
105 private float mTouchPos;
106 private float mLastTouchPos;
107 private float mVelocity;
108 private boolean mScrollingDisabled;
109 private boolean mDisableSnap;
110 private float mDisplacement;
114 protected abstract float getOrigin();
115 protected abstract float getViewportLength();
116 protected abstract float getPageStart();
117 protected abstract float getPageLength();
119 Axis(SubdocumentScrollHelper subscroller) {
120 mSubscroller = subscroller;
121 mOverscrollMode =
View.OVER_SCROLL_IF_CONTENT_SCROLLS;
124 public void setOverScrollMode(
int overscrollMode) {
125 mOverscrollMode = overscrollMode;
128 public int getOverScrollMode() {
129 return mOverscrollMode;
132 private float getViewportEnd() {
133 return getOrigin() + getViewportLength();
136 private float getPageEnd() {
137 return getPageStart() + getPageLength();
140 void startTouch(
float pos) {
142 mScrollingDisabled =
false;
143 mFirstTouchPos = mTouchPos = mLastTouchPos =
pos;
146 float panDistance(
float currentPos) {
147 return currentPos - mFirstTouchPos;
150 void setScrollingDisabled(
boolean disabled) {
151 mScrollingDisabled = disabled;
154 void saveTouchPos() {
155 mLastTouchPos = mTouchPos;
158 void updateWithTouchAt(
float pos,
float timeDelta) {
159 float newVelocity = (mTouchPos -
pos) / timeDelta * MS_PER_FRAME;
164 boolean curVelocityIsLow =
Math.abs(mVelocity) < 1.0f / FRAMERATE_MULTIPLIER;
165 boolean directionChange = (mVelocity > 0) != (newVelocity > 0);
166 if (curVelocityIsLow || (directionChange && !FloatUtils.fuzzyEquals(newVelocity, 0.0f))) {
167 mVelocity = newVelocity;
169 float maxChange =
Math.abs(mVelocity * timeDelta * MAX_EVENT_ACCELERATION);
170 mVelocity =
Math.min(mVelocity + maxChange,
Math.max(mVelocity - maxChange, newVelocity));
176 boolean overscrolled() {
177 return getOverscroll() != Overscroll.NONE;
180 private Overscroll getOverscroll() {
181 boolean minus = (getOrigin() < getPageStart());
182 boolean plus = (getViewportEnd() > getPageEnd());
184 return Overscroll.BOTH;
186 return Overscroll.MINUS;
188 return Overscroll.PLUS;
190 return Overscroll.NONE;
196 private float getExcess() {
197 switch (getOverscroll()) {
198 case MINUS:
return getPageStart() - getOrigin();
199 case PLUS:
return getViewportEnd() - getPageEnd();
200 case BOTH:
return (getViewportEnd() - getPageEnd()) + (getPageStart() - getOrigin());
201 default:
return 0.0f;
209 boolean scrollable() {
212 if (mSubscroller.scrolling()) {
213 return !mScrollingDisabled;
217 if (mScrollingDisabled) {
223 return getViewportLength() <= getPageLength() - MIN_SCROLLABLE_DISTANCE ||
224 getOverScrollMode() ==
View.OVER_SCROLL_ALWAYS;
231 float getEdgeResistance(
boolean forPinching) {
232 float excess = getExcess();
233 if (excess > 0.0f && (getOverscroll() == Overscroll.BOTH || !forPinching)) {
236 return Math.max(0.0f, SNAP_LIMIT - excess / getViewportLength());
242 float getRealVelocity() {
243 return scrollable() ? mVelocity : 0f;
247 mFlingState = FlingStates.
PANNING;
250 void startFling(
boolean stopped) {
251 mDisableSnap = mSubscroller.scrolling();
254 mFlingState = FlingStates.
STOPPED;
261 boolean advanceFling() {
262 if (mFlingState != FlingStates.FLINGING) {
265 if (mSubscroller.scrolling() && !mSubscroller.lastScrollSucceeded()) {
272 float excess = getExcess();
273 Overscroll overscroll = getOverscroll();
274 boolean decreasingOverscroll =
false;
275 if ((overscroll == Overscroll.MINUS && mVelocity > 0) ||
276 (overscroll == Overscroll.PLUS && mVelocity < 0))
278 decreasingOverscroll =
true;
281 if (mDisableSnap || FloatUtils.fuzzyEquals(excess, 0.0f) || decreasingOverscroll) {
283 if (
Math.abs(mVelocity) >= VELOCITY_THRESHOLD) {
284 mVelocity *= FRICTION_FAST;
286 float t = mVelocity / VELOCITY_THRESHOLD;
287 mVelocity *= FloatUtils.interpolate(FRICTION_SLOW, FRICTION_FAST, t);
291 float elasticity = 1.0f - excess / (getViewportLength() * SNAP_LIMIT);
292 if (overscroll == Overscroll.MINUS) {
293 mVelocity =
Math.min((mVelocity + OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
295 mVelocity =
Math.max((mVelocity - OVERSCROLL_DECEL_RATE) * elasticity, 0.0f);
304 mFlingState = FlingStates.
STOPPED;
313 if (mFlingState == FlingStates.PANNING)
314 mDisplacement += (mLastTouchPos - mTouchPos) * getEdgeResistance(
false);
316 mDisplacement += mVelocity;
321 if (getOverScrollMode() ==
View.OVER_SCROLL_NEVER) {
322 if (mDisplacement + getOrigin() < getPageStart()) {
323 mDisplacement = getPageStart() - getOrigin();
325 }
else if (mDisplacement + getViewportEnd() > getPageEnd()) {
326 mDisplacement = getPageEnd() - getViewportEnd();
332 float resetDisplacement() {
333 float d = mDisplacement;
334 mDisplacement = 0.0f;