Merge "Fixing drag outline not getting drawn when the shortcuts popup is shown." into ub-launcher3-dorval-polish
diff --git a/res/xml/launcher_preferences.xml b/res/xml/launcher_preferences.xml
index 939fece..c76f118 100644
--- a/res/xml/launcher_preferences.xml
+++ b/res/xml/launcher_preferences.xml
@@ -16,23 +16,6 @@
 
 <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
 
-    <SwitchPreference
-        android:key="pref_add_icon_to_home"
-        android:title="@string/auto_add_shortcuts_label"
-        android:summary="@string/auto_add_shortcuts_description"
-        android:defaultValue="true"
-        android:persistent="true"
-        />
-
-    <ListPreference
-        android:key="pref_override_icon_shape"
-        android:title="@string/icon_shape_override_label"
-        android:summary="%s"
-        android:entries="@array/icon_shape_override_paths_names"
-        android:entryValues="@array/icon_shape_override_paths_values"
-        android:defaultValue=""
-        android:persistent="false" />
-
     <Preference
         android:key="pref_icon_badging"
         android:title="@string/icon_badging_title"
@@ -46,10 +29,27 @@
     </Preference>
 
     <SwitchPreference
+        android:key="pref_add_icon_to_home"
+        android:title="@string/auto_add_shortcuts_label"
+        android:summary="@string/auto_add_shortcuts_description"
+        android:defaultValue="true"
+        android:persistent="true"
+        />
+
+    <SwitchPreference
         android:key="pref_allowRotation"
         android:title="@string/allow_rotation_title"
         android:defaultValue="@bool/allow_rotation"
         android:persistent="true"
         />
 
+    <ListPreference
+        android:key="pref_override_icon_shape"
+        android:title="@string/icon_shape_override_label"
+        android:summary="%s"
+        android:entries="@array/icon_shape_override_paths_names"
+        android:entryValues="@array/icon_shape_override_paths_values"
+        android:defaultValue=""
+        android:persistent="false" />
+
 </PreferenceScreen>
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 12870a1..26c5c9d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -215,9 +215,9 @@
     private static final int ACTIVITY_START_DELAY = 1000;
 
     // How long to wait before the new-shortcut animation automatically pans the workspace
-    private static int NEW_APPS_PAGE_MOVE_DELAY = 500;
-    private static int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
-    @Thunk static int NEW_APPS_ANIMATION_DELAY = 500;
+    private static final int NEW_APPS_PAGE_MOVE_DELAY = 500;
+    private static final int NEW_APPS_ANIMATION_INACTIVE_TIMEOUT_SECONDS = 5;
+    @Thunk static final int NEW_APPS_ANIMATION_DELAY = 500;
 
     private final ExtractedColors mExtractedColors = new ExtractedColors();
 
@@ -231,7 +231,7 @@
     private AppWidgetManagerCompat mAppWidgetManager;
     private LauncherAppWidgetHost mAppWidgetHost;
 
-    private int[] mTmpAddItemCellCoordinates = new int[2];
+    private final int[] mTmpAddItemCellCoordinates = new int[2];
 
     @Thunk Hotseat mHotseat;
     private ViewGroup mOverviewPanel;
@@ -261,15 +261,15 @@
     private boolean mPaused = true;
     private boolean mOnResumeNeedsLoad;
 
-    private ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<Runnable>();
-    private ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<Runnable>();
+    private final ArrayList<Runnable> mBindOnResumeCallbacks = new ArrayList<>();
+    private final ArrayList<Runnable> mOnResumeCallbacks = new ArrayList<>();
     private ViewOnDrawExecutor mPendingExecutor;
 
     private LauncherModel mModel;
     private ModelWriter mModelWriter;
     private IconCache mIconCache;
     private LauncherAccessibilityDelegate mAccessibilityDelegate;
-    private Handler mHandler = new Handler();
+    private final Handler mHandler = new Handler();
     private boolean mIsResumeFromActionScreenOff;
     private boolean mHasFocus = false;
 
@@ -283,7 +283,7 @@
     // match the sensor state.
     private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
 
-    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<Integer>();
+    private final ArrayList<Integer> mSynchronouslyBoundPages = new ArrayList<>();
 
     // We only want to get the SharedPreferences once since it does an FS stat each time we get
     // it from the context.
@@ -296,8 +296,8 @@
     // the press state and keep this reference to reset the press state when we return to launcher.
     private BubbleTextView mWaitingForResume;
 
-    protected static HashMap<String, CustomAppWidget> sCustomAppWidgets =
-            new HashMap<String, CustomAppWidget>();
+    protected static final HashMap<String, CustomAppWidget> sCustomAppWidgets =
+        new HashMap<>();
 
     static {
         if (TestingUtils.ENABLE_CUSTOM_WIDGET_TEST) {
@@ -310,7 +310,7 @@
     // simply unregister this runnable.
     private Runnable mExitSpringLoadedModeRunnable;
 
-    @Thunk Runnable mBuildLayersRunnable = new Runnable() {
+    @Thunk final Runnable mBuildLayersRunnable = new Runnable() {
         public void run() {
             if (mWorkspace != null) {
                 mWorkspace.buildPageHardwareLayers();
@@ -1062,13 +1062,13 @@
     public interface CustomContentCallbacks {
         // Custom content is completely shown. {@code fromResume} indicates whether this was caused
         // by a onResume or by scrolling otherwise.
-        public void onShow(boolean fromResume);
+        void onShow(boolean fromResume);
 
         // Custom content is completely hidden
-        public void onHide();
+        void onHide();
 
         // Custom content scroll progress changed. From 0 (not showing) to 1 (fully showing).
-        public void onScrollProgressChanged(float progress);
+        void onScrollProgressChanged(float progress);
 
         // Indicates whether the user is allowed to scroll away from the custom content.
         boolean isScrollingAllowed();
@@ -1079,28 +1079,28 @@
         /**
          * Touch interaction leading to overscroll has begun
          */
-        public void onScrollInteractionBegin();
+        void onScrollInteractionBegin();
 
         /**
          * Touch interaction related to overscroll has ended
          */
-        public void onScrollInteractionEnd();
+        void onScrollInteractionEnd();
 
         /**
          * Scroll progress, between 0 and 100, when the user scrolls beyond the leftmost
          * screen (or in the case of RTL, the rightmost screen).
          */
-        public void onScrollChange(float progress, boolean rtl);
+        void onScrollChange(float progress, boolean rtl);
 
         /**
          * Called when the launcher is ready to use the overlay
          * @param callbacks A set of callbacks provided by Launcher in relation to the overlay
          */
-        public void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
+        void setOverlayCallbacks(LauncherOverlayCallbacks callbacks);
     }
 
     public interface LauncherOverlayCallbacks {
-        public void onScrollChanged(float progress);
+        void onScrollChanged(float progress);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
@@ -1284,7 +1284,7 @@
         mDragController.addDragListener(mWorkspace);
 
         // Get the search/delete/uninstall bar
-        mDropTargetBar = (DropTargetBar) mDragLayer.findViewById(R.id.drop_target_bar);
+        mDropTargetBar = mDragLayer.findViewById(R.id.drop_target_bar);
 
         // Setup Apps and Widgets
         mAppsView = (AllAppsContainerView) findViewById(R.id.apps_view);
@@ -1606,7 +1606,6 @@
                                 }
                             }
                         });
-                        return;
                     }
                 });
             }
@@ -1743,8 +1742,9 @@
         // as slow logic in the callbacks eat into the time the scroller expects for the snapToPage
         // animation.
         if (isActionMain) {
-            boolean callbackAllowsMoveToDefaultScreen = mLauncherCallbacks != null ?
-                    mLauncherCallbacks.shouldMoveToDefaultScreenOnHomeIntent() : true;
+            boolean callbackAllowsMoveToDefaultScreen =
+                mLauncherCallbacks == null || mLauncherCallbacks
+                    .shouldMoveToDefaultScreenOnHomeIntent();
             if (shouldMoveToDefaultScreen && !mWorkspace.isTouchActive()
                     && callbackAllowsMoveToDefaultScreen) {
 
@@ -3195,7 +3195,7 @@
                 orderedScreenIds.indexOf(Workspace.FIRST_SCREEN_ID) != 0) {
             orderedScreenIds.remove(Workspace.FIRST_SCREEN_ID);
             orderedScreenIds.add(0, Workspace.FIRST_SCREEN_ID);
-            mModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
+            LauncherModel.updateWorkspaceScreenOrder(this, orderedScreenIds);
         } else if (!FeatureFlags.QSB_ON_FIRST_SCREEN && orderedScreenIds.isEmpty()) {
             // If there are no screens, we need to have an empty screen
             mWorkspace.addExtraEmptyScreen();
@@ -3282,7 +3282,7 @@
 
         // Get the list of added items and intersect them with the set of items here
         final AnimatorSet anim = LauncherAnimUtils.createAnimatorSet();
-        final Collection<Animator> bounceAnims = new ArrayList<Animator>();
+        final Collection<Animator> bounceAnims = new ArrayList<>();
         final boolean animateIcons = forceAnimateIcons && canRunNewAppsAnimation();
         Workspace workspace = mWorkspace;
         long newItemsScreenId = -1;
@@ -3666,7 +3666,7 @@
      * multiple calls to bind the same list.)
      */
     @Thunk ArrayList<AppInfo> mTmpAppsList;
-    private Runnable mBindAllApplicationsRunnable = new Runnable() {
+    private final Runnable mBindAllApplicationsRunnable = new Runnable() {
         public void run() {
             bindAllApplications(mTmpAppsList);
             mTmpAppsList = null;
@@ -3870,7 +3870,7 @@
         }
     }
 
-    private Runnable mBindAllWidgetsRunnable = new Runnable() {
+    private final Runnable mBindAllWidgetsRunnable = new Runnable() {
             public void run() {
                 bindAllWidgets(mAllWidgets);
             }
@@ -3941,7 +3941,7 @@
     }
 
     private boolean shouldShowDiscoveryBounce() {
-        if (mState != mState.WORKSPACE) {
+        if (mState != State.WORKSPACE) {
             return false;
         }
         if (mLauncherCallbacks != null && mLauncherCallbacks.shouldShowDiscoveryBounce()) {
@@ -3950,10 +3950,7 @@
         if (!mIsResumeFromActionScreenOff) {
             return false;
         }
-        if (mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false)) {
-            return false;
-        }
-        return true;
+        return !mSharedPrefs.getBoolean(APPS_VIEW_SHOWN, false);
     }
 
     protected void moveWorkspaceToDefaultScreen() {
diff --git a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
index 8e5452e..3f06ec9 100644
--- a/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
+++ b/src/com/android/launcher3/allapps/search/AppsSearchContainerLayout.java
@@ -23,13 +23,11 @@
 import android.text.Spannable;
 import android.text.SpannableString;
 import android.text.SpannableStringBuilder;
-import android.text.TextUtils;
 import android.text.method.TextKeyListener;
 import android.util.AttributeSet;
 import android.view.KeyEvent;
 import android.view.View;
 import android.widget.FrameLayout;
-
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.ExtendedEditText;
 import com.android.launcher3.Launcher;
@@ -43,7 +41,6 @@
 import com.android.launcher3.discovery.AppDiscoveryUpdateState;
 import com.android.launcher3.graphics.TintedDrawableSpan;
 import com.android.launcher3.util.ComponentKey;
-
 import java.util.ArrayList;
 
 /**
diff --git a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
index 33bf275..ff357c0 100644
--- a/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/ClippedFolderIconLayoutRule.java
@@ -29,8 +29,8 @@
     }
 
     @Override
-    public FolderIcon.PreviewItemDrawingParams computePreviewItemDrawingParams(int index,
-            int curNumItems, FolderIcon.PreviewItemDrawingParams params) {
+    public PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+            PreviewItemDrawingParams params) {
 
         float totalScale = scaleForItem(index, curNumItems);
         float transX;
@@ -47,7 +47,7 @@
         }
 
         if (params == null) {
-            params = new FolderIcon.PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
+            params = new PreviewItemDrawingParams(transX, transY, totalScale, overlayAlpha);
         } else {
             params.update(transX, transY, totalScale);
             params.overlayAlpha = overlayAlpha;
diff --git a/src/com/android/launcher3/folder/FolderAnimationManager.java b/src/com/android/launcher3/folder/FolderAnimationManager.java
index 7e6205a..3648c60 100644
--- a/src/com/android/launcher3/folder/FolderAnimationManager.java
+++ b/src/com/android/launcher3/folder/FolderAnimationManager.java
@@ -71,8 +71,7 @@
     private final TimeInterpolator mFolderInterpolator;
     private final TimeInterpolator mLargeFolderPreviewItemInterpolator;
 
-    private final FolderIcon.PreviewItemDrawingParams mTmpParams =
-            new FolderIcon.PreviewItemDrawingParams(0, 0, 0, 0);
+    private final PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
 
     private static final Property<View, Float> SCALE_PROPERTY =
             new Property<View, Float>(Float.class, "scale") {
@@ -202,20 +201,20 @@
         play(a, new RoundedRectRevealOutlineProvider(initialRadius, finalRadius, startRect,
                 endRect).createRevealAnimator(mFolder, !mIsOpening));
 
+        // Animate the elevation midway so that the shadow is not noticeable in the background.
+        int midDuration = mDuration / 2;
+        Animator z = getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0);
+        play(a, z, midDuration, midDuration);
+
         a.addListener(new AnimatorListenerAdapter() {
             @Override
             public void onAnimationEnd(Animator animation) {
                 super.onAnimationEnd(animation);
                 mFolder.setTranslationX(0.0f);
                 mFolder.setTranslationY(0.0f);
+                mFolder.setTranslationZ(0.0f);
                 mFolder.setScaleX(1f);
                 mFolder.setScaleY(1f);
-
-                if (mIsOpening) {
-                    getAnimator(mFolder, View.TRANSLATION_Z, -mFolder.getElevation(), 0)
-                            .setDuration(150)
-                            .start();
-                }
             }
         });
 
@@ -324,7 +323,12 @@
     }
 
     private void play(AnimatorSet as, Animator a) {
-        a.setDuration(mDuration);
+        play(as, a, a.getStartDelay(), mDuration);
+    }
+
+    private void play(AnimatorSet as, Animator a, long startDelay, int duration) {
+        a.setStartDelay(startDelay);
+        a.setDuration(duration);
         as.play(a);
     }
 
@@ -344,12 +348,6 @@
                 : ObjectAnimator.ofFloat(view, property, v2, v1);
     }
 
-    private Animator getAnimator(List<BubbleTextView> items, Property property, int v1, int v2) {
-        return mIsOpening
-                ? ObjectAnimator.ofArgb(items, property, v1, v2)
-                : ObjectAnimator.ofArgb(items, property, v2, v1);
-    }
-
     private Animator getAnimator(GradientDrawable drawable, String property, int v1, int v2) {
         return mIsOpening
                 ? ObjectAnimator.ofArgb(drawable, property, v1, v2)
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 215a31c..1cc285e 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -19,8 +19,6 @@
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
 import android.animation.ObjectAnimator;
-import android.animation.ValueAnimator;
-import android.animation.ValueAnimator.AnimatorUpdateListener;
 import android.content.Context;
 import android.graphics.Canvas;
 import android.graphics.Point;
@@ -227,8 +225,7 @@
     }
 
     public boolean acceptDrop(ItemInfo dragInfo) {
-        final ItemInfo item = dragInfo;
-        return !mFolder.isDestroyed() && willAcceptItem(item);
+        return !mFolder.isDestroyed() && willAcceptItem(dragInfo);
     }
 
     public void addItem(ShortcutInfo item) {
@@ -423,39 +420,6 @@
         return mBadgeInfo != null && mBadgeInfo.hasBadge();
     }
 
-    static class PreviewItemDrawingParams {
-        PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-            this.overlayAlpha = overlayAlpha;
-        }
-
-        public void update(float transX, float transY, float scale) {
-            // We ensure the update will not interfere with an animation on the layout params
-            // If the final values differ, we cancel the animation.
-            if (anim != null) {
-                if (anim.finalTransX == transX || anim.finalTransY == transY
-                        || anim.finalScale == scale) {
-                    return;
-                }
-                anim.cancel();
-            }
-
-            this.transX = transX;
-            this.transY = transY;
-            this.scale = scale;
-        }
-
-        float transX;
-        float transY;
-        float scale;
-        public float overlayAlpha;
-        boolean hidden;
-        FolderPreviewItemAnim anim;
-        Drawable drawable;
-    }
-
     private float getLocalCenterForIndex(int index, int curNumItems, int[] center) {
         mTmpParams = computePreviewItemDrawingParams(
                 Math.min(mPreviewLayoutRule.maxNumItems(), index), curNumItems, mTmpParams);
@@ -465,12 +429,12 @@
         float offsetX = mTmpParams.transX + (mTmpParams.scale * mIntrinsicIconSize) / 2;
         float offsetY = mTmpParams.transY + (mTmpParams.scale * mIntrinsicIconSize) / 2;
 
-        center[0] = (int) Math.round(offsetX);
-        center[1] = (int) Math.round(offsetY);
+        center[0] = Math.round(offsetX);
+        center[1] = Math.round(offsetY);
         return mTmpParams.scale;
     }
 
-    private PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
+    PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params) {
         // We use an index of -1 to represent an icon on the workspace for the destroy and
         // create animations
@@ -582,89 +546,14 @@
         }
     }
 
-    class FolderPreviewItemAnim {
-        ValueAnimator mValueAnimator;
-        float finalScale;
-        float finalTransX;
-        float finalTransY;
-
-        /**
-         *
-         * @param params layout params to animate
-         * @param index0 original index of the item to be animated
-         * @param nItems0 original number of items in the preview
-         * @param index1 new index of the item to be animated
-         * @param nItems1 new number of items in the preview
-         * @param duration duration in ms of the animation
-         * @param onCompleteRunnable runnable to execute upon animation completion
-         */
-        public FolderPreviewItemAnim(final PreviewItemDrawingParams params, int index0, int nItems0,
-                int index1, int nItems1, int duration, final Runnable onCompleteRunnable) {
-
-            computePreviewItemDrawingParams(index1, nItems1, mTmpParams);
-
-            finalScale = mTmpParams.scale;
-            finalTransX = mTmpParams.transX;
-            finalTransY = mTmpParams.transY;
-
-            computePreviewItemDrawingParams(index0, nItems0, mTmpParams);
-
-            final float scale0 = mTmpParams.scale;
-            final float transX0 = mTmpParams.transX;
-            final float transY0 = mTmpParams.transY;
-
-            mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
-            mValueAnimator.addUpdateListener(new AnimatorUpdateListener(){
-                public void onAnimationUpdate(ValueAnimator animation) {
-                    float progress = animation.getAnimatedFraction();
-
-                    params.transX = transX0 + progress * (finalTransX - transX0);
-                    params.transY = transY0 + progress * (finalTransY - transY0);
-                    params.scale = scale0 + progress * (finalScale - scale0);
-                    invalidate();
-                }
-            });
-
-            mValueAnimator.addListener(new AnimatorListenerAdapter() {
-                @Override
-                public void onAnimationStart(Animator animation) {
-                }
-
-                @Override
-                public void onAnimationEnd(Animator animation) {
-                    if (onCompleteRunnable != null) {
-                        onCompleteRunnable.run();
-                    }
-                    params.anim = null;
-                }
-            });
-            mValueAnimator.setDuration(duration);
-        }
-
-        public void start() {
-            mValueAnimator.start();
-        }
-
-        public void cancel() {
-            mValueAnimator.cancel();
-        }
-
-        public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
-            return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
-                    finalScale == anim.finalScale;
-
-        }
-    }
-
     private void animateFirstItem(final Drawable d, int duration, final boolean reverse,
             final Runnable onCompleteRunnable) {
-
         FolderPreviewItemAnim anim;
         if (!reverse) {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), -1, -1, 0, 2, duration,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), -1, -1, 0, 2, duration,
                     onCompleteRunnable);
         } else {
-            anim = new FolderPreviewItemAnim(mDrawingParams.get(0), 0, 2, -1, -1, duration,
+            anim = new FolderPreviewItemAnim(this, mDrawingParams.get(0), 0, 2, -1, -1, duration,
                     onCompleteRunnable);
         }
         anim.start();
@@ -740,7 +629,7 @@
                     mReferenceDrawable = p.drawable;
                 }
             } else {
-                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(p, i, prevNumItems, i,
+                FolderPreviewItemAnim anim = new FolderPreviewItemAnim(this, p, i, prevNumItems, i,
                         nItemsInPreview, DROP_IN_ANIMATION_DURATION, null);
 
                 if (p.anim != null) {
@@ -901,7 +790,7 @@
         }
     }
 
-    public interface PreviewLayoutRule {
+    interface PreviewLayoutRule {
         PreviewItemDrawingParams computePreviewItemDrawingParams(int index, int curNumItems,
             PreviewItemDrawingParams params);
         void init(int availableSpace, float intrinsicIconSize, boolean rtl);
diff --git a/src/com/android/launcher3/folder/FolderPreviewItemAnim.java b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
new file mode 100644
index 0000000..0da7c5c
--- /dev/null
+++ b/src/com/android/launcher3/folder/FolderPreviewItemAnim.java
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2017 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.launcher3.folder;
+
+import android.animation.Animator;
+import android.animation.AnimatorListenerAdapter;
+import android.animation.ValueAnimator;
+
+import com.android.launcher3.LauncherAnimUtils;
+
+/**
+ * Animates a Folder preview item.
+ */
+class FolderPreviewItemAnim {
+    private ValueAnimator mValueAnimator;
+
+    float finalScale;
+    float finalTransX;
+    float finalTransY;
+
+    private PreviewItemDrawingParams mTmpParams = new PreviewItemDrawingParams(0, 0, 0, 0);
+
+    /**
+     * @param folderIcon The FolderIcon this preview will be drawn in.
+     * @param params layout params to animate
+     * @param index0 original index of the item to be animated
+     * @param items0 original number of items in the preview
+     * @param index1 new index of the item to be animated
+     * @param items1 new number of items in the preview
+     * @param duration duration in ms of the animation
+     * @param onCompleteRunnable runnable to execute upon animation completion
+     */
+    FolderPreviewItemAnim(final FolderIcon folderIcon, final PreviewItemDrawingParams params,
+            int index0, int items0, int index1, int items1, int duration,
+            final Runnable onCompleteRunnable) {
+        folderIcon.computePreviewItemDrawingParams(index1, items1, mTmpParams);
+
+        finalScale = mTmpParams.scale;
+        finalTransX = mTmpParams.transX;
+        finalTransY = mTmpParams.transY;
+
+        folderIcon.computePreviewItemDrawingParams(index0, items0, mTmpParams);
+
+        final float scale0 = mTmpParams.scale;
+        final float transX0 = mTmpParams.transX;
+        final float transY0 = mTmpParams.transY;
+
+        mValueAnimator = LauncherAnimUtils.ofFloat(0f, 1.0f);
+        mValueAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener(){
+            public void onAnimationUpdate(ValueAnimator animation) {
+                float progress = animation.getAnimatedFraction();
+
+                params.transX = transX0 + progress * (finalTransX - transX0);
+                params.transY = transY0 + progress * (finalTransY - transY0);
+                params.scale = scale0 + progress * (finalScale - scale0);
+                folderIcon.invalidate();
+            }
+        });
+        mValueAnimator.addListener(new AnimatorListenerAdapter() {
+            @Override
+            public void onAnimationEnd(Animator animation) {
+                if (onCompleteRunnable != null) {
+                    onCompleteRunnable.run();
+                }
+                params.anim = null;
+            }
+        });
+        mValueAnimator.setDuration(duration);
+    }
+
+    public void start() {
+        mValueAnimator.start();
+    }
+
+    public void cancel() {
+        mValueAnimator.cancel();
+    }
+
+    public boolean hasEqualFinalState(FolderPreviewItemAnim anim) {
+        return finalTransY == anim.finalTransY && finalTransX == anim.finalTransX &&
+                finalScale == anim.finalScale;
+
+    }
+}
diff --git a/src/com/android/launcher3/folder/PreviewItemDrawingParams.java b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
new file mode 100644
index 0000000..607b7ca
--- /dev/null
+++ b/src/com/android/launcher3/folder/PreviewItemDrawingParams.java
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2017 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.launcher3.folder;
+
+import android.graphics.drawable.Drawable;
+
+/**
+ * Manages the parameters used to draw a Folder preview item.
+ */
+class PreviewItemDrawingParams {
+    float transX;
+    float transY;
+    float scale;
+    float overlayAlpha;
+    FolderPreviewItemAnim anim;
+    public boolean hidden;
+    Drawable drawable;
+
+    PreviewItemDrawingParams(float transX, float transY, float scale, float overlayAlpha) {
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+        this.overlayAlpha = overlayAlpha;
+    }
+
+    public void update(float transX, float transY, float scale) {
+        // We ensure the update will not interfere with an animation on the layout params
+        // If the final values differ, we cancel the animation.
+        if (anim != null) {
+            if (anim.finalTransX == transX || anim.finalTransY == transY
+                    || anim.finalScale == scale) {
+                return;
+            }
+            anim.cancel();
+        }
+
+        this.transX = transX;
+        this.transY = transY;
+        this.scale = scale;
+    }
+}
diff --git a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
index 1ece278..138dc1c 100644
--- a/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
+++ b/src/com/android/launcher3/folder/StackFolderIconLayoutRule.java
@@ -16,8 +16,6 @@
 
 package com.android.launcher3.folder;
 
-import com.android.launcher3.folder.FolderIcon.PreviewItemDrawingParams;
-
 public class StackFolderIconLayoutRule implements FolderIcon.PreviewLayoutRule {
 
     static final int MAX_NUM_ITEMS_IN_PREVIEW = 3;