Paper.java revision 28cb4161da5fc3756933ca67d509b8af1c6275f1
17c8da7ce66017295a65ec028084b90800be377f8James Zern/* 27c8da7ce66017295a65ec028084b90800be377f8James Zern * Copyright (C) 2010 The Android Open Source Project 37c8da7ce66017295a65ec028084b90800be377f8James Zern * 47c8da7ce66017295a65ec028084b90800be377f8James Zern * Licensed under the Apache License, Version 2.0 (the "License"); 57c8da7ce66017295a65ec028084b90800be377f8James Zern * you may not use this file except in compliance with the License. 67c8da7ce66017295a65ec028084b90800be377f8James Zern * You may obtain a copy of the License at 77c8da7ce66017295a65ec028084b90800be377f8James Zern * 87c8da7ce66017295a65ec028084b90800be377f8James Zern * http://www.apache.org/licenses/LICENSE-2.0 97c8da7ce66017295a65ec028084b90800be377f8James Zern * 107c8da7ce66017295a65ec028084b90800be377f8James Zern * Unless required by applicable law or agreed to in writing, software 117c8da7ce66017295a65ec028084b90800be377f8James Zern * distributed under the License is distributed on an "AS IS" BASIS, 127c8da7ce66017295a65ec028084b90800be377f8James Zern * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 137c8da7ce66017295a65ec028084b90800be377f8James Zern * See the License for the specific language governing permissions and 147c8da7ce66017295a65ec028084b90800be377f8James Zern * limitations under the License. 157c8da7ce66017295a65ec028084b90800be377f8James Zern */ 167c8da7ce66017295a65ec028084b90800be377f8James Zern 177c8da7ce66017295a65ec028084b90800be377f8James Zernpackage com.android.gallery3d.ui; 187c8da7ce66017295a65ec028084b90800be377f8James Zern 197c8da7ce66017295a65ec028084b90800be377f8James Zernimport android.graphics.Rect; 207c8da7ce66017295a65ec028084b90800be377f8James Zernimport android.opengl.Matrix; 217c8da7ce66017295a65ec028084b90800be377f8James Zernimport android.view.animation.DecelerateInterpolator; 227c8da7ce66017295a65ec028084b90800be377f8James Zernimport android.view.animation.Interpolator; 237c8da7ce66017295a65ec028084b90800be377f8James Zern 247c8da7ce66017295a65ec028084b90800be377f8James Zernimport com.android.gallery3d.common.Utils; 257c8da7ce66017295a65ec028084b90800be377f8James Zern 267c8da7ce66017295a65ec028084b90800be377f8James Zern// This class does the overscroll effect. 277c8da7ce66017295a65ec028084b90800be377f8James Zernclass Paper { 287c8da7ce66017295a65ec028084b90800be377f8James Zern @SuppressWarnings("unused") 297c8da7ce66017295a65ec028084b90800be377f8James Zern private static final String TAG = "Paper"; 307c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int ROTATE_FACTOR = 4; 317c8da7ce66017295a65ec028084b90800be377f8James Zern private EdgeAnimation mAnimationLeft = new EdgeAnimation(); 327c8da7ce66017295a65ec028084b90800be377f8James Zern private EdgeAnimation mAnimationRight = new EdgeAnimation(); 337c8da7ce66017295a65ec028084b90800be377f8James Zern private int mWidth; 347c8da7ce66017295a65ec028084b90800be377f8James Zern private float[] mMatrix = new float[16]; 357c8da7ce66017295a65ec028084b90800be377f8James Zern 367c8da7ce66017295a65ec028084b90800be377f8James Zern public void overScroll(float distance) { 377c8da7ce66017295a65ec028084b90800be377f8James Zern distance /= mWidth; // make it relative to width 387c8da7ce66017295a65ec028084b90800be377f8James Zern if (distance < 0) { 397c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationLeft.onPull(-distance); 407c8da7ce66017295a65ec028084b90800be377f8James Zern } else { 417c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationRight.onPull(distance); 427c8da7ce66017295a65ec028084b90800be377f8James Zern } 437c8da7ce66017295a65ec028084b90800be377f8James Zern } 447c8da7ce66017295a65ec028084b90800be377f8James Zern 457c8da7ce66017295a65ec028084b90800be377f8James Zern public void edgeReached(float velocity) { 467c8da7ce66017295a65ec028084b90800be377f8James Zern velocity /= mWidth; // make it relative to width 477c8da7ce66017295a65ec028084b90800be377f8James Zern if (velocity < 0) { 487c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationRight.onAbsorb(-velocity); 497c8da7ce66017295a65ec028084b90800be377f8James Zern } else { 507c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationLeft.onAbsorb(velocity); 517c8da7ce66017295a65ec028084b90800be377f8James Zern } 527c8da7ce66017295a65ec028084b90800be377f8James Zern } 537c8da7ce66017295a65ec028084b90800be377f8James Zern 547c8da7ce66017295a65ec028084b90800be377f8James Zern public void onRelease() { 557c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationLeft.onRelease(); 567c8da7ce66017295a65ec028084b90800be377f8James Zern mAnimationRight.onRelease(); 577c8da7ce66017295a65ec028084b90800be377f8James Zern } 587c8da7ce66017295a65ec028084b90800be377f8James Zern 597c8da7ce66017295a65ec028084b90800be377f8James Zern public boolean advanceAnimation() { 607c8da7ce66017295a65ec028084b90800be377f8James Zern // Note that we use "|" because we want both animations get updated. 617c8da7ce66017295a65ec028084b90800be377f8James Zern return mAnimationLeft.update() | mAnimationRight.update(); 627c8da7ce66017295a65ec028084b90800be377f8James Zern } 637c8da7ce66017295a65ec028084b90800be377f8James Zern 647c8da7ce66017295a65ec028084b90800be377f8James Zern public void setSize(int width, int height) { 657c8da7ce66017295a65ec028084b90800be377f8James Zern mWidth = width; 667c8da7ce66017295a65ec028084b90800be377f8James Zern } 677c8da7ce66017295a65ec028084b90800be377f8James Zern 687c8da7ce66017295a65ec028084b90800be377f8James Zern public float[] getTransform(Rect rect, float scrollX) { 697c8da7ce66017295a65ec028084b90800be377f8James Zern float left = mAnimationLeft.getValue(); 707c8da7ce66017295a65ec028084b90800be377f8James Zern float right = mAnimationRight.getValue(); 717c8da7ce66017295a65ec028084b90800be377f8James Zern float screenX = rect.centerX() - scrollX; 727c8da7ce66017295a65ec028084b90800be377f8James Zern // We linearly interpolate the value [left, right] for the screenX 737c8da7ce66017295a65ec028084b90800be377f8James Zern // range int [-1/4, 5/4]*mWidth. So if part of the thumbnail is outside 747c8da7ce66017295a65ec028084b90800be377f8James Zern // the screen, we still get some transform. 757c8da7ce66017295a65ec028084b90800be377f8James Zern float x = screenX + mWidth / 4; 767c8da7ce66017295a65ec028084b90800be377f8James Zern int range = 3 * mWidth / 2; 777c8da7ce66017295a65ec028084b90800be377f8James Zern float t = ((range - x) * left - x * right) / range; 787c8da7ce66017295a65ec028084b90800be377f8James Zern // compress t to the range (-1, 1) by the function 797c8da7ce66017295a65ec028084b90800be377f8James Zern // f(t) = (1 / (1 + e^-t) - 0.5) * 2 807c8da7ce66017295a65ec028084b90800be377f8James Zern // then multiply by 90 to make the range (-45, 45) 817c8da7ce66017295a65ec028084b90800be377f8James Zern float degrees = 827c8da7ce66017295a65ec028084b90800be377f8James Zern (1 / (1 + (float) Math.exp(-t * ROTATE_FACTOR)) - 0.5f) * 2 * -45; 837c8da7ce66017295a65ec028084b90800be377f8James Zern Matrix.setIdentityM(mMatrix, 0); 847c8da7ce66017295a65ec028084b90800be377f8James Zern Matrix.translateM(mMatrix, 0, mMatrix, 0, rect.centerX(), rect.centerY(), 0); 857c8da7ce66017295a65ec028084b90800be377f8James Zern Matrix.rotateM(mMatrix, 0, degrees, 0, 1, 0); 867c8da7ce66017295a65ec028084b90800be377f8James Zern Matrix.translateM(mMatrix, 0, mMatrix, 0, -rect.width() / 2, -rect.height() / 2, 0); 877c8da7ce66017295a65ec028084b90800be377f8James Zern return mMatrix; 887c8da7ce66017295a65ec028084b90800be377f8James Zern } 897c8da7ce66017295a65ec028084b90800be377f8James Zern} 907c8da7ce66017295a65ec028084b90800be377f8James Zern 917c8da7ce66017295a65ec028084b90800be377f8James Zern// This class follows the structure of frameworks's EdgeEffect class. 927c8da7ce66017295a65ec028084b90800be377f8James Zernclass EdgeAnimation { 937c8da7ce66017295a65ec028084b90800be377f8James Zern @SuppressWarnings("unused") 947c8da7ce66017295a65ec028084b90800be377f8James Zern private static final String TAG = "EdgeAnimation"; 957c8da7ce66017295a65ec028084b90800be377f8James Zern 967c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int STATE_IDLE = 0; 977c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int STATE_PULL = 1; 987c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int STATE_ABSORB = 2; 997c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int STATE_RELEASE = 3; 1007c8da7ce66017295a65ec028084b90800be377f8James Zern 1017c8da7ce66017295a65ec028084b90800be377f8James Zern // Time it will take the effect to fully done in ms 1027c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int ABSORB_TIME = 200; 1037c8da7ce66017295a65ec028084b90800be377f8James Zern private static final int RELEASE_TIME = 500; 1047c8da7ce66017295a65ec028084b90800be377f8James Zern 1057c8da7ce66017295a65ec028084b90800be377f8James Zern private static final float VELOCITY_FACTOR = 0.1f; 1067c8da7ce66017295a65ec028084b90800be377f8James Zern 1077c8da7ce66017295a65ec028084b90800be377f8James Zern private final Interpolator mInterpolator; 1087c8da7ce66017295a65ec028084b90800be377f8James Zern 1097c8da7ce66017295a65ec028084b90800be377f8James Zern private int mState; 1107c8da7ce66017295a65ec028084b90800be377f8James Zern private float mValue; 1117c8da7ce66017295a65ec028084b90800be377f8James Zern 1127c8da7ce66017295a65ec028084b90800be377f8James Zern private float mValueStart; 1137c8da7ce66017295a65ec028084b90800be377f8James Zern private float mValueFinish; 1147c8da7ce66017295a65ec028084b90800be377f8James Zern private long mStartTime; 1157c8da7ce66017295a65ec028084b90800be377f8James Zern private long mDuration; 1167c8da7ce66017295a65ec028084b90800be377f8James Zern 1177c8da7ce66017295a65ec028084b90800be377f8James Zern public EdgeAnimation() { 1187c8da7ce66017295a65ec028084b90800be377f8James Zern mInterpolator = new DecelerateInterpolator(); 1197c8da7ce66017295a65ec028084b90800be377f8James Zern mState = STATE_IDLE; 1207c8da7ce66017295a65ec028084b90800be377f8James Zern } 1217c8da7ce66017295a65ec028084b90800be377f8James Zern 1227c8da7ce66017295a65ec028084b90800be377f8James Zern private void startAnimation(float start, float finish, long duration, 1237c8da7ce66017295a65ec028084b90800be377f8James Zern int newState) { 1247c8da7ce66017295a65ec028084b90800be377f8James Zern mValueStart = start; 1257c8da7ce66017295a65ec028084b90800be377f8James Zern mValueFinish = finish; 1267c8da7ce66017295a65ec028084b90800be377f8James Zern mDuration = duration; 1277c8da7ce66017295a65ec028084b90800be377f8James Zern mStartTime = now(); 1287c8da7ce66017295a65ec028084b90800be377f8James Zern mState = newState; 1297c8da7ce66017295a65ec028084b90800be377f8James Zern } 1307c8da7ce66017295a65ec028084b90800be377f8James Zern 1317c8da7ce66017295a65ec028084b90800be377f8James Zern // The deltaDistance's magnitude is in the range of -1 (no change) to 1. 1327c8da7ce66017295a65ec028084b90800be377f8James Zern // The value 1 is the full length of the view. Negative values means the 1337c8da7ce66017295a65ec028084b90800be377f8James Zern // movement is in the opposite direction. 1347c8da7ce66017295a65ec028084b90800be377f8James Zern public void onPull(float deltaDistance) { 1357c8da7ce66017295a65ec028084b90800be377f8James Zern if (mState == STATE_ABSORB) return; 1367c8da7ce66017295a65ec028084b90800be377f8James Zern mValue = Utils.clamp(mValue + deltaDistance, -1.0f, 1.0f); 1377c8da7ce66017295a65ec028084b90800be377f8James Zern mState = STATE_PULL; 1387c8da7ce66017295a65ec028084b90800be377f8James Zern } 1397c8da7ce66017295a65ec028084b90800be377f8James Zern 1407c8da7ce66017295a65ec028084b90800be377f8James Zern public void onRelease() { 1417c8da7ce66017295a65ec028084b90800be377f8James Zern if (mState == STATE_IDLE || mState == STATE_ABSORB) return; 142 startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); 143 } 144 145 public void onAbsorb(float velocity) { 146 float finish = Utils.clamp(mValue + velocity * VELOCITY_FACTOR, 147 -1.0f, 1.0f); 148 startAnimation(mValue, finish, ABSORB_TIME, STATE_ABSORB); 149 } 150 151 public boolean update() { 152 if (mState == STATE_IDLE) return false; 153 if (mState == STATE_PULL) return true; 154 155 float t = Utils.clamp((float)(now() - mStartTime) / mDuration, 0.0f, 1.0f); 156 /* Use linear interpolation for absorb, quadratic for others */ 157 float interp = (mState == STATE_ABSORB) 158 ? t : mInterpolator.getInterpolation(t); 159 160 mValue = mValueStart + (mValueFinish - mValueStart) * interp; 161 162 if (t >= 1.0f) { 163 switch (mState) { 164 case STATE_ABSORB: 165 startAnimation(mValue, 0, RELEASE_TIME, STATE_RELEASE); 166 break; 167 case STATE_RELEASE: 168 mState = STATE_IDLE; 169 break; 170 } 171 } 172 173 return true; 174 } 175 176 public float getValue() { 177 return mValue; 178 } 179 180 private long now() { 181 return AnimationTime.get(); 182 } 183} 184