Remove Position and PositionRepository.

Also fix a regression introduced in previous change.

Change-Id: I534c8ca7838f3518856cf48878dd49beeb331562
diff --git a/src/com/android/gallery3d/app/AlbumPage.java b/src/com/android/gallery3d/app/AlbumPage.java
index 37ddb18..ba21198 100644
--- a/src/com/android/gallery3d/app/AlbumPage.java
+++ b/src/com/android/gallery3d/app/AlbumPage.java
@@ -48,17 +48,13 @@
 import com.android.gallery3d.ui.GLView;
 import com.android.gallery3d.ui.GridDrawer;
 import com.android.gallery3d.ui.HighlightDrawer;
-import com.android.gallery3d.ui.PositionProvider;
-import com.android.gallery3d.ui.PositionRepository;
-import com.android.gallery3d.ui.PositionRepository.Position;
+import com.android.gallery3d.ui.RelativePosition;
 import com.android.gallery3d.ui.ScreenNailHolder;
 import com.android.gallery3d.ui.SelectionManager;
 import com.android.gallery3d.ui.SlotView;
 import com.android.gallery3d.util.Future;
 import com.android.gallery3d.util.GalleryUtils;
 
-import java.util.Random;
-
 public class AlbumPage extends ActivityState implements GalleryActionBar.ClusterRunner,
         SelectionManager.SelectionListener, MediaSet.SyncListener {
     @SuppressWarnings("unused")
@@ -109,6 +105,7 @@
 
     private int mLoadingBits = 0;
     private boolean mInitialSynced = false;
+    private RelativePosition mOpenCenter = new RelativePosition();
 
     private final GLView mRootPane = new GLView() {
         private final float mMatrix[] = new float[16];
@@ -132,12 +129,11 @@
                 mAlbumView.setSelectionDrawer(mGridDrawer);
             }
 
+            // Set the mSlotView as a reference point to the open animation
+            mOpenCenter.setReferencePosition(0, slotViewTop);
             mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
             GalleryUtils.setViewPointMatrix(mMatrix,
                     (right - left) / 2, (bottom - top) / 2, -mUserDistance);
-            // Reset position offset after the layout is changed.
-            PositionRepository.getInstance(mActivity).setOffset(
-                    0, slotViewTop);
         }
 
         @Override
@@ -257,6 +253,7 @@
         }
     }
 
+    @Override
     public void doCluster(int clusterType) {
         String basePath = mMediaSet.getPath().toString();
         String newPath = FilterUtils.newClusterPath(basePath, clusterType);
@@ -285,52 +282,19 @@
         Context context = mActivity.getAndroidContext();
         mVibrator = (Vibrator) context.getSystemService(Context.VIBRATOR_SERVICE);
 
-        startTransition(data);
-
         // Enable auto-select-all for mtp album
         if (data.getBoolean(KEY_AUTO_SELECT_ALL)) {
             mSelectionManager.selectAll();
         }
-    }
 
-    private void startTransition() {
-        final PositionRepository repository =
-                PositionRepository.getInstance(mActivity);
-        mSlotView.startTransition(new PositionProvider() {
-            private final Position mTempPosition = new Position();
-            public Position getPosition(int identity, Position target) {
-                Position p = repository.get(identity);
-                if (p != null) return p;
-                mTempPosition.set(target);
-                mTempPosition.z = 128;
-                return mTempPosition;
+        // Don't show animation if it is restored
+        if (restoreState == null && data != null) {
+            int[] center = data.getIntArray(KEY_SET_CENTER);
+            if (center != null) {
+                mOpenCenter.setAbsolutePosition(center[0], center[1]);
+                mSlotView.startScatteringAnimation(mOpenCenter);
             }
-        });
-    }
-
-    private void startTransition(Bundle data) {
-        final PositionRepository repository =
-                PositionRepository.getInstance(mActivity);
-        final int[] center = data == null
-                ? null
-                : data.getIntArray(KEY_SET_CENTER);
-        final Random random = new Random();
-        mSlotView.startTransition(new PositionProvider() {
-            private final Position mTempPosition = new Position();
-            public Position getPosition(int identity, Position target) {
-                Position p = repository.get(identity);
-                if (p != null) return p;
-                if (center != null) {
-                    random.setSeed(identity);
-                    mTempPosition.set(center[0], center[1],
-                            0, random.nextInt(60) - 30, 0);
-                } else {
-                    mTempPosition.set(target);
-                    mTempPosition.z = 128;
-                }
-                return mTempPosition;
-            }
-        });
+        }
     }
 
     @Override
@@ -338,9 +302,6 @@
         super.onResume();
         mIsActive = true;
         setContentPane(mRootPane);
-        // Reset position offset for resuming.
-        PositionRepository.getInstance(mActivity).setOffset(
-                mSlotView.bounds().left, mSlotView.bounds().top);
 
         Path path = mMediaSet.getPath();
         boolean enableHomeButton = (mActivity.getStateManager().getStateCount() > 1) |
@@ -558,11 +519,11 @@
                 if (data == null) return;
                 mFocusIndex = data.getIntExtra(PhotoPage.KEY_INDEX_HINT, 0);
                 mSlotView.setCenterIndex(mFocusIndex);
-                startTransition();
+                mSlotView.startRestoringAnimation(mFocusIndex);
                 break;
             }
             case REQUEST_DO_ANIMATION: {
-                startTransition(null);
+                mSlotView.startRisingAnimation();
                 break;
             }
         }
diff --git a/src/com/android/gallery3d/app/AlbumSetPage.java b/src/com/android/gallery3d/app/AlbumSetPage.java
index d526b66..7b87501 100644
--- a/src/com/android/gallery3d/app/AlbumSetPage.java
+++ b/src/com/android/gallery3d/app/AlbumSetPage.java
@@ -47,9 +47,6 @@
 import com.android.gallery3d.ui.GLView;
 import com.android.gallery3d.ui.GridDrawer;
 import com.android.gallery3d.ui.HighlightDrawer;
-import com.android.gallery3d.ui.PositionProvider;
-import com.android.gallery3d.ui.PositionRepository;
-import com.android.gallery3d.ui.PositionRepository.Position;
 import com.android.gallery3d.ui.SelectionManager;
 import com.android.gallery3d.ui.SlotView;
 import com.android.gallery3d.util.Future;
@@ -133,10 +130,6 @@
             }
 
             mSlotView.layout(0, slotViewTop, slotViewRight, slotViewBottom);
-
-            // Reset position offset after the layout is changed.
-            PositionRepository.getInstance(mActivity).setOffset(
-                    0, slotViewTop);
         }
 
         @Override
@@ -167,18 +160,13 @@
         } else if (mSelectionManager.inSelectionMode()) {
             mSelectionManager.leaveSelectionMode();
         } else {
-            // TODO: fix this regression during refactoring
-            // mSlotView.savePositions(
-            //        PositionRepository.getInstance(mActivity));
             super.onBackPressed();
         }
     }
 
-    private void savePositions(int slotIndex, int center[]) {
+    private void getSlotCenter(int slotIndex, int center[]) {
         Rect offset = new Rect();
         mRootPane.getBoundsOf(mSlotView, offset);
-        // TODO: fix this regression during refactoring
-        // mSlotView.savePositions(PositionRepository.getInstance(mActivity));
         Rect r = mSlotView.getSlotRect(slotIndex);
         int scrollX = mSlotView.getScrollX();
         int scrollY = mSlotView.getScrollY();
@@ -198,7 +186,7 @@
             Bundle data = new Bundle(getData());
             String mediaPath = targetSet.getPath().toString();
             int[] center = new int[2];
-            savePositions(slotIndex, center);
+            getSlotCenter(slotIndex, center);
             data.putIntArray(AlbumPage.KEY_SET_CENTER, center);
             if (mGetAlbum && targetSet.isLeafAlbum()) {
                 Activity activity = (Activity) mActivity;
@@ -254,6 +242,7 @@
         }
     }
 
+    @Override
     public void doCluster(int clusterType) {
         String basePath = mMediaSet.getPath().toString();
         String newPath = FilterUtils.switchClusterPath(basePath, clusterType);
@@ -278,7 +267,6 @@
         mActionBar = mActivity.getGalleryActionBar();
         mSelectedAction = data.getInt(AlbumSetPage.KEY_SELECTED_CLUSTER_TYPE,
                 FilterUtils.CLUSTER_BY_ALBUM);
-        startTransition();
     }
 
     private void clearLoadingBit(int loadingBit) {
@@ -331,9 +319,6 @@
         super.onResume();
         mIsActive = true;
         setContentPane(mRootPane);
-        // Reset position offset for resuming.
-        PositionRepository.getInstance(mActivity).setOffset(
-                mSlotView.bounds().left, mSlotView.bounds().top);
 
         // Set the reload bit here to prevent it exit this page in clearLoadingBit().
         setLoadingBit(BIT_LOADING_RELOAD);
@@ -395,6 +380,7 @@
 
         mActionModeHandler = new ActionModeHandler(mActivity, mSelectionManager);
         mActionModeHandler.setActionModeListener(new ActionModeListener() {
+            @Override
             public boolean onActionItemClicked(MenuItem item) {
                 return onItemSelected(item);
             }
@@ -508,27 +494,11 @@
     protected void onStateResult(int requestCode, int resultCode, Intent data) {
         switch (requestCode) {
             case REQUEST_DO_ANIMATION: {
-                startTransition();
+                mSlotView.startRisingAnimation();
             }
         }
     }
 
-    private void startTransition() {
-        final PositionRepository repository =
-                PositionRepository.getInstance(mActivity);
-        mSlotView.startTransition(new PositionProvider() {
-            private final Position mTempPosition = new Position();
-            public Position getPosition(int identity, Position target) {
-                Position p = repository.get(identity);
-                if (p == null) {
-                    p = mTempPosition;
-                    p.set(target.x, target.y, 128, target.theta, 1);
-                }
-                return p;
-            }
-        });
-    }
-
     private String getSelectedString() {
         int count = mSelectionManager.getSelectedCount();
         int action = mActionBar.getClusterTypeAction();
@@ -539,8 +509,8 @@
         return String.format(format, count);
     }
 
+    @Override
     public void onSelectionModeChange(int mode) {
-
         switch (mode) {
             case SelectionManager.ENTER_SELECTION_MODE: {
                 mActionBar.disableClusterMenu(true);
@@ -564,6 +534,7 @@
         }
     }
 
+    @Override
     public void onSelectionChange(Path path, boolean selected) {
         Utils.assertTrue(mActionMode != null);
         mActionModeHandler.setTitle(getSelectedString());
@@ -584,6 +555,7 @@
                     mSelectionManager);
             mDetailsHelper = new DetailsHelper(mActivity, mRootPane, mDetailsSource);
             mDetailsHelper.setCloseListener(new CloseListener() {
+                @Override
                 public void onClose() {
                     hideDetails();
                 }
@@ -615,10 +587,12 @@
     }
 
     private class MyLoadingListener implements LoadingListener {
+        @Override
         public void onLoadingStarted() {
             setLoadingBit(BIT_LOADING_RELOAD);
         }
 
+        @Override
         public void onLoadingFinished() {
             clearLoadingBit(BIT_LOADING_RELOAD);
         }
@@ -626,16 +600,20 @@
 
     private class MyDetailsSource implements DetailsHelper.DetailsSource {
         private int mIndex;
+
+        @Override
         public int size() {
             return mAlbumSetDataAdapter.size();
         }
 
+        @Override
         public int getIndex() {
             return mIndex;
         }
 
         // If requested index is out of active window, suggest a valid index.
         // If there is no valid index available, return -1.
+        @Override
         public int findIndex(int indexHint) {
             if (mAlbumSetDataAdapter.isActive(indexHint)) {
                 mIndex = indexHint;
@@ -648,6 +626,7 @@
             return mIndex;
         }
 
+        @Override
         public MediaDetails getDetails() {
             MediaObject item = mAlbumSetDataAdapter.getMediaSet(mIndex);
             if (item != null) {
diff --git a/src/com/android/gallery3d/app/PhotoPage.java b/src/com/android/gallery3d/app/PhotoPage.java
index 9a1992e..050fad4 100644
--- a/src/com/android/gallery3d/app/PhotoPage.java
+++ b/src/com/android/gallery3d/app/PhotoPage.java
@@ -31,8 +31,8 @@
 import android.view.MenuInflater;
 import android.view.MenuItem;
 import android.view.View;
-import android.view.ViewGroup;
 import android.view.View.MeasureSpec;
+import android.view.ViewGroup;
 import android.view.WindowManager;
 import android.widget.ShareActionProvider;
 import android.widget.Toast;
@@ -58,8 +58,6 @@
 import com.android.gallery3d.ui.ScreenNail;
 import com.android.gallery3d.ui.ScreenNailHolder;
 import com.android.gallery3d.ui.PhotoView;
-import com.android.gallery3d.ui.PositionRepository;
-import com.android.gallery3d.ui.PositionRepository.Position;
 import com.android.gallery3d.ui.SelectionManager;
 import com.android.gallery3d.ui.SynchronizedHandler;
 import com.android.gallery3d.ui.UserInteractionListener;
@@ -142,8 +140,6 @@
         protected void onLayout(
                 boolean changed, int left, int top, int right, int bottom) {
             mPhotoView.layout(0, 0, right - left, bottom - top);
-            // Reset position offset after the layout is changed.
-            PositionRepository.getInstance(mActivity).setOffset(0, 0);
             int filmStripHeight = 0;
             if (mFilmStripView != null) {
                 mFilmStripView.measure(
@@ -428,17 +424,6 @@
         if (mShowDetails) {
             hideDetails();
         } else {
-            PositionRepository repository = PositionRepository.getInstance(mActivity);
-            repository.clear();
-            if (mCurrentPhoto != null) {
-                Position position = new Position();
-                position.x = mRootPane.getWidth() / 2;
-                position.y = mRootPane.getHeight() / 2;
-                position.z = -1000;
-                repository.putPosition(
-                        System.identityHashCode(mCurrentPhoto.getPath()),
-                        position);
-            }
             super.onBackPressed();
         }
     }
@@ -653,9 +638,6 @@
         super.onResume();
         mIsActive = true;
         setContentPane(mRootPane);
-        // Reset position offset for resuming.
-        PositionRepository.getInstance(mActivity).setOffset(
-                mPhotoView.bounds().left, mPhotoView.bounds().top);
 
         mModel.resume();
         mPhotoView.resume();
diff --git a/src/com/android/gallery3d/ui/PositionProvider.java b/src/com/android/gallery3d/ui/PositionProvider.java
deleted file mode 100644
index f1cb4e0..0000000
--- a/src/com/android/gallery3d/ui/PositionProvider.java
+++ /dev/null
@@ -1,23 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.ui;
-
-import com.android.gallery3d.ui.PositionRepository.Position;
-
-public interface PositionProvider {
-    public Position getPosition(int identity, Position target);
-}
diff --git a/src/com/android/gallery3d/ui/PositionRepository.java b/src/com/android/gallery3d/ui/PositionRepository.java
deleted file mode 100644
index 1827c3b..0000000
--- a/src/com/android/gallery3d/ui/PositionRepository.java
+++ /dev/null
@@ -1,140 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- *      http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.gallery3d.ui;
-
-import android.util.SparseArray;
-
-import com.android.gallery3d.app.GalleryActivity;
-import com.android.gallery3d.common.Utils;
-
-import java.util.WeakHashMap;
-
-public class PositionRepository {
-    private static final WeakHashMap<GalleryActivity, PositionRepository>
-            sMap = new WeakHashMap<GalleryActivity, PositionRepository>();
-
-    public static class Position implements Cloneable {
-        public float x;
-        public float y;
-        public float z;
-        public float theta;
-        public float alpha;
-
-        public Position() {
-        }
-
-        public Position(float x, float y, float z) {
-            this(x, y, z, 0f, 1f);
-        }
-
-        public Position(float x, float y, float z, float ftheta, float alpha) {
-            this.x = x;
-            this.y = y;
-            this.z = z;
-            this.theta = ftheta;
-            this.alpha = alpha;
-        }
-
-        @Override
-        public Position clone() {
-            try {
-                return (Position) super.clone();
-            } catch (CloneNotSupportedException e) {
-                throw new AssertionError(); // we do support clone.
-            }
-        }
-
-        public void set(Position another) {
-            x = another.x;
-            y = another.y;
-            z = another.z;
-            theta = another.theta;
-            alpha = another.alpha;
-        }
-
-        public void set(float x, float y, float z, float ftheta, float alpha) {
-            this.x = x;
-            this.y = y;
-            this.z = z;
-            this.theta = ftheta;
-            this.alpha = alpha;
-        }
-
-        @Override
-        public boolean equals(Object object) {
-            if (!(object instanceof Position)) return false;
-            Position position = (Position) object;
-            return x == position.x && y == position.y && z == position.z
-                    && theta == position.theta
-                    && alpha == position.alpha;
-        }
-
-        public static void interpolate(
-                Position source, Position target, Position output, float progress) {
-            if (progress < 1f) {
-                output.set(
-                        Utils.interpolateScale(source.x, target.x, progress),
-                        Utils.interpolateScale(source.y, target.y, progress),
-                        Utils.interpolateScale(source.z, target.z, progress),
-                        Utils.interpolateAngle(source.theta, target.theta, progress),
-                        Utils.interpolateScale(source.alpha, target.alpha, progress));
-            } else {
-                output.set(target);
-            }
-        }
-    }
-
-    public static PositionRepository getInstance(GalleryActivity activity) {
-        PositionRepository repository = sMap.get(activity);
-        if (repository == null) {
-            repository = new PositionRepository();
-            sMap.put(activity, repository);
-        }
-        return repository;
-    }
-
-    private SparseArray<Position> mData = new SparseArray<Position>();
-    private int mOffsetX;
-    private int mOffsetY;
-    private Position mTempPosition = new Position();
-
-    public Position get(int identity) {
-        Position position = mData.get(identity);
-        if (position == null) return null;
-        mTempPosition.set(position);
-        position = mTempPosition;
-        position.x -= mOffsetX;
-        position.y -= mOffsetY;
-        return position;
-    }
-
-    public void setOffset(int offsetX, int offsetY) {
-        mOffsetX = offsetX;
-        mOffsetY = offsetY;
-    }
-
-    public void putPosition(int identity, Position position) {
-        Position clone = position.clone();
-        clone.x += mOffsetX;
-        clone.y += mOffsetY;
-        mData.put(identity, clone);
-    }
-
-    public void clear() {
-        mData.clear();
-    }
-}
diff --git a/src/com/android/gallery3d/ui/RelativePosition.java b/src/com/android/gallery3d/ui/RelativePosition.java
index 98292e3..0f2bfd8 100644
--- a/src/com/android/gallery3d/ui/RelativePosition.java
+++ b/src/com/android/gallery3d/ui/RelativePosition.java
@@ -22,12 +22,12 @@
     private float mReferenceX;
     private float mReferenceY;
 
-    public void setAbsolute(int absoluteX, int absoluteY) {
+    public void setAbsolutePosition(int absoluteX, int absoluteY) {
         mAbsoluteX = absoluteX;
         mAbsoluteY = absoluteY;
     }
 
-    public void setReference(int x, int y) {
+    public void setReferencePosition(int x, int y) {
         mReferenceX = x;
         mReferenceY = y;
     }
diff --git a/src/com/android/gallery3d/ui/SlotView.java b/src/com/android/gallery3d/ui/SlotView.java
index 529a496..e12bb08 100644
--- a/src/com/android/gallery3d/ui/SlotView.java
+++ b/src/com/android/gallery3d/ui/SlotView.java
@@ -66,7 +66,7 @@
     private UserInteractionListener mUIListener;
 
     private boolean mMoreAnimation = false;
-    private MyAnimation mAnimation = null;
+    private SlotAnimation mAnimation = null;
     private final Layout mLayout = new Layout();
     private int mStartIndex = INDEX_NONE;
 
@@ -156,19 +156,25 @@
                 (mLayout.getVisibleStart() + mLayout.getVisibleEnd()) / 2;
         mLayout.setSize(r - l, b - t);
         makeSlotVisible(visibleIndex);
-
-        onLayoutChanged(r - l, b - t);
         if (mOverscrollEffect == OVERSCROLL_3D) {
             mPaper.setSize(r - l, b - t);
         }
     }
 
-    protected void onLayoutChanged(int width, int height) {
+    public void startScatteringAnimation(RelativePosition position) {
+        mAnimation = new ScatteringAnimation(position);
+        mAnimation.start();
+        if (mLayout.mSlotCount != 0) invalidate();
     }
 
-    // TODO: Fix this regression. Transition is disabled in this change
-    public void startTransition(PositionProvider position) {
-        mAnimation = new MyAnimation();
+    public void startRisingAnimation() {
+        mAnimation = new RisingAnimation();
+        mAnimation.start();
+        if (mLayout.mSlotCount != 0) invalidate();
+    }
+
+    public void startRestoringAnimation(int targetIndex) {
+        mAnimation = new RestoringAnimation(targetIndex);
         mAnimation.start();
         if (mLayout.mSlotCount != 0) invalidate();
     }
@@ -261,10 +267,8 @@
 
         more |= paperActive;
 
-        float interpolate = 1f;
         if (mAnimation != null) {
             more |= mAnimation.calculate(animTime);
-            interpolate = mAnimation.value;
         }
 
         canvas.translate(-mScrollX, -mScrollY);
@@ -273,8 +277,8 @@
         int requestedSlot[] = expandIntArray(mRequestRenderSlots,
                 mLayout.mVisibleEnd - mLayout.mVisibleStart);
 
-        for (int i = mLayout.mVisibleStart; i < mLayout.mVisibleEnd; ++i) {
-            int r = renderItem(canvas, i, 0, interpolate, paperActive);
+        for (int i = mLayout.mVisibleEnd - 1; i >= mLayout.mVisibleStart; --i) {
+            int r = renderItem(canvas, i, 0, paperActive);
             if ((r & RENDER_MORE_FRAME) != 0) more = true;
             if ((r & RENDER_MORE_PASS) != 0) requestedSlot[requestCount++] = i;
         }
@@ -283,7 +287,7 @@
             int newCount = 0;
             for (int i = 0; i < requestCount; ++i) {
                 int r = renderItem(canvas,
-                        requestedSlot[i], pass, interpolate, paperActive);
+                        requestedSlot[i], pass, paperActive);
                 if ((r & RENDER_MORE_FRAME) != 0) more = true;
                 if ((r & RENDER_MORE_PASS) != 0) requestedSlot[newCount++] = i;
             }
@@ -306,8 +310,8 @@
         mMoreAnimation = more;
     }
 
-    private int renderItem(GLCanvas canvas,
-            int index, int pass, float interpolate, boolean paperActive) {
+    private int renderItem(
+            GLCanvas canvas, int index, int pass, boolean paperActive) {
         canvas.save(GLCanvas.SAVE_FLAG_ALPHA | GLCanvas.SAVE_FLAG_MATRIX);
         Rect rect = getSlotRect(index);
         if (paperActive) {
@@ -315,23 +319,71 @@
         } else {
             canvas.translate(rect.left, rect.top, 0);
         }
+        if (mAnimation != null && mAnimation.isActive()) {
+            mAnimation.apply(canvas, index, rect);
+        }
         int result = mRenderer.renderSlot(
                 canvas, index, pass, rect.right - rect.left, rect.bottom - rect.top);
         canvas.restore();
         return result;
     }
 
-    public static class MyAnimation extends Animation {
-        public float value;
+    public static abstract class SlotAnimation extends Animation {
+        protected float mProgress = 0;
 
-        public MyAnimation() {
+        public SlotAnimation() {
             setInterpolator(new DecelerateInterpolator(4));
             setDuration(1500);
         }
 
         @Override
         protected void onCalculate(float progress) {
-            value = progress;
+            mProgress = progress;
+        }
+
+        abstract public void apply(GLCanvas canvas, int slotIndex, Rect target);
+    }
+
+    public static class RisingAnimation extends SlotAnimation {
+        private static final int RISING_DISTANCE = 128;
+
+        @Override
+        public void apply(GLCanvas canvas, int slotIndex, Rect target) {
+            canvas.translate(0, 0, RISING_DISTANCE * (1 - mProgress));
+        }
+    }
+
+    public static class ScatteringAnimation extends SlotAnimation {
+        private int PHOTO_DISTANCE = 1000;
+        private RelativePosition mCenter;
+
+        public ScatteringAnimation(RelativePosition center) {
+            mCenter = center;
+        }
+
+        @Override
+        public void apply(GLCanvas canvas, int slotIndex, Rect target) {
+            canvas.translate(
+                    (mCenter.getX() - target.centerX()) * (1 - mProgress),
+                    (mCenter.getY() - target.centerY()) * (1 - mProgress),
+                    slotIndex * PHOTO_DISTANCE * (1 - mProgress));
+            canvas.setAlpha(mProgress);
+        }
+    }
+
+    public static class RestoringAnimation extends SlotAnimation {
+        private static final int DISTANCE = 1000;
+        private int mTargetIndex;
+
+        public RestoringAnimation(int targetIndex) {
+            mTargetIndex = targetIndex;
+        }
+
+        @Override
+        public void apply(GLCanvas canvas, int slotIndex, Rect target) {
+            if (slotIndex == mTargetIndex) {
+                canvas.translate(0, 0, -DISTANCE * (1 - mProgress));
+            }
         }
     }
 
diff --git a/tests/src/com/android/gallery3d/data/GalleryAppStub.java b/tests/src/com/android/gallery3d/data/GalleryAppStub.java
index 36075f4..47693d2 100644
--- a/tests/src/com/android/gallery3d/data/GalleryAppStub.java
+++ b/tests/src/com/android/gallery3d/data/GalleryAppStub.java
@@ -19,7 +19,6 @@
 import com.android.gallery3d.app.GalleryApp;
 import com.android.gallery3d.app.StateManager;
 import com.android.gallery3d.ui.GLRoot;
-import com.android.gallery3d.ui.PositionRepository;
 import com.android.gallery3d.util.ThreadPool;
 
 import android.content.ContentResolver;
@@ -35,7 +34,6 @@
     public DecodeUtils getDecodeService() { return null; }
 
     public GLRoot getGLRoot() { return null; }
-    public PositionRepository getPositionRepository() { return null; }
 
     public Context getAndroidContext() { return null; }