Adding overlay interaction support

> Sending unboundX to the overlay which is the total untranslated X and not just deltaX from last frame
> Handling overlay callback and translating workspace accordingly

Change-Id: I3bd8d9efac738e9ce131758f0e5ff1b9c1d6a8fc
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index a5c6720..68b0dff 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1107,12 +1107,7 @@
          * 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(int progress, boolean rtl);
-
-        /**
-         * Screen has stopped scrolling
-         */
-        public void onScrollSettled();
+        public void onScrollChange(float progress, boolean rtl);
 
         /**
          * Called when the launcher is ready to use the overlay
@@ -1134,11 +1129,16 @@
     }
 
     public interface LauncherOverlayCallbacks {
-
+        public void onScrollChanged(float progress);
     }
 
     class LauncherOverlayCallbacksImpl implements LauncherOverlayCallbacks {
 
+        public void onScrollChanged(float progress) {
+            if (mWorkspace != null) {
+                mWorkspace.onOverlayScrollChanged(progress);
+            }
+        }
     }
 
     protected boolean hasSettings() {
@@ -1631,6 +1631,10 @@
         FirstFrameAnimatorHelper.initializeDrawListener(getWindow().getDecorView());
         mAttached = true;
         mVisible = true;
+
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onAttachedToWindow();
+        }
     }
 
     @Override
@@ -1643,6 +1647,10 @@
             mAttached = false;
         }
         updateAutoAdvanceState();
+
+        if (mLauncherCallbacks != null) {
+            mLauncherCallbacks.onDetachedFromWindow();
+        }
     }
 
     public void onWindowVisibilityChanged(int visibility) {
diff --git a/src/com/android/launcher3/LauncherCallbacks.java b/src/com/android/launcher3/LauncherCallbacks.java
index 591bd92..0be45c3 100644
--- a/src/com/android/launcher3/LauncherCallbacks.java
+++ b/src/com/android/launcher3/LauncherCallbacks.java
@@ -43,6 +43,8 @@
     public void onRequestPermissionsResult(int requestCode, String[] permissions,
             int[] grantResults);
     public void onWindowFocusChanged(boolean hasFocus);
+    public void onAttachedToWindow();
+    public void onDetachedFromWindow();
     public boolean onPrepareOptionsMenu(Menu menu);
     public void dump(String prefix, FileDescriptor fd, PrintWriter w, String[] args);
     public void onHomeIntent();
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index c826c5f..b3a714b 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -100,7 +100,7 @@
 
     @ViewDebug.ExportedProperty(category = "launcher")
     protected int mNextPage = INVALID_PAGE;
-    private int mMaxScrollX;
+    protected int mMaxScrollX;
     protected LauncherScroller mScroller;
     private Interpolator mDefaultInterpolator;
     private VelocityTracker mVelocityTracker;
@@ -542,9 +542,13 @@
         super.setOnLongClickListener(l);
     }
 
+    protected int getUnboundedScrollX() {
+        return getScrollX();
+    }
+
     @Override
     public void scrollBy(int x, int y) {
-        scrollTo(getScrollX() + x, getScrollY() + y);
+        scrollTo(getUnboundedScrollX() + x, getScrollY() + y);
     }
 
     @Override
@@ -1987,7 +1991,7 @@
         int halfScreenSize = getViewportWidth() / 2;
 
         final int newX = getScrollForPage(whichPage);
-        int delta = newX - getScrollX();
+        int delta = newX - getUnboundedScrollX();
         int duration = 0;
 
         if (Math.abs(velocity) < mMinFlingVelocity) {
@@ -2037,7 +2041,7 @@
         whichPage = validateNewPage(whichPage);
 
         int newX = getScrollForPage(whichPage);
-        final int delta = newX - getScrollX();
+        final int delta = newX - getUnboundedScrollX();
         snapToPage(whichPage, delta, duration, immediate, interpolator);
     }
 
@@ -2069,7 +2073,7 @@
             mScroller.setInterpolator(mDefaultInterpolator);
         }
 
-        mScroller.startScroll(getScrollX(), 0, delta, 0, duration);
+        mScroller.startScroll(getUnboundedScrollX(), 0, delta, 0, duration);
 
         updatePageIndicator();
 
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 840f9f2..e983e79 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -31,6 +31,7 @@
 import android.content.res.Resources;
 import android.graphics.Bitmap;
 import android.graphics.Canvas;
+import android.graphics.Matrix;
 import android.graphics.Paint;
 import android.graphics.Point;
 import android.graphics.PointF;
@@ -51,6 +52,9 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.accessibility.AccessibilityManager;
+import android.view.animation.AccelerateInterpolator;
+import android.view.animation.DecelerateInterpolator;
+import android.view.animation.Interpolator;
 import android.widget.TextView;
 
 import com.android.launcher3.folder.Folder;
@@ -264,9 +268,12 @@
     LauncherOverlay mLauncherOverlay;
     boolean mScrollInteractionBegan;
     boolean mStartedSendingScrollEvents;
-    boolean mShouldSendPageSettled;
-    int mLastOverlaySroll = 0;
+    float mLastOverlaySroll = 0;
+    // Total over scrollX in the overlay direction.
+    private int mUnboundedScrollX;
     private boolean mForceDrawAdjacentPages = false;
+    // Total over scrollX in the overlay direction.
+    private float mOverlayTranslation;
 
     // Handles workspace state transitions
     private WorkspaceStateTransitionAnimation mStateTransitionAnimation;
@@ -1202,11 +1209,6 @@
             stripEmptyScreens();
             mStripScreensOnPageStopMoving = false;
         }
-
-        if (mShouldSendPageSettled) {
-            mLauncherOverlay.onScrollSettled();
-            mShouldSendPageSettled = false;
-        }
     }
 
     protected void onScrollInteractionBegin() {
@@ -1225,6 +1227,27 @@
 
     public void setLauncherOverlay(LauncherOverlay overlay) {
         mLauncherOverlay = overlay;
+        // A new overlay has been set. Reset event tracking
+        mStartedSendingScrollEvents = false;
+        onOverlayScrollChanged(0);
+    }
+
+    @Override
+    protected int getUnboundedScrollX() {
+        if (mLauncherOverlay != null) {
+            if ((mIsRtl && mUnboundedScrollX > mMaxScrollX) ||
+                    (!mIsRtl && mUnboundedScrollX < 0)) {
+                return mUnboundedScrollX;
+            }
+        }
+
+        return super.getUnboundedScrollX();
+    }
+
+    @Override
+    public void scrollTo(int x, int y) {
+        mUnboundedScrollX = x;
+        super.scrollTo(x, y);
     }
 
     @Override
@@ -1242,15 +1265,10 @@
             if (!mStartedSendingScrollEvents && mScrollInteractionBegan) {
                 mStartedSendingScrollEvents = true;
                 mLauncherOverlay.onScrollInteractionBegin();
-                mShouldSendPageSettled = true;
             }
-            int screenSize = getViewportWidth();
-            float f = (amount / screenSize);
 
-            int progress = (int) Math.abs((f * 100));
-
-            mLastOverlaySroll = progress;
-            mLauncherOverlay.onScrollChange(progress, mIsRtl);
+            mLastOverlaySroll = Math.abs(amount / getViewportWidth());
+            mLauncherOverlay.onScrollChange(mLastOverlaySroll, mIsRtl);
         } else if (shouldOverScroll) {
             dampedOverScroll(amount);
         }
@@ -1260,6 +1278,49 @@
         }
     }
 
+    private final Interpolator mAlphaInterpolator = new DecelerateInterpolator(3f);
+
+    /**
+     * The overlay scroll is being controlled locally, just update our overlay effect
+     */
+    public void onOverlayScrollChanged(float scroll) {
+        float offset = 0f;
+        float slip = 0f;
+
+        scroll = Math.max(scroll - offset, 0);
+        scroll = Math.min(1, scroll / (1 - offset));
+
+        float alpha = 1 - mAlphaInterpolator.getInterpolation(scroll);
+        float transX = mLauncher.getDragLayer().getMeasuredWidth() * scroll;
+        transX *= 1 - slip;
+
+        if (mIsRtl) {
+            transX = -transX;
+        }
+
+        // TODO(adamcohen): figure out a final effect here. We may need to recommend
+        // different effects based on device performance. On at least one relatively high-end
+        // device I've tried, translating the launcher causes things to get quite laggy.
+        setTranslationAndAlpha(mLauncher.getSearchDropTargetBar(), transX, alpha);
+        setTranslationAndAlpha(getPageIndicator(), transX, alpha);
+        setTranslationAndAlpha(getChildAt(getCurrentPage()), transX, alpha);
+        setTranslationAndAlpha(mLauncher.getHotseat(), transX, alpha);
+
+        // When the animation finishes, reset all pages, just in case we missed a page.
+        if (transX == 0) {
+            for (int i = getChildCount() - 1; i >= 0; i--) {
+                setTranslationAndAlpha(getChildAt(i), 0, alpha);
+            }
+        }
+    }
+
+    private void setTranslationAndAlpha(View v, float transX, float alpha) {
+        if (v != null) {
+            v.setTranslationX(transX);
+            v.setAlpha(alpha);
+        }
+    }
+
     @Override
     protected void getEdgeVerticalPostion(int[] pos) {
         View child = getChildAt(getPageCount() - 1);
diff --git a/src/com/android/launcher3/testing/LauncherExtension.java b/src/com/android/launcher3/testing/LauncherExtension.java
index 279be70..475762f 100644
--- a/src/com/android/launcher3/testing/LauncherExtension.java
+++ b/src/com/android/launcher3/testing/LauncherExtension.java
@@ -293,5 +293,13 @@
         public void setLauncherSearchCallback(Object callbacks) {
             // Do nothing
         }
+
+        @Override
+        public void onAttachedToWindow() {
+        }
+
+        @Override
+        public void onDetachedFromWindow() {
+        };
     }
 }