Moving some state specific logic to subclass of LauncherState

Bug: 67678570
Change-Id: I1316f91c9f19bd572e4a0da67a22fa8921e1dcf9
diff --git a/res/values/config.xml b/res/values/config.xml
index c0bf360..7a33ae6 100644
--- a/res/values/config.xml
+++ b/res/values/config.xml
@@ -50,8 +50,6 @@
 
     <!-- Out of 100, the percent to shrink the workspace during spring loaded mode. -->
     <integer name="config_workspaceSpringLoadShrinkPercentage">90</integer>
-    <!-- Out of 100, the percent to shrink the workspace during overview mode. -->
-    <integer name="config_workspaceOverviewShrinkPercentage">70</integer>
 
     <!-- This constant stores the ratio of the all apps button drawable which
          is used for internal (baked-in) padding -->
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 8f7e882..8aaad13 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -570,7 +570,7 @@
         }
     }
 
-    int getOverviewModeButtonBarHeight() {
+    public int getOverviewModeButtonBarHeight() {
         int zoneHeight = (int) (overviewModeIconZoneRatio * availableHeightPx);
         return Utilities.boundToRange(zoneHeight,
                 overviewModeMinIconZoneHeightPx,
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 46eb263..b06081b 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_NOSENSOR;
+import static android.content.pm.ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED;
+
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_NEXT_FRAME;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.logging.LoggerUtils.newContainerTarget;
@@ -47,7 +50,6 @@
 import android.content.IntentSender;
 import android.content.SharedPreferences;
 import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
-import android.content.pm.ActivityInfo;
 import android.content.pm.PackageManager;
 import android.database.sqlite.SQLiteDatabase;
 import android.graphics.Point;
@@ -264,10 +266,6 @@
 
     private PopupDataProvider mPopupDataProvider;
 
-    // Determines how long to wait after a rotation before restoring the screen orientation to
-    // match the sensor state.
-    private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
-
     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
@@ -291,18 +289,8 @@
 
     public ViewGroupFocusHelper mFocusHandler;
     private boolean mRotationEnabled = false;
-
     private boolean mAppLaunchSuccess;
 
-    @Thunk void setOrientation() {
-        if (mRotationEnabled) {
-            unlockScreenOrientation(true);
-        } else {
-            setRequestedOrientation(
-                    ActivityInfo.SCREEN_ORIENTATION_NOSENSOR);
-        }
-    }
-
     private RotationPrefChangeHandler mRotationPrefChangeHandler;
 
     @Override
@@ -416,7 +404,8 @@
 
         // On large interfaces, or on devices that a user has specifically enabled screen rotation,
         // we want the screen to auto-rotate based on the current orientation
-        setOrientation();
+        setRequestedOrientation(mRotationEnabled
+                ? SCREEN_ORIENTATION_UNSPECIFIED : SCREEN_ORIENTATION_NOSENSOR);
 
         setContentView(mLauncherView);
 
@@ -1896,11 +1885,8 @@
         AbstractFloatingView topView = AbstractFloatingView.getTopOpenView(this);
         if (topView != null) {
             topView.onBackPressed();
-        } else if (isInState(LauncherState.ALL_APPS)) {
-            ued.logActionCommand(Action.Command.BACK, ContainerType.ALLAPPS);
-            showWorkspace(true);
-        } else if (isInState(LauncherState.OVERVIEW)) {
-            ued.logActionCommand(Action.Command.BACK, ContainerType.OVERVIEW);
+        } else if (!isInState(LauncherState.NORMAL)) {
+            ued.logActionCommand(Action.Command.BACK, mWorkspace.getState().containerType);
             showWorkspace(true);
         } else {
             // Back button is a no-op here, but give at least some feedback for the button press
@@ -2381,7 +2367,7 @@
         if (!mDragController.isDragging()) {
             if (itemUnderLongClick == null) {
                 // User long pressed on empty space
-                if (isInState(LauncherState.OVERVIEW)) {
+                if (mWorkspace.isPageRearrangeEnabled()) {
                     mWorkspace.startReordering(v);
                     getUserEventDispatcher().logActionOnContainer(Action.Touch.LONGPRESS,
                             Action.Direction.NONE, ContainerType.OVERVIEW);
@@ -2544,15 +2530,6 @@
             return;
         }
 
-        // Lock the orientation:
-        if (mRotationEnabled) {
-            setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
-        }
-
-        // Prevent any Un/InstallShortcutReceivers from updating the db while we are
-        // in spring loaded mode
-        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
-
         mStateTransitionAnimation.startAnimationToWorkspace(
                 LauncherState.SPRING_LOADED, true /* animated */,
                 null /* onCompleteRunnable */);
@@ -2565,13 +2542,6 @@
     public void exitSpringLoadedDragMode(int delay, final Runnable onCompleteRunnable) {
         if (!isInState(LauncherState.SPRING_LOADED)) return;
 
-        // Unlock rotation lock
-        unlockScreenOrientation(false);
-
-        // Re-enable any Un/InstallShortcutReceiver and now process any queued items
-        InstallShortcutReceiver.disableAndFlushInstallQueue(
-                InstallShortcutReceiver.FLAG_DRAG_AND_DROP, this);
-
         if (mExitSpringLoadedModeRunnable != null) {
             mHandler.removeCallbacks(mExitSpringLoadedModeRunnable);
         }
@@ -3333,18 +3303,8 @@
         mModel.refreshAndBindWidgetsAndShortcuts(packageUser);
     }
 
-    public void unlockScreenOrientation(boolean immediate) {
-        if (mRotationEnabled) {
-            if (immediate) {
-                setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-            } else {
-                mHandler.postDelayed(new Runnable() {
-                    public void run() {
-                        setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
-                    }
-                }, RESTORE_SCREEN_ORIENTATION_DELAY);
-            }
-        }
+    public boolean isRotationEnabled () {
+        return mRotationEnabled;
     }
 
     private void markAppsViewShown() {
diff --git a/src/com/android/launcher3/LauncherState.java b/src/com/android/launcher3/LauncherState.java
index 4619f4e..0ac27e5 100644
--- a/src/com/android/launcher3/LauncherState.java
+++ b/src/com/android/launcher3/LauncherState.java
@@ -19,9 +19,9 @@
 import static android.view.View.IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS;
 
 import static com.android.launcher3.LauncherAnimUtils.ALL_APPS_TRANSITION_MS;
-import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
-import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
 
+import com.android.launcher3.states.OverviewState;
+import com.android.launcher3.states.SpringLoadedState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 
 import java.util.Arrays;
@@ -40,19 +40,15 @@
 
     private static final LauncherState[] sAllStates = new LauncherState[4];
 
-    public static LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
+    public static final LauncherState NORMAL = new LauncherState(0, ContainerType.WORKSPACE,
             0, FLAG_DO_NOT_RESTORE);
 
-    public static LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS,
+    public static final LauncherState ALL_APPS = new LauncherState(1, ContainerType.ALLAPPS,
             ALL_APPS_TRANSITION_MS, FLAG_DISABLE_ACCESSIBILITY);
 
-    public static LauncherState SPRING_LOADED = new LauncherState(2, ContainerType.WORKSPACE,
-            SPRING_LOADED_TRANSITION_MS,
-            FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE);
+    public static final LauncherState SPRING_LOADED = new SpringLoadedState(2);
 
-    public static LauncherState OVERVIEW = new LauncherState(3, ContainerType.OVERVIEW,
-            OVERVIEW_TRANSITION_MS,
-            FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT | FLAG_DO_NOT_RESTORE);
+    public static final LauncherState OVERVIEW = new OverviewState(3);
 
     public final int ordinal;
 
@@ -101,4 +97,12 @@
     public static LauncherState[] values() {
         return Arrays.copyOf(sAllStates, sAllStates.length);
     }
+
+    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+        return new float[] {1, 0};
+    }
+
+    public void onStateEnabled(Launcher launcher) { }
+
+    public void onStateDisabled(Launcher launcher) { }
 }
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 87f3dda..5258fba 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1341,12 +1341,11 @@
     /**
      * return true if freescroll has been enabled, false otherwise
      */
-    public boolean enableFreeScroll() {
+    protected void enableFreeScroll() {
         setEnableFreeScroll(true);
-        return true;
     }
 
-    public void disableFreeScroll() {
+    protected void disableFreeScroll() {
         setEnableFreeScroll(false);
     }
 
diff --git a/src/com/android/launcher3/Utilities.java b/src/com/android/launcher3/Utilities.java
index 7167778..e6bc770 100644
--- a/src/com/android/launcher3/Utilities.java
+++ b/src/com/android/launcher3/Utilities.java
@@ -614,6 +614,12 @@
         return c == null || c.isEmpty();
     }
 
+    public static boolean isAccessibilityEnabled(Context context) {
+        AccessibilityManager accessibilityManager = (AccessibilityManager)
+                context.getSystemService(Context.ACCESSIBILITY_SERVICE);
+        return accessibilityManager.isEnabled();
+    }
+
     public static void sendCustomAccessibilityEvent(View target, int type, String text) {
         AccessibilityManager accessibilityManager = (AccessibilityManager)
                 target.getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 671ba07..173ff6d 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -20,6 +20,7 @@
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_EXIT_DELAY;
 import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
 import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -82,6 +83,7 @@
 import com.android.launcher3.graphics.PreloadIconDrawable;
 import com.android.launcher3.popup.PopupContainerWithArrow;
 import com.android.launcher3.shortcuts.ShortcutDragPreviewProvider;
+import com.android.launcher3.states.OverviewState;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Action;
 import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
@@ -173,16 +175,11 @@
     @Thunk final Launcher mLauncher;
     @Thunk DragController mDragController;
 
-    // These are temporary variables to prevent having to allocate a new object just to
-    // return an (x, y) value from helper functions. Do NOT use them to maintain other state.
-    private static final Rect sTempRect = new Rect();
-
     private final int[] mTempXY = new int[2];
     @Thunk float[] mDragViewVisualCenter = new float[2];
     private final float[] mTempTouchCoordinates = new float[2];
 
     private SpringLoadedDragController mSpringLoadedDragController;
-    private final float mOverviewModeShrinkFactor;
 
     // Direction used for moving the workspace and hotseat UI
     public enum Direction {
@@ -271,6 +268,8 @@
     boolean mOverlayShown = false;
 
     private boolean mForceDrawAdjacentPages = false;
+    private boolean mPageRearrangeEnabled = false;
+
     // Total over scrollX in the overlay direction.
     private float mOverlayTranslation;
 
@@ -307,8 +306,6 @@
         mWallpaperManager = WallpaperManager.getInstance(context);
 
         mWallpaperOffset = new WallpaperOffsetInterpolator(this);
-        mOverviewModeShrinkFactor =
-                res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
 
         setOnHierarchyChangeListener(this);
         setHapticFeedbackEnabled(false);
@@ -448,7 +445,8 @@
         setClipChildren(false);
         setClipToPadding(false);
 
-        setMinScale(mOverviewModeShrinkFactor);
+        // TODO: Remove this
+        setMinScale(OverviewState.SCALE_FACTOR);
         setupLayoutTransition();
 
         mMaxDistanceForFolderCreation = (0.55f * grid.iconSizePx);
@@ -1126,7 +1124,7 @@
         enableHwLayersOnVisiblePages();
     }
 
-    private void showPageIndicatorAtCurrentScroll() {
+    public void showPageIndicatorAtCurrentScroll() {
         if (mPageIndicator != null) {
             mPageIndicator.setScroll(getScrollX(), computeMaxScrollX());
         }
@@ -1282,7 +1280,7 @@
                 }
             });
 
-            final boolean accessibilityEnabled = isAccessibilityEnabled();
+            final boolean accessibilityEnabled = isAccessibilityEnabled(mLauncher);
             animator.addUpdateListener(
                     new AlphaUpdateListener(mLauncher.getHotseat(), accessibilityEnabled));
             animator.addUpdateListener(
@@ -1291,12 +1289,6 @@
         }
     }
 
-    protected boolean isAccessibilityEnabled() {
-        AccessibilityManager am = (AccessibilityManager)
-                mLauncher.getSystemService(Context.ACCESSIBILITY_SERVICE);
-        return am.isEnabled();
-    }
-
     @Override
     protected void notifyPageSwitchListener(int prevPage) {
         super.notifyPageSwitchListener(prevPage);
@@ -1428,8 +1420,8 @@
     }
 
     private void updateChildrenLayersEnabled() {
-        boolean small = mState == LauncherState.OVERVIEW || mIsSwitchingState;
-        boolean enableChildrenLayers = small || isPageInTransition();
+        boolean enableChildrenLayers =
+                isPageRearrangeEnabled() || mIsSwitchingState || isPageInTransition();
 
         if (enableChildrenLayers != mChildrenLayersEnabled) {
             mChildrenLayersEnabled = enableChildrenLayers;
@@ -1551,59 +1543,33 @@
         snapToPage(whichPage, OVERVIEW_TRANSITION_MS, new ZoomInInterpolator());
     }
 
-    int getOverviewModeTranslationY() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
+    private void onStartStateTransition(LauncherState state) {
+        // Change the internal state only when the transition actually starts
+        mState.onStateDisabled(mLauncher);
+        mState = state;
+        mState.onStateEnabled(mLauncher);
 
-        int scaledHeight = (int) (mOverviewModeShrinkFactor * getNormalChildHeight());
-        Rect workspacePadding = grid.getWorkspacePadding(sTempRect);
-        int workspaceTop = mInsets.top + workspacePadding.top;
-        int workspaceBottom = getViewportHeight() - mInsets.bottom - workspacePadding.bottom;
-        int overviewTop = mInsets.top;
-        int overviewBottom = getViewportHeight() - mInsets.bottom - overviewButtonBarHeight;
-        int workspaceOffsetTopEdge = workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
-        int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
-        return -workspaceOffsetTopEdge + overviewOffsetTopEdge;
+        mIsSwitchingState = true;
+        mTransitionProgress = 0;
+
+        updateChildrenLayersEnabled();
     }
 
-    float getSpringLoadedTranslationY() {
-        DeviceProfile grid = mLauncher.getDeviceProfile();
-        if (grid.isVerticalBarLayout() || getChildCount() == 0) {
-            return 0;
-        }
+    private void onEndStateTransition() {
+        mIsSwitchingState = false;
+        mForceDrawAdjacentPages = false;
+        mTransitionProgress = 1;
 
-        float scaledHeight = grid.workspaceSpringLoadShrinkFactor * getNormalChildHeight();
-        float shrunkTop = mInsets.top + grid.dropTargetBarSizePx;
-        float shrunkBottom = getViewportHeight() - mInsets.bottom
-                - grid.getWorkspacePadding(sTempRect).bottom
-                - grid.workspaceSpringLoadedBottomSpace;
-        float totalShrunkSpace = shrunkBottom - shrunkTop;
-
-        float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
-
-        float halfHeight = getHeight() / 2;
-        float myCenter = getTop() + halfHeight;
-        float cellTopFromCenter = halfHeight - getChildAt(0).getTop();
-        float actualCellTop = myCenter - cellTopFromCenter * grid.workspaceSpringLoadShrinkFactor;
-        return (desiredCellTop - actualCellTop) / grid.workspaceSpringLoadShrinkFactor;
+        updateChildrenLayersEnabled();
+        updateAccessibilityFlags();
     }
 
-    float getOverviewModeShrinkFactor() {
-        return mOverviewModeShrinkFactor;
-    }
-
-
     /**
      * Sets the current workspace {@link LauncherState} and updates the UI without any animations
      */
     public void setState(LauncherState toState) {
-        // Update the current state
-        mState = toState;
+        onStartStateTransition(toState);
         mStateTransitionAnimation.setState(mState);
-
-        updateAccessibilityFlags();
-        onPrepareStateTransition(mState.hasMultipleVisiblePages);
-        onStartStateTransition();
         onEndStateTransition();
     }
 
@@ -1612,20 +1578,18 @@
      */
     public void setStateWithAnimation(LauncherState toState, AnimationLayerSet layerViews,
             AnimatorSet anim, AnimationConfig config) {
-        final LauncherState fromState = mState;
+        StateTransitionListener listener = new StateTransitionListener(toState);
+        mStateTransitionAnimation.setStateWithAnimation(mState, toState, anim, layerViews, config);
 
-        // Update the current state
-        mState = toState;
-        mStateTransitionAnimation.setStateWithAnimation(
-                fromState, toState, anim, layerViews, config);
+        // Invalidate the pages now, so that we have the visible pages before the
+        // animation is started
+        if (toState.hasMultipleVisiblePages) {
+            mForceDrawAdjacentPages = true;
+        }
+        invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
 
-        updateAccessibilityFlags();
-        onPrepareStateTransition(mState.hasMultipleVisiblePages);
-
-        StateTransitionListener listener = new StateTransitionListener();
         ValueAnimator stepAnimator = ValueAnimator.ofFloat(0, 1);
         stepAnimator.addUpdateListener(listener);
-
         anim.play(stepAnimator);
         anim.addListener(listener);
     }
@@ -1646,7 +1610,7 @@
     }
 
     private void updateAccessibilityFlags(CellLayout page, int pageNo) {
-        if (mState == LauncherState.OVERVIEW) {
+        if (isPageRearrangeEnabled()) {
             page.setImportantForAccessibility(IMPORTANT_FOR_ACCESSIBILITY_YES);
             page.getShortcutsAndWidgets().setImportantForAccessibility(
                     IMPORTANT_FOR_ACCESSIBILITY_NO_HIDE_DESCENDANTS);
@@ -1668,43 +1632,19 @@
         }
     }
 
-    public void onPrepareStateTransition(boolean multiplePagesVisible) {
-        mIsSwitchingState = true;
-        mTransitionProgress = 0;
-
-        if (multiplePagesVisible) {
-            mForceDrawAdjacentPages = true;
+    public void setPageRearrangeEnabled(boolean isEnabled) {
+        if (mPageRearrangeEnabled != isEnabled) {
+            mPageRearrangeEnabled = isEnabled;
+            if (isEnabled) {
+                enableFreeScroll();
+            } else {
+                disableFreeScroll();
+            }
         }
-        invalidate(); // This will call dispatchDraw(), which calls getVisiblePages().
-
-        updateChildrenLayersEnabled();
     }
 
-    private void onStartStateTransition() {
-        if (mState == LauncherState.SPRING_LOADED) {
-            // Show the page indicator at the same time as the rest of the transition.
-            showPageIndicatorAtCurrentScroll();
-        }
-        getPageIndicator().setShouldAutoHide(mState != LauncherState.SPRING_LOADED);
-    }
-
-    public void onEndStateTransition() {
-        mIsSwitchingState = false;
-        updateChildrenLayersEnabled();
-        mForceDrawAdjacentPages = false;
-        mTransitionProgress = 1;
-
-        if (mState == LauncherState.OVERVIEW) {
-            enableFreeScroll();
-        } else {
-            disableFreeScroll();
-        }
-
-        ViewGroup overviewPanel = mLauncher.getOverviewPanel();
-        if (isAccessibilityEnabled() && overviewPanel.getVisibility() == View.VISIBLE) {
-            overviewPanel.getChildAt(0).performAccessibilityAction(
-                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
-        }
+    public boolean isPageRearrangeEnabled() {
+        return mPageRearrangeEnabled;
     }
 
     public void startDrag(CellLayout.CellInfo cellInfo, DragOptions options) {
@@ -1803,8 +1743,8 @@
     }
 
     private boolean transitionStateShouldAllowDrop() {
-        return ((!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
-                (mState == LauncherState.NORMAL || mState == LauncherState.SPRING_LOADED));
+        return (!isSwitchingState() || mTransitionProgress > ALLOW_DROP_TRANSITION_PROGRESS) &&
+                workspaceIconsCanBeDragged();
     }
 
     /**
@@ -3022,10 +2962,6 @@
         }
     }
 
-    public WorkspaceStateTransitionAnimation getStateTransitionAnimation() {
-        return mStateTransitionAnimation;
-    }
-
     /**
      * Return the current CellInfo describing our current drag; this method exists
      * so that Launcher can sync this object with the correct info when the activity is created/
@@ -3563,16 +3499,6 @@
         }
     }
 
-    @Override
-    public boolean enableFreeScroll() {
-        if (getState() == LauncherState.OVERVIEW) {
-            return super.enableFreeScroll();
-        } else {
-            Log.w(TAG, "enableFreeScroll called but not in overview: state=" + getState());
-            return false;
-        }
-    }
-
     /**
      * Used as a workaround to ensure that the AppWidgetService receives the
      * PACKAGE_ADDED broadcast before updating widgets.
@@ -3629,6 +3555,13 @@
 
     private class StateTransitionListener extends AnimatorListenerAdapter
             implements AnimatorUpdateListener {
+
+        private final LauncherState mToState;
+
+        StateTransitionListener(LauncherState toState) {
+            mToState = toState;
+        }
+
         @Override
         public void onAnimationUpdate(ValueAnimator anim) {
             mTransitionProgress = anim.getAnimatedFraction();
@@ -3636,8 +3569,7 @@
 
         @Override
         public void onAnimationStart(Animator animation) {
-            mTransitionProgress = 0;
-            onStartStateTransition();
+            onStartStateTransition(mToState);
         }
 
         @Override
diff --git a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
index af56fd7..d626b65 100644
--- a/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
+++ b/src/com/android/launcher3/WorkspaceStateTransitionAnimation.java
@@ -18,8 +18,6 @@
 
 import static com.android.launcher3.LauncherAnimUtils.DRAWABLE_ALPHA;
 import static com.android.launcher3.LauncherAnimUtils.SCALE_PROPERTY;
-import static com.android.launcher3.LauncherState.OVERVIEW;
-import static com.android.launcher3.LauncherState.SPRING_LOADED;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
@@ -145,8 +143,6 @@
     private final Launcher mLauncher;
     private final Workspace mWorkspace;
 
-    private final float mSpringLoadedShrinkFactor;
-    private final float mOverviewModeShrinkFactor;
     private final boolean mWorkspaceFadeInAdjacentScreens;
 
     private float mNewScale;
@@ -157,9 +153,6 @@
 
         DeviceProfile grid = mLauncher.getDeviceProfile();
         Resources res = launcher.getResources();
-        mSpringLoadedShrinkFactor = mLauncher.getDeviceProfile().workspaceSpringLoadShrinkFactor;
-        mOverviewModeShrinkFactor =
-                res.getInteger(R.integer.config_workspaceOverviewShrinkPercentage) / 100f;
         mWorkspaceScrimAlpha = res.getInteger(R.integer.config_workspaceScrimAlpha);
         mWorkspaceFadeInAdjacentScreens = grid.shouldFadeAdjacentWorkspaceScreens();
     }
@@ -188,17 +181,9 @@
         // Update the workspace state
         int finalBackgroundAlpha = state.hasScrim ? 255 : 0;
 
-        final float finalWorkspaceTranslationY;
-        if (state == OVERVIEW) {
-            mNewScale = mOverviewModeShrinkFactor;
-            finalWorkspaceTranslationY = mWorkspace.getOverviewModeTranslationY();
-        } else if (state == SPRING_LOADED) {
-            mNewScale = mSpringLoadedShrinkFactor;
-            finalWorkspaceTranslationY = mWorkspace.getSpringLoadedTranslationY();
-        } else {
-            mNewScale = 1f;
-            finalWorkspaceTranslationY = 0;
-        }
+        float[] scaleAndTranslationY = state.getWorkspaceScaleAndTranslation(mLauncher);
+        final float mNewScale = scaleAndTranslationY[0];
+        final float finalWorkspaceTranslationY = scaleAndTranslationY[1];
 
         int toPage = mWorkspace.getPageNearestToCenterOfScreen();
         final int childCount = mWorkspace.getChildCount();
diff --git a/src/com/android/launcher3/states/OverviewState.java b/src/com/android/launcher3/states/OverviewState.java
new file mode 100644
index 0000000..911cec6
--- /dev/null
+++ b/src/com/android/launcher3/states/OverviewState.java
@@ -0,0 +1,78 @@
+/*
+ * 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.OVERVIEW_TRANSITION_MS;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
+import android.graphics.Rect;
+import android.view.accessibility.AccessibilityNodeInfo;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for overview state
+ */
+public class OverviewState extends LauncherState {
+
+    // The percent to shrink the workspace during overview mode
+    public static final float SCALE_FACTOR = 0.7f;
+
+    private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE | FLAG_HIDE_HOTSEAT |
+            FLAG_DO_NOT_RESTORE;
+
+    public OverviewState(int id) {
+        super(id, ContainerType.WORKSPACE, OVERVIEW_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+        DeviceProfile grid = launcher.getDeviceProfile();
+        Workspace ws = launcher.getWorkspace();
+        Rect insets = launcher.getDragLayer().getInsets();
+
+        int overviewButtonBarHeight = grid.getOverviewModeButtonBarHeight();
+        int scaledHeight = (int) (SCALE_FACTOR * ws.getNormalChildHeight());
+        Rect workspacePadding = grid.getWorkspacePadding(null);
+        int workspaceTop = insets.top + workspacePadding.top;
+        int workspaceBottom = ws.getViewportHeight() - insets.bottom - workspacePadding.bottom;
+        int overviewTop = insets.top;
+        int overviewBottom = ws.getViewportHeight() - insets.bottom - overviewButtonBarHeight;
+        int workspaceOffsetTopEdge =
+                workspaceTop + ((workspaceBottom - workspaceTop) - scaledHeight) / 2;
+        int overviewOffsetTopEdge = overviewTop + (overviewBottom - overviewTop - scaledHeight) / 2;
+        return new float[] {SCALE_FACTOR, -workspaceOffsetTopEdge + overviewOffsetTopEdge };
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        launcher.getWorkspace().setPageRearrangeEnabled(true);
+
+        if (isAccessibilityEnabled(launcher)) {
+            launcher.getOverviewPanel().getChildAt(0).performAccessibilityAction(
+                    AccessibilityNodeInfo.ACTION_ACCESSIBILITY_FOCUS, null);
+        }
+    }
+
+    @Override
+    public void onStateDisabled(Launcher launcher) {
+        launcher.getWorkspace().setPageRearrangeEnabled(false);
+    }
+}
diff --git a/src/com/android/launcher3/states/SpringLoadedState.java b/src/com/android/launcher3/states/SpringLoadedState.java
new file mode 100644
index 0000000..f60ef0c
--- /dev/null
+++ b/src/com/android/launcher3/states/SpringLoadedState.java
@@ -0,0 +1,108 @@
+/*
+ * 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.states;
+
+import static com.android.launcher3.LauncherAnimUtils.SPRING_LOADED_TRANSITION_MS;
+
+import android.content.pm.ActivityInfo;
+import android.graphics.Rect;
+import android.os.Handler;
+
+import com.android.launcher3.DeviceProfile;
+import com.android.launcher3.InstallShortcutReceiver;
+import com.android.launcher3.Launcher;
+import com.android.launcher3.LauncherState;
+import com.android.launcher3.Workspace;
+import com.android.launcher3.userevent.nano.LauncherLogProto.ContainerType;
+
+/**
+ * Definition for spring loaded state used during drag and drop.
+ */
+public class SpringLoadedState extends LauncherState {
+
+    private static final int STATE_FLAGS = FLAG_SHOW_SCRIM | FLAG_MULTI_PAGE |
+            FLAG_DISABLE_ACCESSIBILITY | FLAG_DO_NOT_RESTORE;
+
+    // Determines how long to wait after a rotation before restoring the screen orientation to
+    // match the sensor state.
+    private static final int RESTORE_SCREEN_ORIENTATION_DELAY = 500;
+
+    public SpringLoadedState(int id) {
+        super(id, ContainerType.OVERVIEW, SPRING_LOADED_TRANSITION_MS, STATE_FLAGS);
+    }
+
+    @Override
+    public float[] getWorkspaceScaleAndTranslation(Launcher launcher) {
+        DeviceProfile grid = launcher.getDeviceProfile();
+        Workspace ws = launcher.getWorkspace();
+        if (grid.isVerticalBarLayout() || ws.getChildCount() == 0) {
+            return super.getWorkspaceScaleAndTranslation(launcher);
+        }
+
+        float scale = grid.workspaceSpringLoadShrinkFactor;
+        Rect insets = launcher.getDragLayer().getInsets();
+
+        float scaledHeight = scale * ws.getNormalChildHeight();
+        float shrunkTop = insets.top + grid.dropTargetBarSizePx;
+        float shrunkBottom = ws.getViewportHeight() - insets.bottom
+                - grid.getWorkspacePadding(null).bottom
+                - grid.workspaceSpringLoadedBottomSpace;
+        float totalShrunkSpace = shrunkBottom - shrunkTop;
+
+        float desiredCellTop = shrunkTop + (totalShrunkSpace - scaledHeight) / 2;
+
+        float halfHeight = ws.getHeight() / 2;
+        float myCenter = ws.getTop() + halfHeight;
+        float cellTopFromCenter = halfHeight - ws.getChildAt(0).getTop();
+        float actualCellTop = myCenter - cellTopFromCenter * scale;
+        return new float[] { scale, (desiredCellTop - actualCellTop) / scale};
+    }
+
+    @Override
+    public void onStateEnabled(Launcher launcher) {
+        Workspace ws = launcher.getWorkspace();
+        ws.showPageIndicatorAtCurrentScroll();
+        ws.getPageIndicator().setShouldAutoHide(false);
+
+        // Lock the orientation:
+        if (launcher.isRotationEnabled()) {
+            launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_LOCKED);
+        }
+
+        // Prevent any Un/InstallShortcutReceivers from updating the db while we are
+        // in spring loaded mode
+        InstallShortcutReceiver.enableInstallQueue(InstallShortcutReceiver.FLAG_DRAG_AND_DROP);
+    }
+
+    @Override
+    public void onStateDisabled(final Launcher launcher) {
+        launcher.getWorkspace().getPageIndicator().setShouldAutoHide(true);
+
+        // Unlock rotation lock
+        if (launcher.isRotationEnabled()) {
+            new Handler().postDelayed(new Runnable() {
+                @Override
+                public void run() {
+                    launcher.setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_UNSPECIFIED);
+                }
+            }, RESTORE_SCREEN_ORIENTATION_DELAY);
+        }
+
+        // Re-enable any Un/InstallShortcutReceiver and now process any queued items
+        InstallShortcutReceiver.disableAndFlushInstallQueue(
+                InstallShortcutReceiver.FLAG_DRAG_AND_DROP, launcher);
+    }
+}