summaryrefslogtreecommitdiffstats
path: root/src/com/android/gallery3d/ui/Paper.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/com/android/gallery3d/ui/Paper.java')
-rw-r--r--src/com/android/gallery3d/ui/Paper.java183
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();
+ }
+}