diff options
Diffstat (limited to 'src/com/android/gallery3d/ui/Paper.java')
-rw-r--r-- | src/com/android/gallery3d/ui/Paper.java | 183 |
1 files changed, 183 insertions, 0 deletions
diff --git a/src/com/android/gallery3d/ui/Paper.java b/src/com/android/gallery3d/ui/Paper.java new file mode 100644 index 000000000..b36f5c3a2 --- /dev/null +++ b/src/com/android/gallery3d/ui/Paper.java @@ -0,0 +1,183 @@ +/* + * Copyright (C) 2010 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.gallery3d.ui; + +import android.graphics.Rect; +import android.opengl.Matrix; +import android.view.animation.DecelerateInterpolator; +import android.view.animation.Interpolator; + +import com.android.gallery3d.common.Utils; + +// This class does the overscroll effect. +class Paper { + @SuppressWarnings("unused") + private static final String TAG = "Paper"; + private static final int ROTATE_FACTOR = 4; + private EdgeAnimation mAnimationLeft = new EdgeAnimation(); + private EdgeAnimation mAnimationRight = new EdgeAnimation(); + private int mWidth; + private float[] mMatrix = new float[16]; + + public void overScroll(float distance) { + distance /= mWidth; // make it relative to width + if (distance < 0) { + mAnimationLeft.onPull(-distance); + } else { + mAnimationRight.onPull(distance); + } + } + + public void edgeReached(float velocity) { + velocity /= mWidth; // make it relative to width + if (velocity < 0) { + mAnimationRight.onAbsorb(-velocity); + } else { + mAnimationLeft.onAbsorb(velocity); + } + } + + public void onRelease() { + mAnimationLeft.onRelease(); + mAnimationRight.onRelease(); + } + + public boolean advanceAnimation() { + // Note that we use "|" because we want both animations get updated. + return mAnimationLeft.update() | mAnimationRight.update(); + } + + public void setSize(int width, int height) { + mWidth = width; + } + + public float[] getTransform(Rect rect, float scrollX) { + float left = mAnimationLeft.getValue(); + float right = mAnimationRight.getValue(); + float screenX = rect.centerX() - scrollX; + // We linearly interpolate the value [left, right] for the screenX + // range int [-1/4, 5/4]*mWidth. So if part of the thumbnail is outside + // the screen, we still get some transform. + float x = screenX + mWidth / 4; + int range = 3 * mWidth / 2; + float t = ((range - x) * left - x * right) / range; + // compress t to the range (-1, 1) by the function + // f(t) = (1 / (1 + e^-t) - 0.5) * 2 + // then multiply by 90 to make the range (-45, 45) + float degrees = + (1 / (1 + (float) Math.exp(-t * ROTATE_FACTOR)) - 0.5f) * 2 * -45; + Matrix.setIdentityM(mMatrix, 0); + Matrix.translateM(mMatrix, 0, mMatrix, 0, rect.centerX(), rect.centerY(), 0); + Matrix.rotateM(mMatrix, 0, degrees, 0, 1, 0); + Matrix.translateM(mMatrix, 0, mMatrix, 0, -rect.width() / 2, -rect.height() / 2, 0); + return mMatrix; + } +} + +// This class follows the structure of frameworks's EdgeEffect class. +class EdgeAnimation { + @SuppressWarnings("unused") + private static final String TAG = "EdgeAnimation"; + + private static final int STATE_IDLE = 0; + private static final int STATE_PULL = 1; + private static final int STATE_ABSORB = 2; + private static final int STATE_RELEASE = 3; + + // Time it will take the effect to fully done in ms + private static final int ABSORB_TIME = 200; + private static final int RELEASE_TIME = 500; + + private static final float VELOCITY_FACTOR = 0.1f; + + private final Interpolator mInterpolator; + + private int mState; + private float mValue; + + private float mValueStart; + private float mValueFinish; + private long mStartTime; + private long mDuration; + + public EdgeAnimation() { + mInterpolator = new DecelerateInterpolator(); + mState = STATE_IDLE; + } + + private void startAnimation(float start, float finish, long duration, + int newState) { + mValueStart = start; + mValueFinish = finish; + mDuration = duration; + mStartTime = now(); + mState = newState; + } + + // The deltaDistance's magnitude is in the range of -1 (no change) to 1. + // The value 1 is the full length of the view. Negative values means the + // movement is in the opposite direction. + public void onPull(float deltaDistance) { + if (mState == STATE_ABSORB) return; + mValue = Utils.clamp(mValue + deltaDistance, -1.0f, 1.0f); + mState = STATE_PULL; + } + + public void onRelease() { + if (mState == STATE_IDLE || mState == STATE_ABSORB) return; + startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); + } + + public void onAbsorb(float velocity) { + float finish = Utils.clamp(mValue + velocity * VELOCITY_FACTOR, + -1.0f, 1.0f); + startAnimation(mValue, finish, ABSORB_TIME, STATE_ABSORB); + } + + public boolean update() { + if (mState == STATE_IDLE) return false; + if (mState == STATE_PULL) return true; + + float t = Utils.clamp((float)(now() - mStartTime) / mDuration, 0.0f, 1.0f); + /* Use linear interpolation for absorb, quadratic for others */ + float interp = (mState == STATE_ABSORB) + ? t : mInterpolator.getInterpolation(t); + + mValue = mValueStart + (mValueFinish - mValueStart) * interp; + + if (t >= 1.0f) { + switch (mState) { + case STATE_ABSORB: + startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); + break; + case STATE_RELEASE: + mState = STATE_IDLE; + break; + } + } + + return true; + } + + public float getValue() { + return mValue; + } + + private long now() { + return AnimationTime.get(); + } +} |