package ca.uwaterloo.mainscreencontrols;
import android.content.Context;
import android.view.animation.AnimationUtils;
/**
* This class encapsulates rotation. The duration of the rotation
* can be passed in the constructor and specifies the maximum time that
* the rotation animation should take. Past this time, the rotation is
* automatically moved to its final stage and computeRotationOffset()
* will always return false to indicate that scrolling is over.
*/
public class Rotator {
private int mMode;
private float mStartAngle;
private float mCurrAngle;
private long mStartTime;
private long mDuration;
private float mDeltaAngle;
private boolean mFinished;
private float mCoeffVelocity = 0.05f;
private float mVelocity;
private static final int DEFAULT_DURATION = 250;
private static final int SCROLL_MODE = 0;
private static final int FLING_MODE = 1;
private final float mDeceleration = 240.0f;
/**
* Create a Scroller with the specified interpolator. If the interpolator is
* null, the default (viscous) interpolator will be used.
*/
public Rotator(Context context) {
mFinished = true;
}
/**
*
* Returns whether the scroller has finished scrolling.
*
* @return True if the scroller has finished scrolling, false otherwise.
*/
public final boolean isFinished() {
return mFinished;
}
/**
* Force the finished field to a particular value.
*
* @param finished The new finished value.
*/
public final void forceFinished(boolean finished) {
mFinished = finished;
}
/**
* Returns how long the scroll event will take, in milliseconds.
*
* @return The duration of the scroll in milliseconds.
*/
public final long getDuration() {
return mDuration;
}
/**
* Returns the current X offset in the scroll.
*
* @return The new X offset as an absolute distance from the origin.
*/
public final float getCurrAngle() {
return mCurrAngle;
}
/**
* @hide
* Returns the current velocity.
*
* @return The original velocity less the deceleration. Result may be
* negative.
*/
public float getCurrVelocity() {
return (mCoeffVelocity * mVelocity) - (mDeceleration * timePassed() /* / 2000.0f*/);
}
/**
* Returns the start X offset in the scroll.
*
* @return The start X offset as an absolute distance from the origin.
*/
public final float getStartAngle() {
return mStartAngle;
}
/**
* Returns the time elapsed since the beginning of the scrolling.
*
* @return The elapsed time in milliseconds.
*/
public int timePassed() {
return (int)(AnimationUtils.currentAnimationTimeMillis() - mStartTime);
}
/**
* Extend the scroll animation. This allows a running animation to scroll
* further and longer, when used with {@link #setFinalX(int)} or {@link #setFinalY(int)}.
*
* @param extend Additional time to scroll in milliseconds.
* @see #setFinalX(int)
* @see #setFinalY(int)
*/
public void extendDuration(int extend) {
int passed = timePassed();
mDuration = passed + extend;
mFinished = false;
}
/**
* Stops the animation. Contrary to {@link #forceFinished(boolean)},
* aborting the animating cause the scroller to move to the final x and y
* position
*
* @see #forceFinished(boolean)
*/
public void abortAnimation() {
mFinished = true;
}
/**
* Call this when you want to know the new location. If it returns true,
* the animation is not yet finished. loc will be altered to provide the
* new location.
*/
public boolean computeAngleOffset()
{
if (mFinished) {
return false;
}
long systemClock = AnimationUtils.currentAnimationTimeMillis();
long timePassed = systemClock - mStartTime;
if (timePassed < mDuration) {
switch (mMode) {
case SCROLL_MODE:
float sc = (float)timePassed / mDuration;
mCurrAngle = mStartAngle + Math.round(mDeltaAngle * sc);
break;
case FLING_MODE:
float timePassedSeconds = timePassed / 1000.0f;
float distance;
if(mVelocity < 0)
{
distance = (mCoeffVelocity * mVelocity * timePassedSeconds) -
((mDeceleration * timePassedSeconds * timePassedSeconds) / 2.0f);
}
else{
distance = (-mCoeffVelocity * mVelocity * timePassedSeconds) -
((mDeceleration * timePassedSeconds * timePassedSeconds) / 2.0f);
}
mCurrAngle = mStartAngle - (Math.signum(mVelocity)*Math.round(distance));
break;
}
return true;
}
else
{
mFinished = true;
return false;
}
}
/**
* Start scrolling by providing a starting point and the distance to travel.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
* @param duration Duration of the scroll in milliseconds.
*/
public void startRotate(float startAngle, float dAngle, int duration) {
mMode = SCROLL_MODE;
mFinished = false;
mDuration = duration;
mStartTime = AnimationUtils.currentAnimationTimeMillis();
mStartAngle = startAngle;
mDeltaAngle = dAngle;
}
/**
* Start scrolling by providing a starting point and the distance to travel.
* The scroll will use the default value of 250 milliseconds for the
* duration.
*
* @param startX Starting horizontal scroll offset in pixels. Positive
* numbers will scroll the content to the left.
* @param startY Starting vertical scroll offset in pixels. Positive numbers
* will scroll the content up.
* @param dx Horizontal distance to travel. Positive numbers will scroll the
* content to the left.
* @param dy Vertical distance to travel. Positive numbers will scroll the
* content up.
*/
public void startRotate(float startAngle, float dAngle) {
startRotate(startAngle, dAngle, DEFAULT_DURATION);
}
/**
* Start scrolling based on a fling gesture. The distance travelled will
* depend on the initial velocity of the fling.
*
* @param velocityAngle Initial velocity of the fling (X) measured in pixels per second.
*/
public void fling(float velocityAngle) {
mMode = FLING_MODE;
mFinished = false;
float velocity = velocityAngle;
mVelocity = velocity;
mDuration = (int)(1000.0f * Math.sqrt((2.0f * mCoeffVelocity *
Math.abs(velocity))/mDeceleration));
mStartTime = AnimationUtils.currentAnimationTimeMillis();
}
}