Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 1 | /* |
| 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 | |
| 17 | package com.android.gallery3d.ui; |
| 18 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 19 | import android.graphics.Rect; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 20 | import android.opengl.Matrix; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 21 | import android.view.animation.DecelerateInterpolator; |
| 22 | import android.view.animation.Interpolator; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 23 | |
Owen Lin | 73a04ff | 2012-03-14 17:27:24 +0800 | [diff] [blame] | 24 | import com.android.gallery3d.common.Utils; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 25 | |
| 26 | // This class does the overscroll effect. |
| 27 | class Paper { |
| 28 | private static final String TAG = "Paper"; |
| 29 | private static final int ROTATE_FACTOR = 4; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 30 | private EdgeAnimation mAnimationLeft = new EdgeAnimation(); |
| 31 | private EdgeAnimation mAnimationRight = new EdgeAnimation(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 32 | private int mWidth, mHeight; |
| 33 | private float[] mMatrix = new float[16]; |
| 34 | |
| 35 | public void overScroll(float distance) { |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 36 | distance /= mWidth; // make it relative to width |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 37 | if (distance < 0) { |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 38 | mAnimationLeft.onPull(-distance); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 39 | } else { |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 40 | mAnimationRight.onPull(distance); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 41 | } |
| 42 | } |
| 43 | |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 44 | 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 Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 61 | } |
| 62 | |
| 63 | public void setSize(int width, int height) { |
| 64 | mWidth = width; |
| 65 | mHeight = height; |
| 66 | } |
| 67 | |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 68 | public float[] getTransform(Rect rect, float scrollX) { |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 69 | float left = mAnimationLeft.getValue(); |
| 70 | float right = mAnimationRight.getValue(); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 71 | float screenX = rect.centerX() - scrollX; |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 72 | // 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 Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 78 | // 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 Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 84 | Matrix.translateM(mMatrix, 0, mMatrix, 0, rect.centerX(), rect.centerY(), 0); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 85 | Matrix.rotateM(mMatrix, 0, degrees, 0, 1, 0); |
Owen Lin | 83a036c | 2012-03-22 14:14:40 +0800 | [diff] [blame] | 86 | Matrix.translateM(mMatrix, 0, mMatrix, 0, -rect.width() / 2, -rect.height() / 2, 0); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 87 | return mMatrix; |
| 88 | } |
| 89 | } |
| 90 | |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 91 | // This class follows the structure of frameworks's EdgeEffect class. |
| 92 | class EdgeAnimation { |
| 93 | private static final String TAG = "EdgeAnimation"; |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 94 | |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 95 | 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 Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 99 | |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 100 | // 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 Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 109 | 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 Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 119 | } |
| 120 | |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 121 | 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 Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 170 | } |
| 171 | |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 172 | return true; |
| 173 | } |
| 174 | |
| 175 | public float getValue() { |
Chih-Chung Chang | ca07852 | 2011-09-26 10:40:38 +0800 | [diff] [blame] | 176 | return mValue; |
| 177 | } |
| 178 | |
| 179 | private long now() { |
Chih-Chung Chang | 6696043 | 2012-03-02 18:14:53 +0800 | [diff] [blame] | 180 | return AnimationTime.get(); |
Owen Lin | a2fba68 | 2011-08-17 22:07:43 +0800 | [diff] [blame] | 181 | } |
| 182 | } |