1f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin/* 2f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Copyright (C) 2010 The Android Open Source Project 3f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 4f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Licensed under the Apache License, Version 2.0 (the "License"); 5f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * you may not use this file except in compliance with the License. 6f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * You may obtain a copy of the License at 7f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 8f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * http://www.apache.org/licenses/LICENSE-2.0 9f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * 10f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * Unless required by applicable law or agreed to in writing, software 11f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * distributed under the License is distributed on an "AS IS" BASIS, 12f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * See the License for the specific language governing permissions and 14f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin * limitations under the License. 15f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin */ 16f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 17f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linpackage com.android.gallery3d.ui; 18f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 1977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Changimport com.android.gallery3d.common.Utils; 20f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.ui.PositionRepository.Position; 21f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport com.android.gallery3d.util.GalleryUtils; 22f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 23f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport android.opengl.Matrix; 2477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Changimport android.os.SystemClock; 2577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Changimport android.view.animation.DecelerateInterpolator; 2677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Changimport android.view.animation.Interpolator; 27f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 28f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.opengles.GL11; 29f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linimport javax.microedition.khronos.opengles.GL11ExtensionPack; 30f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 31f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin// This class does the overscroll effect. 32f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Linclass Paper { 33f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final String TAG = "Paper"; 34f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private static final int ROTATE_FACTOR = 4; 3577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private EdgeAnimation mAnimationLeft = new EdgeAnimation(); 3677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private EdgeAnimation mAnimationRight = new EdgeAnimation(); 37f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private int mWidth, mHeight; 38f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin private float[] mMatrix = new float[16]; 39f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 40f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void overScroll(float distance) { 4177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang distance /= mWidth; // make it relative to width 42f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin if (distance < 0) { 4377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationLeft.onPull(-distance); 44f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } else { 4577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationRight.onPull(distance); 46f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 47f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 48f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 4977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public void edgeReached(float velocity) { 5077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang velocity /= mWidth; // make it relative to width 5177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (velocity < 0) { 5277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationRight.onAbsorb(-velocity); 5377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } else { 5477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationLeft.onAbsorb(velocity); 5577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 5677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 5777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 5877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public void onRelease() { 5977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationLeft.onRelease(); 6077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mAnimationRight.onRelease(); 6177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 6277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 6377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public boolean advanceAnimation() { 6477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // Note that we use "|" because we want both animations get updated. 6577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang return mAnimationLeft.update() | mAnimationRight.update(); 66f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 67f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 68f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public void setSize(int width, int height) { 69f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mWidth = width; 70f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin mHeight = height; 71f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 72f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 73f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public float[] getTransform(Position target, Position base, 74f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float scrollX, float scrollY) { 75f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float left = mAnimationLeft.getValue(); 76f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float right = mAnimationRight.getValue(); 77f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float screenX = target.x - scrollX; 7877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // We linearly interpolate the value [left, right] for the screenX 7977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // range int [-1/4, 5/4]*mWidth. So if part of the thumbnail is outside 8077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // the screen, we still get some transform. 8177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang float x = screenX + mWidth / 4; 8277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang int range = 3 * mWidth / 2; 8377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang float t = ((range - x) * left - x * right) / range; 84f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // compress t to the range (-1, 1) by the function 85f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // f(t) = (1 / (1 + e^-t) - 0.5) * 2 86f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin // then multiply by 90 to make the range (-45, 45) 87f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin float degrees = 88f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin (1 / (1 + (float) Math.exp(-t * ROTATE_FACTOR)) - 0.5f) * 2 * -45; 89f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Matrix.setIdentityM(mMatrix, 0); 90f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Matrix.translateM(mMatrix, 0, mMatrix, 0, base.x, base.y, base.z); 91f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Matrix.rotateM(mMatrix, 0, degrees, 0, 1, 0); 92f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin Matrix.translateM(mMatrix, 0, mMatrix, 0, 93f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin target.x - base.x, target.y - base.y, target.z - base.z); 94f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return mMatrix; 95f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 96f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 97f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 9877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang// This class follows the structure of frameworks's EdgeEffect class. 9977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Changclass EdgeAnimation { 10077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final String TAG = "EdgeAnimation"; 10177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 10277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int STATE_IDLE = 0; 10377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int STATE_PULL = 1; 10477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int STATE_ABSORB = 2; 10577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int STATE_RELEASE = 3; 10677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 10777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // Time it will take the effect to fully done in ms 10877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int ABSORB_TIME = 200; 10977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final int RELEASE_TIME = 500; 11077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 11177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private static final float VELOCITY_FACTOR = 0.1f; 11277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 11377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private final Interpolator mInterpolator; 11477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 11577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private int mState; 11677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private long mAnimationStartTime; 11777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private float mValue; 118f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 11977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private float mValueStart; 12077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private float mValueFinish; 12177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private long mStartTime; 12277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private long mDuration; 123f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 12477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public EdgeAnimation() { 12577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mInterpolator = new DecelerateInterpolator(); 12677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mState = STATE_IDLE; 127f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 128f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 12977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private void startAnimation(float start, float finish, long duration, 13077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang int newState) { 13177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mValueStart = start; 13277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mValueFinish = finish; 13377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mDuration = duration; 13477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mStartTime = now(); 13577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mState = newState; 13677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 13777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 13877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // The deltaDistance's magnitude is in the range of -1 (no change) to 1. 13977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // The value 1 is the full length of the view. Negative values means the 14077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang // movement is in the opposite direction. 14177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public void onPull(float deltaDistance) { 14277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (mState == STATE_ABSORB) return; 14377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mValue = Utils.clamp(mValue + deltaDistance, -1.0f, 1.0f); 14477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mState = STATE_PULL; 14577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 14677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 14777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public void onRelease() { 14877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (mState == STATE_IDLE || mState == STATE_ABSORB) return; 14977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); 15077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 151f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 15277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public void onAbsorb(float velocity) { 15377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang float finish = Utils.clamp(mValue + velocity * VELOCITY_FACTOR, 15477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang -1.0f, 1.0f); 15577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang startAnimation(mValue, finish, ABSORB_TIME, STATE_ABSORB); 15677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 157f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 15877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang public boolean update() { 15977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (mState == STATE_IDLE) return false; 16077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (mState == STATE_PULL) return true; 16177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 16277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang float t = Utils.clamp((float)(now() - mStartTime) / mDuration, 0.0f, 1.0f); 16377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang /* Use linear interpolation for absorb, quadratic for others */ 16477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang float interp = (mState == STATE_ABSORB) 16577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang ? t : mInterpolator.getInterpolation(t); 16677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 16777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mValue = mValueStart + (mValueFinish - mValueStart) * interp; 16877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 16977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang if (t >= 1.0f) { 17077dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang switch (mState) { 17177dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang case STATE_ABSORB: 17277dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); 17377dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang break; 17477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang case STATE_RELEASE: 17577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang mState = STATE_IDLE; 17677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang break; 17777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 178f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 17977dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 180f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin return true; 181f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 182f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin 183f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin public float getValue() { 18477dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang return mValue; 18577dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang } 18677dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang 18777dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang private long now() { 18877dcf07756dcf918c56afb0a8f7605577a7ce8e0Chih-Chung Chang return SystemClock.uptimeMillis(); 189f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin } 190f9a0a4306d589b4a4e20554fed512a603426bfa1Owen Lin} 191