blob: 3b67a049a1e3cc094497c298004c658589d03128 [file] [log] [blame]
Owen Lina2fba682011-08-17 22:07:43 +08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package com.android.gallery3d.ui;
18
Owen Lin83a036c2012-03-22 14:14:40 +080019import android.graphics.Rect;
Owen Lina2fba682011-08-17 22:07:43 +080020import android.opengl.Matrix;
Chih-Chung Changca078522011-09-26 10:40:38 +080021import android.view.animation.DecelerateInterpolator;
22import android.view.animation.Interpolator;
Owen Lina2fba682011-08-17 22:07:43 +080023
Owen Lin73a04ff2012-03-14 17:27:24 +080024import com.android.gallery3d.common.Utils;
Owen Lina2fba682011-08-17 22:07:43 +080025
26// This class does the overscroll effect.
27class Paper {
28 private static final String TAG = "Paper";
29 private static final int ROTATE_FACTOR = 4;
Chih-Chung Changca078522011-09-26 10:40:38 +080030 private EdgeAnimation mAnimationLeft = new EdgeAnimation();
31 private EdgeAnimation mAnimationRight = new EdgeAnimation();
Owen Lina2fba682011-08-17 22:07:43 +080032 private int mWidth, mHeight;
33 private float[] mMatrix = new float[16];
34
35 public void overScroll(float distance) {
Chih-Chung Changca078522011-09-26 10:40:38 +080036 distance /= mWidth; // make it relative to width
Owen Lina2fba682011-08-17 22:07:43 +080037 if (distance < 0) {
Chih-Chung Changca078522011-09-26 10:40:38 +080038 mAnimationLeft.onPull(-distance);
Owen Lina2fba682011-08-17 22:07:43 +080039 } else {
Chih-Chung Changca078522011-09-26 10:40:38 +080040 mAnimationRight.onPull(distance);
Owen Lina2fba682011-08-17 22:07:43 +080041 }
42 }
43
Chih-Chung Changca078522011-09-26 10:40:38 +080044 public void edgeReached(float velocity) {
45 velocity /= mWidth; // make it relative to width
46 if (velocity < 0) {
47 mAnimationRight.onAbsorb(-velocity);
48 } else {
49 mAnimationLeft.onAbsorb(velocity);
50 }
51 }
52
53 public void onRelease() {
54 mAnimationLeft.onRelease();
55 mAnimationRight.onRelease();
56 }
57
58 public boolean advanceAnimation() {
59 // Note that we use "|" because we want both animations get updated.
60 return mAnimationLeft.update() | mAnimationRight.update();
Owen Lina2fba682011-08-17 22:07:43 +080061 }
62
63 public void setSize(int width, int height) {
64 mWidth = width;
65 mHeight = height;
66 }
67
Owen Lin83a036c2012-03-22 14:14:40 +080068 public float[] getTransform(Rect rect, float scrollX) {
Owen Lina2fba682011-08-17 22:07:43 +080069 float left = mAnimationLeft.getValue();
70 float right = mAnimationRight.getValue();
Owen Lin83a036c2012-03-22 14:14:40 +080071 float screenX = rect.centerX() - scrollX;
Chih-Chung Changca078522011-09-26 10:40:38 +080072 // We linearly interpolate the value [left, right] for the screenX
73 // range int [-1/4, 5/4]*mWidth. So if part of the thumbnail is outside
74 // the screen, we still get some transform.
75 float x = screenX + mWidth / 4;
76 int range = 3 * mWidth / 2;
77 float t = ((range - x) * left - x * right) / range;
Owen Lina2fba682011-08-17 22:07:43 +080078 // compress t to the range (-1, 1) by the function
79 // f(t) = (1 / (1 + e^-t) - 0.5) * 2
80 // then multiply by 90 to make the range (-45, 45)
81 float degrees =
82 (1 / (1 + (float) Math.exp(-t * ROTATE_FACTOR)) - 0.5f) * 2 * -45;
83 Matrix.setIdentityM(mMatrix, 0);
Owen Lin83a036c2012-03-22 14:14:40 +080084 Matrix.translateM(mMatrix, 0, mMatrix, 0, rect.centerX(), rect.centerY(), 0);
Owen Lina2fba682011-08-17 22:07:43 +080085 Matrix.rotateM(mMatrix, 0, degrees, 0, 1, 0);
Owen Lin83a036c2012-03-22 14:14:40 +080086 Matrix.translateM(mMatrix, 0, mMatrix, 0, -rect.width() / 2, -rect.height() / 2, 0);
Owen Lina2fba682011-08-17 22:07:43 +080087 return mMatrix;
88 }
89}
90
Chih-Chung Changca078522011-09-26 10:40:38 +080091// This class follows the structure of frameworks's EdgeEffect class.
92class EdgeAnimation {
93 private static final String TAG = "EdgeAnimation";
Owen Lina2fba682011-08-17 22:07:43 +080094
Chih-Chung Changca078522011-09-26 10:40:38 +080095 private static final int STATE_IDLE = 0;
96 private static final int STATE_PULL = 1;
97 private static final int STATE_ABSORB = 2;
98 private static final int STATE_RELEASE = 3;
Owen Lina2fba682011-08-17 22:07:43 +080099
Chih-Chung Changca078522011-09-26 10:40:38 +0800100 // Time it will take the effect to fully done in ms
101 private static final int ABSORB_TIME = 200;
102 private static final int RELEASE_TIME = 500;
103
104 private static final float VELOCITY_FACTOR = 0.1f;
105
106 private final Interpolator mInterpolator;
107
108 private int mState;
Chih-Chung Changca078522011-09-26 10:40:38 +0800109 private float mValue;
110
111 private float mValueStart;
112 private float mValueFinish;
113 private long mStartTime;
114 private long mDuration;
115
116 public EdgeAnimation() {
117 mInterpolator = new DecelerateInterpolator();
118 mState = STATE_IDLE;
Owen Lina2fba682011-08-17 22:07:43 +0800119 }
120
Chih-Chung Changca078522011-09-26 10:40:38 +0800121 private void startAnimation(float start, float finish, long duration,
122 int newState) {
123 mValueStart = start;
124 mValueFinish = finish;
125 mDuration = duration;
126 mStartTime = now();
127 mState = newState;
128 }
129
130 // The deltaDistance's magnitude is in the range of -1 (no change) to 1.
131 // The value 1 is the full length of the view. Negative values means the
132 // movement is in the opposite direction.
133 public void onPull(float deltaDistance) {
134 if (mState == STATE_ABSORB) return;
135 mValue = Utils.clamp(mValue + deltaDistance, -1.0f, 1.0f);
136 mState = STATE_PULL;
137 }
138
139 public void onRelease() {
140 if (mState == STATE_IDLE || mState == STATE_ABSORB) return;
141 startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE);
142 }
143
144 public void onAbsorb(float velocity) {
145 float finish = Utils.clamp(mValue + velocity * VELOCITY_FACTOR,
146 -1.0f, 1.0f);
147 startAnimation(mValue, finish, ABSORB_TIME, STATE_ABSORB);
148 }
149
150 public boolean update() {
151 if (mState == STATE_IDLE) return false;
152 if (mState == STATE_PULL) return true;
153
154 float t = Utils.clamp((float)(now() - mStartTime) / mDuration, 0.0f, 1.0f);
155 /* Use linear interpolation for absorb, quadratic for others */
156 float interp = (mState == STATE_ABSORB)
157 ? t : mInterpolator.getInterpolation(t);
158
159 mValue = mValueStart + (mValueFinish - mValueStart) * interp;
160
161 if (t >= 1.0f) {
162 switch (mState) {
163 case STATE_ABSORB:
164 startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE);
165 break;
166 case STATE_RELEASE:
167 mState = STATE_IDLE;
168 break;
169 }
Owen Lina2fba682011-08-17 22:07:43 +0800170 }
171
Owen Lina2fba682011-08-17 22:07:43 +0800172 return true;
173 }
174
175 public float getValue() {
Chih-Chung Changca078522011-09-26 10:40:38 +0800176 return mValue;
177 }
178
179 private long now() {
Chih-Chung Chang66960432012-03-02 18:14:53 +0800180 return AnimationTime.get();
Owen Lina2fba682011-08-17 22:07:43 +0800181 }
182}