Merge "UNREVIEWED: TAPL prototype for in-lab flake test" into ub-launcher3-edmonton-polish
diff --git a/quickstep/src/com/android/quickstep/views/RecentsView.java b/quickstep/src/com/android/quickstep/views/RecentsView.java
index f7f496f..92852d2 100644
--- a/quickstep/src/com/android/quickstep/views/RecentsView.java
+++ b/quickstep/src/com/android/quickstep/views/RecentsView.java
@@ -259,8 +259,7 @@
     public RecentsView(Context context, AttributeSet attrs, int defStyleAttr) {
         super(context, attrs, defStyleAttr);
         setPageSpacing(getResources().getDimensionPixelSize(R.dimen.recents_page_spacing));
-        setEnableFreeScroll(true);
-        setEnableOverscroll(true);
+        enableFreeScroll(true);
 
         mFastFlingVelocity = getResources()
                 .getDimensionPixelSize(R.dimen.recents_fast_fling_velocity);
diff --git a/quickstep/src/com/android/quickstep/views/TaskView.java b/quickstep/src/com/android/quickstep/views/TaskView.java
index c4ccd96..508e5bb 100644
--- a/quickstep/src/com/android/quickstep/views/TaskView.java
+++ b/quickstep/src/com/android/quickstep/views/TaskView.java
@@ -121,7 +121,6 @@
                     TaskUtils.getLaunchComponentKeyForTask(getTask().key));
         });
         setOutlineProvider(new TaskOutlineProvider(getResources()));
-        setDescendantFocusability(FOCUS_BLOCK_DESCENDANTS);
     }
 
     @Override
diff --git a/src/com/android/launcher3/LauncherScroller.java b/src/com/android/launcher3/LauncherScroller.java
index af87550..a9b4955 100644
--- a/src/com/android/launcher3/LauncherScroller.java
+++ b/src/com/android/launcher3/LauncherScroller.java
@@ -459,13 +459,13 @@
         return Math.log(INFLEXION * Math.abs(velocity) / (mFlingFriction * mPhysicalCoeff));
     }
 
-    public int getSplineFlingDuration(float velocity) {
+    private int getSplineFlingDuration(float velocity) {
         final double l = getSplineDeceleration(velocity);
         final double decelMinusOne = DECELERATION_RATE - 1.0;
         return (int) (1000.0 * Math.exp(l / decelMinusOne));
     }
 
-    public double getSplineFlingDistance(float velocity) {
+    private double getSplineFlingDistance(float velocity) {
         final double l = getSplineDeceleration(velocity);
         final double decelMinusOne = DECELERATION_RATE - 1.0;
         return mFlingFriction * mPhysicalCoeff * Math.exp(DECELERATION_RATE / decelMinusOne * l);
diff --git a/src/com/android/launcher3/LauncherStateManager.java b/src/com/android/launcher3/LauncherStateManager.java
index 8b415d6..3c7c1aa 100644
--- a/src/com/android/launcher3/LauncherStateManager.java
+++ b/src/com/android/launcher3/LauncherStateManager.java
@@ -382,7 +382,9 @@
     }
 
     private void onStateTransitionStart(LauncherState state) {
-        mState.onStateDisabled(mLauncher);
+        if (mState != state) {
+            mState.onStateDisabled(mLauncher);
+        }
         mState = state;
         mState.onStateEnabled(mLauncher);
         mLauncher.getAppWidgetHost().setResumed(state == LauncherState.NORMAL);
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 0cb6539..db5dc66 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -63,6 +63,7 @@
     protected static final ComputePageScrollsLogic SIMPLE_SCROLL_LOGIC = (v) -> v.getVisibility() != GONE;
 
     public static final int PAGE_SNAP_ANIMATION_DURATION = 750;
+    public static final int SLOW_PAGE_SNAP_ANIMATION_DURATION = 950;
 
     // OverScroll constants
     private final static int OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION = 270;
@@ -82,6 +83,7 @@
     public static final int INVALID_RESTORE_PAGE = -1001;
 
     private boolean mFreeScroll = false;
+    private boolean mSettleOnPageInFreeScroll = false;
 
     protected int mFlingThresholdVelocity;
     protected int mMinFlingVelocity;
@@ -354,6 +356,17 @@
 
     @Override
     public void scrollTo(int x, int y) {
+        // In free scroll mode, we clamp the scrollX
+        if (mFreeScroll) {
+            // If the scroller is trying to move to a location beyond the maximum allowed
+            // in the free scroll mode, we make sure to end the scroll operation.
+            if (!mScroller.isFinished() && (x > mMaxScrollX || x < 0)) {
+                forceFinishScroller(false);
+            }
+
+            x = Utilities.boundToRange(x, 0, mMaxScrollX);
+        }
+
         mUnboundedScrollX = x;
 
         boolean isXBeforeFirstPage = mIsRtl ? (x > mMaxScrollX) : (x < 0);
@@ -1013,7 +1026,13 @@
         dampedOverScroll(amount);
     }
 
-    protected void setEnableFreeScroll(boolean freeScroll) {
+
+    protected void enableFreeScroll(boolean settleOnPageInFreeScroll) {
+        setEnableFreeScroll(true);
+        mSettleOnPageInFreeScroll = settleOnPageInFreeScroll;
+    }
+
+    private void setEnableFreeScroll(boolean freeScroll) {
         boolean wasFreeScroll = mFreeScroll;
         mFreeScroll = freeScroll;
 
@@ -1022,6 +1041,8 @@
         } else if (wasFreeScroll) {
             snapToPage(getNextPage());
         }
+
+        setEnableOverscroll(!freeScroll);
     }
 
     protected void setEnableOverscroll(boolean enable) {
@@ -1134,15 +1155,42 @@
                         snapToDestination();
                     }
                 } else {
-                    int unscaledScrollX = getScrollX() - (int) Math.round(
-                            mScroller.getSplineFlingDistance(velocityX) * Math.signum(velocityX));
-                    int duration = mScroller.getSplineFlingDuration(velocityX);
-                    int finalPage = getPageNearestToCenterOfScreen(unscaledScrollX);
-                    if ((isFling || isSignificantMove) && (finalPage != mCurrentPage)) {
-                        snapToPage(finalPage, duration);
-                    } else {
-                        snapToDestination();
+                    if (!mScroller.isFinished()) {
+                        abortScrollerAnimation(true);
                     }
+
+                    float scaleX = getScaleX();
+                    int vX = (int) (-velocityX * scaleX);
+                    int initialScrollX = (int) (getScrollX() * scaleX);
+
+                    mScroller.setInterpolator(mDefaultInterpolator);
+                    mScroller.fling(initialScrollX,
+                            getScrollY(), vX, 0, Integer.MIN_VALUE, Integer.MAX_VALUE, 0, 0);
+                    int unscaledScrollX = (int) (mScroller.getFinalX() / scaleX);
+                    mNextPage = getPageNearestToCenterOfScreen(unscaledScrollX);
+                    int firstPageScroll = getScrollForPage(!mIsRtl ? 0 : getPageCount() - 1);
+                    int lastPageScroll = getScrollForPage(!mIsRtl ? getPageCount() - 1 : 0);
+                    if (mSettleOnPageInFreeScroll && unscaledScrollX > 0
+                            && unscaledScrollX < mMaxScrollX) {
+                        // If scrolling ends in the half of the added space that is closer to the
+                        // end, settle to the end. Otherwise snap to the nearest page.
+                        // If flinging past one of the ends, don't change the velocity as it will
+                        // get stopped at the end anyway.
+                        final int finalX = unscaledScrollX < firstPageScroll / 2 ?
+                                0 :
+                                unscaledScrollX > (lastPageScroll + mMaxScrollX) / 2 ?
+                                        mMaxScrollX :
+                                        getScrollForPage(mNextPage);
+
+                        mScroller.setFinalX((int) (finalX * getScaleX()));
+                        // Ensure the scroll/snap doesn't happen too fast;
+                        int extraScrollDuration = OVERSCROLL_PAGE_SNAP_ANIMATION_DURATION
+                                - mScroller.getDuration();
+                        if (extraScrollDuration > 0) {
+                            mScroller.extendDuration(extraScrollDuration);
+                        }
+                    }
+                    invalidate();
                 }
                 onScrollInteractionEnd();
             } else if (mTouchState == TOUCH_STATE_PREV_PAGE) {
diff --git a/src/com/android/launcher3/allapps/AllAppsContainerView.java b/src/com/android/launcher3/allapps/AllAppsContainerView.java
index 8993978..fdf32af 100644
--- a/src/com/android/launcher3/allapps/AllAppsContainerView.java
+++ b/src/com/android/launcher3/allapps/AllAppsContainerView.java
@@ -312,6 +312,12 @@
         }
     }
 
+    @Override
+    public int getCanvasClipTopForOverscroll() {
+        // Do not clip if the QSB is attached to the spring, otherwise the QSB will get clipped.
+        return mSpringViews.get(getSearchView().getId()) ? 0 : mHeader.getTop();
+    }
+
     private void rebindAdapters(boolean showTabs) {
         rebindAdapters(showTabs, false /* force */);
     }
diff --git a/src/com/android/launcher3/graphics/LauncherIcons.java b/src/com/android/launcher3/graphics/LauncherIcons.java
index 89ba72a..1fa233a 100644
--- a/src/com/android/launcher3/graphics/LauncherIcons.java
+++ b/src/com/android/launcher3/graphics/LauncherIcons.java
@@ -182,7 +182,12 @@
      * The bitmap is also visually normalized with other icons.
      */
     public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk) {
-        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false);
+        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, false, null);
+    }
+
+    public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
+            boolean isInstantApp) {
+        return createBadgedIconBitmap(icon, user, iconAppTargetSdk, isInstantApp, null);
     }
 
     /**
@@ -191,8 +196,10 @@
      * The bitmap is also visually normalized with other icons.
      */
     public BitmapInfo createBadgedIconBitmap(Drawable icon, UserHandle user, int iconAppTargetSdk,
-            boolean isInstantApp) {
-        float[] scale = new float[1];
+            boolean isInstantApp, float [] scale) {
+        if (scale == null) {
+            scale = new float[1];
+        }
         icon = normalizeAndWrapToAdaptiveIcon(icon, iconAppTargetSdk, null, scale);
         Bitmap bitmap = createIconBitmap(icon, scale[0]);
         if (Utilities.ATLEAST_OREO && icon instanceof AdaptiveIconDrawable) {
diff --git a/src/com/android/launcher3/qsb/QsbContainerView.java b/src/com/android/launcher3/qsb/QsbContainerView.java
index b26d39f..8e29df1 100644
--- a/src/com/android/launcher3/qsb/QsbContainerView.java
+++ b/src/com/android/launcher3/qsb/QsbContainerView.java
@@ -16,6 +16,10 @@
 
 package com.android.launcher3.qsb;
 
+import static android.appwidget.AppWidgetManager.ACTION_APPWIDGET_BIND;
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_ID;
+import static android.appwidget.AppWidgetManager.EXTRA_APPWIDGET_PROVIDER;
+
 import android.app.Activity;
 import android.app.Fragment;
 import android.app.SearchManager;
@@ -74,11 +78,12 @@
     /**
      * A fragment to display the QSB.
      */
-    public static class QsbFragment extends Fragment implements View.OnClickListener {
+    public static class QsbFragment extends Fragment {
 
+        public static final int QSB_WIDGET_HOST_ID = 1026;
         private static final int REQUEST_BIND_QSB = 1;
-        private static final String QSB_WIDGET_ID = "qsb_widget_id";
 
+        protected String mKeyWidgetId = "qsb_widget_id";
         private QsbWidgetHost mQsbWidgetHost;
         private AppWidgetProviderInfo mWidgetInfo;
         private QsbWidgetHostView mQsb;
@@ -90,10 +95,15 @@
         @Override
         public void onCreate(Bundle savedInstanceState) {
             super.onCreate(savedInstanceState);
-            mQsbWidgetHost = new QsbWidgetHost(getActivity());
+            mQsbWidgetHost = createHost();
             mOrientation = getContext().getResources().getConfiguration().orientation;
         }
 
+        protected QsbWidgetHost createHost() {
+            return new QsbWidgetHost(getActivity(), QSB_WIDGET_HOST_ID,
+                    (c) -> new QsbWidgetHostView(c));
+        }
+
         private FrameLayout mWrapper;
 
         @Override
@@ -110,24 +120,16 @@
         }
 
         private View createQsb(ViewGroup container) {
-            Activity activity = getActivity();
-            mWidgetInfo = getSearchWidgetProvider(activity);
+            mWidgetInfo = getSearchWidgetProvider();
             if (mWidgetInfo == null) {
                 // There is no search provider, just show the default widget.
-                return QsbWidgetHostView.getDefaultView(container);
+                return getDefaultView(container, false /* show setup icon */);
             }
-
+            Bundle opts = createBindOptions();
+            Activity activity = getActivity();
             AppWidgetManager widgetManager = AppWidgetManager.getInstance(activity);
-            InvariantDeviceProfile idp = LauncherAppState.getIDP(activity);
 
-            Bundle opts = new Bundle();
-            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(activity, idp.numColumns, 1, null);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
-            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
-
-            int widgetId = Utilities.getPrefs(activity).getInt(QSB_WIDGET_ID, -1);
+            int widgetId = Utilities.getPrefs(activity).getInt(mKeyWidgetId, -1);
             AppWidgetProviderInfo widgetInfo = widgetManager.getAppWidgetInfo(widgetId);
             boolean isWidgetBound = (widgetInfo != null) &&
                     widgetInfo.provider.equals(mWidgetInfo.provider);
@@ -166,32 +168,18 @@
             }
 
             // Return a default widget with setup icon.
-            View v = QsbWidgetHostView.getDefaultView(container);
-            View setupButton = v.findViewById(R.id.btn_qsb_setup);
-            setupButton.setVisibility(View.VISIBLE);
-            setupButton.setOnClickListener(this);
-            return v;
+            return getDefaultView(container, true /* show setup icon */);
         }
 
         private void saveWidgetId(int widgetId) {
-            Utilities.getPrefs(getActivity()).edit().putInt(QSB_WIDGET_ID, widgetId).apply();
-        }
-
-        @Override
-        public void onClick(View view) {
-            // Start intent for bind the widget
-            Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_BIND);
-            // Allocate a new widget id for QSB
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, mQsbWidgetHost.allocateAppWidgetId());
-            intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider);
-            startActivityForResult(intent, REQUEST_BIND_QSB);
+            Utilities.getPrefs(getActivity()).edit().putInt(mKeyWidgetId, widgetId).apply();
         }
 
         @Override
         public void onActivityResult(int requestCode, int resultCode, Intent data) {
             if (requestCode == REQUEST_BIND_QSB) {
                 if (resultCode == Activity.RESULT_OK) {
-                    saveWidgetId(data.getIntExtra(AppWidgetManager.EXTRA_APPWIDGET_ID, -1));
+                    saveWidgetId(data.getIntExtra(EXTRA_APPWIDGET_ID, -1));
                     rebindFragment();
                 } else {
                     mQsbWidgetHost.deleteHost();
@@ -228,48 +216,83 @@
         public boolean isQsbEnabled() {
             return FeatureFlags.QSB_ON_FIRST_SCREEN;
         }
-    }
 
-    /**
-     * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
-     * provided by the same package which is set to be global search activity.
-     * If widgetCategory is not supported, or no such widget is found, returns the first widget
-     * provided by the package.
-     */
-    public static AppWidgetProviderInfo getSearchWidgetProvider(Context context) {
-        SearchManager searchManager =
-                (SearchManager) context.getSystemService(Context.SEARCH_SERVICE);
-        ComponentName searchComponent = searchManager.getGlobalSearchActivity();
-        if (searchComponent == null) return null;
-        String providerPkg = searchComponent.getPackageName();
+        protected Bundle createBindOptions() {
+            InvariantDeviceProfile idp = LauncherAppState.getIDP(getActivity());
 
-        AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+            Bundle opts = new Bundle();
+            Rect size = AppWidgetResizeFrame.getWidgetSizeRanges(getActivity(),
+                    idp.numColumns, 1, null);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_WIDTH, size.left);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MIN_HEIGHT, size.top);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_WIDTH, size.right);
+            opts.putInt(AppWidgetManager.OPTION_APPWIDGET_MAX_HEIGHT, size.bottom);
+            return opts;
+        }
 
-        AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(context);
-        for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
-            if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
-                if ((info.widgetCategory & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
-                    return info;
-                } else if (defaultWidgetForSearchPackage == null) {
-                    defaultWidgetForSearchPackage = info;
+        protected View getDefaultView(ViewGroup container, boolean showSetupIcon) {
+            // Return a default widget with setup icon.
+            View v = QsbWidgetHostView.getDefaultView(container);
+            if (showSetupIcon) {
+                View setupButton = v.findViewById(R.id.btn_qsb_setup);
+                setupButton.setVisibility(View.VISIBLE);
+                setupButton.setOnClickListener((v2) -> startActivityForResult(
+                        new Intent(ACTION_APPWIDGET_BIND)
+                                .putExtra(EXTRA_APPWIDGET_ID, mQsbWidgetHost.allocateAppWidgetId())
+                                .putExtra(EXTRA_APPWIDGET_PROVIDER, mWidgetInfo.provider),
+                        REQUEST_BIND_QSB));
+            }
+            return v;
+        }
+
+        /**
+         * Returns a widget with category {@link AppWidgetProviderInfo#WIDGET_CATEGORY_SEARCHBOX}
+         * provided by the same package which is set to be global search activity.
+         * If widgetCategory is not supported, or no such widget is found, returns the first widget
+         * provided by the package.
+         */
+        protected AppWidgetProviderInfo getSearchWidgetProvider() {
+            SearchManager searchManager =
+                    (SearchManager) getActivity().getSystemService(Context.SEARCH_SERVICE);
+            ComponentName searchComponent = searchManager.getGlobalSearchActivity();
+            if (searchComponent == null) return null;
+            String providerPkg = searchComponent.getPackageName();
+
+            AppWidgetProviderInfo defaultWidgetForSearchPackage = null;
+
+            AppWidgetManager appWidgetManager = AppWidgetManager.getInstance(getActivity());
+            for (AppWidgetProviderInfo info : appWidgetManager.getInstalledProviders()) {
+                if (info.provider.getPackageName().equals(providerPkg) && info.configure == null) {
+                    if ((info.widgetCategory
+                            & AppWidgetProviderInfo.WIDGET_CATEGORY_SEARCHBOX) != 0) {
+                        return info;
+                    } else if (defaultWidgetForSearchPackage == null) {
+                        defaultWidgetForSearchPackage = info;
+                    }
                 }
             }
+            return defaultWidgetForSearchPackage;
         }
-        return defaultWidgetForSearchPackage;
     }
 
-    private static class QsbWidgetHost extends AppWidgetHost {
+    public static class QsbWidgetHost extends AppWidgetHost {
 
-        private static final int QSB_WIDGET_HOST_ID = 1026;
+        private final WidgetViewFactory mViewFactory;
 
-        public QsbWidgetHost(Context context) {
-            super(context, QSB_WIDGET_HOST_ID);
+        public QsbWidgetHost(Context context, int hostId, WidgetViewFactory viewFactory) {
+            super(context, hostId);
+            mViewFactory = viewFactory;
         }
 
         @Override
         protected AppWidgetHostView onCreateView(
                 Context context, int appWidgetId, AppWidgetProviderInfo appWidget) {
-            return new QsbWidgetHostView(context);
+            return mViewFactory.newView(context);
         }
     }
+
+    public interface WidgetViewFactory {
+
+        QsbWidgetHostView newView(Context context);
+    }
 }
diff --git a/src/com/android/launcher3/qsb/QsbWidgetHostView.java b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
index 7d8a4db..407812d 100644
--- a/src/com/android/launcher3/qsb/QsbWidgetHostView.java
+++ b/src/com/android/launcher3/qsb/QsbWidgetHostView.java
@@ -58,13 +58,9 @@
         try {
             super.onLayout(changed, left, top, right, bottom);
         } catch (final RuntimeException e) {
-            post(new Runnable() {
-                @Override
-                public void run() {
-                    // Update the widget with 0 Layout id, to reset the view to error view.
-                    updateAppWidget(new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0));
-                }
-            });
+            // Update the widget with 0 Layout id, to reset the view to error view.
+            post(() -> updateAppWidget(
+                    new RemoteViews(getAppWidgetInfo().provider.getPackageName(), 0)));
         }
     }
 
@@ -76,24 +72,16 @@
     @Override
     protected View getDefaultView() {
         View v = super.getDefaultView();
-        v.setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                Launcher.getLauncher(getContext()).startSearch("", false, null, true);
-            }
-        });
+        v.setOnClickListener((v2) ->
+                Launcher.getLauncher(getContext()).startSearch("", false, null, true));
         return v;
     }
 
     public static View getDefaultView(ViewGroup parent) {
         View v = LayoutInflater.from(parent.getContext())
                 .inflate(R.layout.qsb_default_view, parent, false);
-        v.findViewById(R.id.btn_qsb_search).setOnClickListener(new OnClickListener() {
-            @Override
-            public void onClick(View view) {
-                Launcher.getLauncher(view.getContext()).startSearch("", false, null, true);
-            }
-        });
+        v.findViewById(R.id.btn_qsb_search).setOnClickListener((v2) ->
+                Launcher.getLauncher(v2.getContext()).startSearch("", false, null, true));
         return v;
     }
 }
diff --git a/src/com/android/launcher3/views/SpringRelativeLayout.java b/src/com/android/launcher3/views/SpringRelativeLayout.java
index 5022d65..b0313ce 100644
--- a/src/com/android/launcher3/views/SpringRelativeLayout.java
+++ b/src/com/android/launcher3/views/SpringRelativeLayout.java
@@ -54,7 +54,7 @@
                 }
             };
 
-    private final SparseBooleanArray mSpringViews = new SparseBooleanArray();
+    protected final SparseBooleanArray mSpringViews = new SparseBooleanArray();
     private final SpringAnimation mSpring;
 
     private float mDampedScrollShift = 0;
@@ -85,12 +85,24 @@
         invalidate();
     }
 
+    /**
+     * Used to clip the canvas when drawing child views during overscroll.
+     */
+    public int getCanvasClipTopForOverscroll() {
+        return 0;
+    }
+
     @Override
     protected boolean drawChild(Canvas canvas, View child, long drawingTime) {
         if (mDampedScrollShift != 0 && mSpringViews.get(child.getId())) {
+            int saveCount = canvas.save();
+
+            canvas.clipRect(0, getCanvasClipTopForOverscroll(), getWidth(), getHeight());
             canvas.translate(0, mDampedScrollShift);
             boolean result = super.drawChild(canvas, child, drawingTime);
-            canvas.translate(0, -mDampedScrollShift);
+
+            canvas.restoreToCount(saveCount);
+
             return result;
         }
         return super.drawChild(canvas, child, drawingTime);