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