Merge "Not using pressHome to start Launcher when tests are running in-proc" into ub-launcher3-master
diff --git a/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
new file mode 100644
index 0000000..9a18b45
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -0,0 +1,123 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.quickstep.views.IconRecentsView.CONTENT_ALPHA;
+
+import android.animation.AnimatorSet;
+import android.animation.ObjectAnimator;
+import android.content.ComponentName;
+import android.graphics.Rect;
+
+import androidx.annotation.Nullable;
+
+import com.android.launcher3.anim.AnimatorPlaybackController;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.IconRecentsView;
+import com.android.quickstep.views.RecentsView;
+
+import java.util.function.BiPredicate;
+import java.util.function.Consumer;
+
+/**
+ * {@link ActivityControlHelper} for recents when the default launcher is different than the
+ * currently running one and apps should interact with the {@link RecentsActivity} as opposed
+ * to the in-launcher one.
+ */
+public final class FallbackActivityControllerHelper extends
+        GoActivityControlHelper<RecentsActivity> {
+
+    public FallbackActivityControllerHelper(ComponentName homeComponent) { }
+
+    @Override
+    public AnimationFactory prepareRecentsUI(RecentsActivity activity, boolean activityVisible,
+            boolean animateActivity, Consumer<AnimatorPlaybackController> callback) {
+        if (activityVisible) {
+            return (transitionLength) -> { };
+        }
+
+        IconRecentsView rv = activity.getOverviewPanel();
+        rv.setAlpha(0);
+
+        return new AnimationFactory() {
+
+            boolean isAnimatingToRecents = false;
+
+            @Override
+            public void onRemoteAnimationReceived(RemoteAnimationTargetSet targets) {
+                isAnimatingToRecents = targets != null && targets.isAnimatingHome();
+                if (!isAnimatingToRecents) {
+                    rv.setAlpha(1);
+                }
+                createActivityController(getSwipeUpDestinationAndLength(
+                        activity.getDeviceProfile(), activity, new Rect()));
+            }
+
+            @Override
+            public void createActivityController(long transitionLength) {
+                if (!isAnimatingToRecents) {
+                    return;
+                }
+
+                ObjectAnimator anim = ObjectAnimator.ofFloat(rv, CONTENT_ALPHA, 0, 1);
+                anim.setDuration(transitionLength).setInterpolator(LINEAR);
+                AnimatorSet animatorSet = new AnimatorSet();
+                animatorSet.play(anim);
+                callback.accept(AnimatorPlaybackController.wrap(animatorSet, transitionLength));
+            }
+        };
+    }
+
+    @Override
+    public ActivityInitListener createActivityInitListener(
+            BiPredicate<RecentsActivity, Boolean> onInitListener) {
+        return new RecentsActivityTracker(onInitListener);
+    }
+
+    @Nullable
+    @Override
+    public RecentsActivity getCreatedActivity() {
+        return RecentsActivityTracker.getCurrentActivity();
+    }
+
+    @Nullable
+    @Override
+    public RecentsView getVisibleRecentsView() {
+        RecentsActivity activity = getCreatedActivity();
+        if (activity != null && activity.hasWindowFocus()) {
+            return activity.getOverviewPanel();
+        }
+        return null;
+    }
+
+    @Override
+    public boolean switchToRecentsIfVisible(Runnable onCompleteCallback) {
+        return false;
+    }
+
+    @Override
+    public AlphaProperty getAlphaProperty(RecentsActivity activity) {
+        return activity.getDragLayer().getAlphaProperty(0);
+    }
+
+    @Override
+    public int getContainerType() {
+        return LauncherLogProto.ContainerType.SIDELOADED_LAUNCHER;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
new file mode 100644
index 0000000..7078871
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/GoActivityControlHelper.java
@@ -0,0 +1,59 @@
+package com.android.quickstep;
+
+import android.content.Context;
+import android.graphics.Rect;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.DeviceProfile;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Base activity control helper for Go that stubs out most of the functionality that is not needed
+ * for Go.
+ *
+ * @param <T> activity that contains the overview
+ */
+public abstract class GoActivityControlHelper<T extends BaseDraggingActivity> implements
+        ActivityControlHelper<T> {
+
+    @Override
+    public void onTransitionCancelled(T activity, boolean activityVisible) {
+        // Go transitions to overview are all atomic.
+    }
+
+    @Override
+    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context, Rect outRect) {
+        // TODO Implement outRect depending on where the task should animate to.
+        // Go does not support swipe up gesture.
+        return 0;
+    }
+
+    @Override
+    public void onSwipeUpComplete(T activity) {
+        // Go does not support swipe up gesture.
+    }
+
+    @Override
+    public HomeAnimationFactory prepareHomeUI(T activity) {
+        // Go does not support gestures from app to home.
+        return null;
+    }
+
+    @Override
+    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
+        // Go does not support gestures to overview.
+        return null;
+    }
+
+    @Override
+    public boolean shouldMinimizeSplitScreen() {
+        // Go does not support split screen.
+        return true;
+    }
+
+    @Override
+    public boolean isInLiveTileMode() {
+        // Go does not support live tiles.
+        return false;
+    }
+}
diff --git a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
index 8f31e9f..76685f3 100644
--- a/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
+++ b/go/quickstep/src/com/android/quickstep/LauncherActivityControllerHelper.java
@@ -18,55 +18,23 @@
 
 import static com.android.launcher3.LauncherState.OVERVIEW;
 
-import android.content.Context;
-import android.graphics.Rect;
-
-import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Launcher;
 import com.android.launcher3.LauncherAppState;
 import com.android.launcher3.LauncherInitListener;
-import com.android.launcher3.LauncherState;
 import com.android.launcher3.anim.AnimatorPlaybackController;
 import com.android.launcher3.dragndrop.DragLayer;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.MultiValueAlpha.AlphaProperty;
 import com.android.quickstep.views.IconRecentsView;
-import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
 
 import java.util.function.BiPredicate;
 import java.util.function.Consumer;
 
 /**
- * {@link ActivityControlHelper} for the in-launcher recents. As Go does not support most gestures
- * from app to overview/home, most of this class is stubbed out.
+ * {@link ActivityControlHelper} for the in-launcher recents.
  * TODO: Implement the app to overview animation functionality
  */
-public final class LauncherActivityControllerHelper implements ActivityControlHelper<Launcher>{
-
-    @Override
-    public void onTransitionCancelled(Launcher activity, boolean activityVisible) {
-        LauncherState startState = activity.getStateManager().getRestState();
-        activity.getStateManager().goToState(startState, activityVisible);
-    }
-
-    @Override
-    public int getSwipeUpDestinationAndLength(DeviceProfile dp, Context context,
-            Rect outRect) {
-        // TODO Implement outRect depending on where the task should animate to.
-        // Go does not support swipe up gesture.
-        return 0;
-    }
-
-    @Override
-    public void onSwipeUpComplete(Launcher activity) {
-        // Go does not support swipe up gesture.
-    }
-
-    @Override
-    public HomeAnimationFactory prepareHomeUI(Launcher activity) {
-        // Go does not support gestures from app to home.
-        return null;
-    }
+public final class LauncherActivityControllerHelper extends GoActivityControlHelper<Launcher> {
 
     @Override
     public AnimationFactory prepareRecentsUI(Launcher activity,
@@ -126,16 +94,6 @@
     }
 
     @Override
-    public Rect getOverviewWindowBounds(Rect homeBounds, RemoteAnimationTargetCompat target) {
-        return homeBounds;
-    }
-
-    @Override
-    public boolean shouldMinimizeSplitScreen() {
-        return true;
-    }
-
-    @Override
     public AlphaProperty getAlphaProperty(Launcher activity) {
         return activity.getDragLayer().getAlphaProperty(DragLayer.ALPHA_INDEX_SWIPE_UP);
     }
@@ -146,10 +104,4 @@
         return launcher != null ? launcher.getStateManager().getState().containerType
                 : LauncherLogProto.ContainerType.APP;
     }
-
-    @Override
-    public boolean isInLiveTileMode() {
-        // Go does not support live tiles.
-        return false;
-    }
 }
diff --git a/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
new file mode 100644
index 0000000..d9873fe
--- /dev/null
+++ b/go/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import static com.android.systemui.shared.system.ActivityManagerWrapper
+        .CLOSE_SYSTEM_WINDOWS_REASON_RECENTS;
+
+import android.animation.AnimatorSet;
+import android.annotation.TargetApi;
+import android.content.Context;
+import android.os.Build;
+import android.os.SystemClock;
+import android.view.ViewConfiguration;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.MainThreadExecutor;
+import com.android.launcher3.logging.UserEventDispatcher;
+import com.android.launcher3.userevent.nano.LauncherLogProto;
+import com.android.quickstep.ActivityControlHelper.ActivityInitListener;
+import com.android.quickstep.views.IconRecentsView;
+import com.android.systemui.shared.system.ActivityManagerWrapper;
+import com.android.systemui.shared.system.LatencyTrackerCompat;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+
+/**
+ * Helper class to handle various atomic commands for switching between Overview.
+ */
+@TargetApi(Build.VERSION_CODES.P)
+public class OverviewCommandHelper {
+
+    private final Context mContext;
+    private final ActivityManagerWrapper mAM;
+    private final RecentsModel mRecentsModel;
+    private final MainThreadExecutor mMainThreadExecutor;
+    private final OverviewComponentObserver mOverviewComponentObserver;
+
+    private long mLastToggleTime;
+
+    public OverviewCommandHelper(Context context, OverviewComponentObserver observer) {
+        mContext = context;
+        mAM = ActivityManagerWrapper.getInstance();
+        mMainThreadExecutor = new MainThreadExecutor();
+        mRecentsModel = RecentsModel.INSTANCE.get(mContext);
+        mOverviewComponentObserver = observer;
+    }
+
+    public void onOverviewToggle() {
+        // If currently screen pinning, do not enter overview
+        if (mAM.isScreenPinningActive()) {
+            return;
+        }
+
+        mAM.closeSystemWindows(CLOSE_SYSTEM_WINDOWS_REASON_RECENTS);
+        mMainThreadExecutor.execute(new RecentsActivityCommand<>());
+    }
+
+    public void onOverviewShown(boolean triggeredFromAltTab) {
+        mMainThreadExecutor.execute(new ShowRecentsCommand());
+    }
+
+    public void onOverviewHidden() {
+        mMainThreadExecutor.execute(new HideRecentsCommand());
+    }
+
+    public void onTip(int actionType, int viewType) {
+        mMainThreadExecutor.execute(() ->
+                UserEventDispatcher.newInstance(mContext).logActionTip(actionType, viewType));
+    }
+
+    private class ShowRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            return mHelper.getVisibleRecentsView() != null;
+        }
+    }
+
+    private class HideRecentsCommand extends RecentsActivityCommand {
+
+        @Override
+        protected boolean handleCommand(long elapsedTime) {
+            IconRecentsView recents = (IconRecentsView) mHelper.getVisibleRecentsView();
+            if (recents == null) {
+                return false;
+            }
+            //TODO: Launch last running task or go to home.
+            return true;
+        }
+    }
+
+    private class RecentsActivityCommand<T extends BaseDraggingActivity> implements Runnable {
+
+        protected final ActivityControlHelper<T> mHelper;
+        private final long mCreateTime;
+        private final AppToOverviewAnimationProvider<T> mAnimationProvider;
+
+        private final long mToggleClickedTime = SystemClock.uptimeMillis();
+        private boolean mUserEventLogged;
+        private ActivityInitListener mListener;
+
+        public RecentsActivityCommand() {
+            mHelper = mOverviewComponentObserver.getActivityControlHelper();
+            mCreateTime = SystemClock.elapsedRealtime();
+            mAnimationProvider =
+                    new AppToOverviewAnimationProvider<>(mHelper, RecentsModel.getRunningTaskId());
+
+            // Preload the plan
+            mRecentsModel.getTasks(null);
+        }
+
+        @Override
+        public void run() {
+            long elapsedTime = mCreateTime - mLastToggleTime;
+            mLastToggleTime = mCreateTime;
+
+            if (handleCommand(elapsedTime)) {
+                // Command already handled.
+                return;
+            }
+
+            if (mHelper.switchToRecentsIfVisible(null /* onCompleteCallback */)) {
+                // If successfully switched, then return
+                return;
+            }
+
+            // Otherwise, start overview.
+            mListener = mHelper.createActivityInitListener(this::onActivityReady);
+            mListener.registerAndStartActivity(mOverviewComponentObserver.getOverviewIntent(),
+                    this::createWindowAnimation, mContext, mMainThreadExecutor.getHandler(),
+                    mAnimationProvider.getRecentsLaunchDuration());
+        }
+
+        protected boolean handleCommand(long elapsedTime) {
+            IconRecentsView recents = mHelper.getVisibleRecentsView();
+            if (recents != null) {
+                //TODO: Launch next task in icon recents.
+                return true;
+            } else if (elapsedTime < ViewConfiguration.getDoubleTapTimeout()) {
+                // The user tried to launch back into overview too quickly, either after
+                // launching an app, or before overview has actually shown, just ignore for now
+                return true;
+            }
+            return false;
+        }
+
+        private boolean onActivityReady(T activity, Boolean wasVisible) {
+            if (!mUserEventLogged) {
+                activity.getUserEventDispatcher().logActionCommand(
+                        LauncherLogProto.Action.Command.RECENTS_BUTTON,
+                        mHelper.getContainerType(),
+                        LauncherLogProto.ContainerType.TASKSWITCHER);
+                mUserEventLogged = true;
+            }
+            return mAnimationProvider.onActivityReady(activity, wasVisible);
+        }
+
+        private AnimatorSet createWindowAnimation(RemoteAnimationTargetCompat[] targetCompats) {
+            if (LatencyTrackerCompat.isEnabled(mContext)) {
+                LatencyTrackerCompat.logToggleRecents(
+                        (int) (SystemClock.uptimeMillis() - mToggleClickedTime));
+            }
+
+            mListener.unregister();
+
+            return mAnimationProvider.createWindowAnimation(targetCompats);
+        }
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
index 9cb4daf..1042c60 100644
--- a/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
+++ b/quickstep/recents_ui_overrides/src/com/android/launcher3/LauncherAppTransitionManagerImpl.java
@@ -19,8 +19,8 @@
 import static com.android.launcher3.LauncherState.NORMAL;
 import static com.android.launcher3.anim.Interpolators.AGGRESSIVE_EASE;
 import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.quickstep.TaskUtils.findTaskViewToLaunch;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+import static com.android.quickstep.TaskViewUtils.findTaskViewToLaunch;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
 
 import android.animation.Animator;
 import android.animation.AnimatorListenerAdapter;
diff --git a/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
similarity index 97%
rename from quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
index a84d230..1ed1353 100644
--- a/quickstep/src/com/android/quickstep/FallbackActivityControllerHelper.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/FallbackActivityControllerHelper.java
@@ -50,11 +50,7 @@
 public final class FallbackActivityControllerHelper implements
         ActivityControlHelper<RecentsActivity> {
 
-    private final ComponentName mHomeComponent;
-
-    public FallbackActivityControllerHelper(ComponentName homeComponent) {
-        mHomeComponent = homeComponent;
-    }
+    public FallbackActivityControllerHelper() { }
 
     @Override
     public void onTransitionCancelled(RecentsActivity activity, boolean activityVisible) {
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
similarity index 100%
rename from quickstep/src/com/android/quickstep/OverviewCommandHelper.java
rename to quickstep/recents_ui_overrides/src/com/android/quickstep/OverviewCommandHelper.java
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
index 8d7039e..5decc3e 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/RecentsActivity.java
@@ -20,7 +20,7 @@
         .STATUS_BAR_TRANSITION_DURATION;
 import static com.android.launcher3.QuickstepAppTransitionManagerImpl
         .STATUS_BAR_TRANSITION_PRE_DELAY;
-import static com.android.quickstep.TaskUtils.getRecentsWindowAnimator;
+import static com.android.quickstep.TaskViewUtils.getRecentsWindowAnimator;
 import static com.android.quickstep.TaskUtils.taskIsATargetWithMode;
 import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_CLOSING;
 
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
new file mode 100644
index 0000000..4526d67
--- /dev/null
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/TaskViewUtils.java
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2019 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.quickstep;
+
+import static com.android.launcher3.anim.Interpolators.LINEAR;
+import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
+import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
+
+import android.animation.ValueAnimator;
+import android.content.ComponentName;
+import android.graphics.RectF;
+import android.view.View;
+
+import com.android.launcher3.BaseDraggingActivity;
+import com.android.launcher3.ItemInfo;
+import com.android.launcher3.Utilities;
+import com.android.quickstep.util.ClipAnimationHelper;
+import com.android.quickstep.util.MultiValueUpdateListener;
+import com.android.quickstep.util.RemoteAnimationTargetSet;
+import com.android.quickstep.views.RecentsView;
+import com.android.quickstep.views.TaskView;
+import com.android.systemui.shared.recents.model.Task;
+import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
+import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
+
+/**
+ * Utility class for helpful methods related to {@link TaskView} objects and their tasks.
+ */
+public final class TaskViewUtils {
+
+    private TaskViewUtils() {}
+
+    /**
+     * Try to find a TaskView that corresponds with the component of the launched view.
+     *
+     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
+     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
+     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
+     */
+    public static TaskView findTaskViewToLaunch(
+            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
+        RecentsView recentsView = activity.getOverviewPanel();
+        if (v instanceof TaskView) {
+            TaskView taskView = (TaskView) v;
+            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
+        }
+
+        // It's possible that the launched view can still be resolved to a visible task view, check
+        // the task id of the opening task and see if we can find a match.
+        if (v.getTag() instanceof ItemInfo) {
+            ItemInfo itemInfo = (ItemInfo) v.getTag();
+            ComponentName componentName = itemInfo.getTargetComponent();
+            int userId = itemInfo.user.getIdentifier();
+            if (componentName != null) {
+                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
+                    TaskView taskView = recentsView.getTaskViewAt(i);
+                    if (recentsView.isTaskViewVisible(taskView)) {
+                        Task.TaskKey key = taskView.getTask().key;
+                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
+                            return taskView;
+                        }
+                    }
+                }
+            }
+        }
+
+        if (targets == null) {
+            return null;
+        }
+        // Resolve the opening task id
+        int openingTaskId = -1;
+        for (RemoteAnimationTargetCompat target : targets) {
+            if (target.mode == MODE_OPENING) {
+                openingTaskId = target.taskId;
+                break;
+            }
+        }
+
+        // If there is no opening task id, fall back to the normal app icon launch animation
+        if (openingTaskId == -1) {
+            return null;
+        }
+
+        // If the opening task id is not currently visible in overview, then fall back to normal app
+        // icon launch animation
+        TaskView taskView = recentsView.getTaskView(openingTaskId);
+        if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
+            return null;
+        }
+        return taskView;
+    }
+
+    /**
+     * @return Animator that controls the window of the opening targets for the recents launch
+     * animation.
+     */
+    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
+            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
+        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
+                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
+
+        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
+        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
+        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
+
+            // Defer fading out the view until after the app window gets faded in
+            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
+            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
+
+            final RemoteAnimationTargetSet mTargetSet;
+
+            final RectF mThumbnailRect;
+
+            {
+                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
+                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
+
+                inOutHelper.prepareAnimation(true /* isOpening */);
+                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
+                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
+
+                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
+                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
+                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
+            }
+
+            @Override
+            public void onUpdate(float percent) {
+                params.setProgress(1 - percent);
+                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
+                if (!skipViewChanges) {
+                    float scale = taskBounds.width() / mThumbnailRect.width();
+                    v.setScaleX(scale);
+                    v.setScaleY(scale);
+                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
+                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
+                    v.setAlpha(mViewAlpha.value);
+                }
+            }
+        });
+        return appAnimator;
+    }
+}
diff --git a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
index ca8c252..6b3f028 100644
--- a/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
+++ b/quickstep/recents_ui_overrides/src/com/android/quickstep/fallback/RecentsRootView.java
@@ -30,13 +30,14 @@
 
 public class RecentsRootView extends BaseDragLayer<RecentsActivity> {
 
+    private static final int MIN_SIZE = 10;
     private final RecentsActivity mActivity;
 
-    private final Point mLastKnownSize = new Point(10, 10);
+    private final Point mLastKnownSize = new Point(MIN_SIZE, MIN_SIZE);
 
     public RecentsRootView(Context context, AttributeSet attrs) {
         super(context, attrs, 1 /* alphaChannelCount */);
-        mActivity = (RecentsActivity) BaseActivity.fromContext(context);
+        mActivity = BaseActivity.fromContext(context);
         setSystemUiVisibility(SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
                 | SYSTEM_UI_FLAG_LAYOUT_HIDE_NAVIGATION
                 | SYSTEM_UI_FLAG_LAYOUT_STABLE);
@@ -53,8 +54,8 @@
     @Override
     protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
         // Check size changes before the actual measure, to avoid multiple measure calls.
-        int width = MeasureSpec.getSize(widthMeasureSpec);
-        int height = MeasureSpec.getSize(heightMeasureSpec);
+        int width = Math.max(MIN_SIZE, MeasureSpec.getSize(widthMeasureSpec));
+        int height = Math.max(MIN_SIZE, MeasureSpec.getSize(heightMeasureSpec));
         if (mLastKnownSize.x != width || mLastKnownSize.y != height) {
             mLastKnownSize.set(width, height);
             mActivity.onRootViewSizeChanged();
diff --git a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
index b6741f4..724a492 100644
--- a/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
+++ b/quickstep/src/com/android/quickstep/OverviewComponentObserver.java
@@ -96,7 +96,7 @@
         } else {
             // The default home app is a different launcher. Use the fallback Overview instead.
             overviewComponent = new ComponentName(mContext, RecentsActivity.class);
-            mActivityControlHelper = new FallbackActivityControllerHelper(defaultHome);
+            mActivityControlHelper = new FallbackActivityControllerHelper();
             overviewIntentCategory = Intent.CATEGORY_DEFAULT;
 
             // User's default home app can change as a result of package updates of this app (such
diff --git a/quickstep/src/com/android/quickstep/TaskUtils.java b/quickstep/src/com/android/quickstep/TaskUtils.java
index c2777e7..5f76ca7 100644
--- a/quickstep/src/com/android/quickstep/TaskUtils.java
+++ b/quickstep/src/com/android/quickstep/TaskUtils.java
@@ -16,44 +16,30 @@
 
 package com.android.quickstep;
 
-import static com.android.launcher3.anim.Interpolators.LINEAR;
-import static com.android.launcher3.anim.Interpolators.TOUCH_RESPONSE_INTERPOLATOR;
-import static com.android.systemui.shared.system.RemoteAnimationTargetCompat.MODE_OPENING;
-
-import android.animation.ValueAnimator;
 import android.content.ComponentName;
 import android.content.Context;
 import android.content.pm.ApplicationInfo;
 import android.content.pm.PackageManager;
-import android.graphics.RectF;
 import android.os.UserHandle;
 import android.util.Log;
-import android.view.View;
 
-import com.android.launcher3.BaseDraggingActivity;
-import com.android.launcher3.ItemInfo;
-import com.android.launcher3.Utilities;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.compat.UserManagerCompat;
 import com.android.launcher3.util.ComponentKey;
-import com.android.quickstep.util.ClipAnimationHelper;
-import com.android.quickstep.util.MultiValueUpdateListener;
-import com.android.quickstep.util.RemoteAnimationTargetSet;
-import com.android.quickstep.views.RecentsView;
-import com.android.quickstep.views.TaskView;
 import com.android.systemui.shared.recents.model.Task;
 import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
-import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat;
 
 import java.util.List;
 
 /**
  * Contains helpful methods for retrieving data from {@link Task}s.
  */
-public class TaskUtils {
+public final class TaskUtils {
 
     private static final String TAG = "TaskUtils";
 
+    private TaskUtils() {}
+
     /**
      * TODO: remove this once we switch to getting the icon and label from IconCache.
      */
@@ -79,117 +65,6 @@
     }
 
 
-    /**
-     * Try to find a TaskView that corresponds with the component of the launched view.
-     *
-     * If this method returns a non-null TaskView, it will be used in composeRecentsLaunchAnimation.
-     * Otherwise, we will assume we are using a normal app transition, but it's possible that the
-     * opening remote target (which we don't get until onAnimationStart) will resolve to a TaskView.
-     */
-    public static TaskView findTaskViewToLaunch(
-            BaseDraggingActivity activity, View v, RemoteAnimationTargetCompat[] targets) {
-        RecentsView recentsView = activity.getOverviewPanel();
-        if (v instanceof TaskView) {
-            TaskView taskView = (TaskView) v;
-            return recentsView.isTaskViewVisible(taskView) ? taskView : null;
-        }
-
-        // It's possible that the launched view can still be resolved to a visible task view, check
-        // the task id of the opening task and see if we can find a match.
-        if (v.getTag() instanceof ItemInfo) {
-            ItemInfo itemInfo = (ItemInfo) v.getTag();
-            ComponentName componentName = itemInfo.getTargetComponent();
-            int userId = itemInfo.user.getIdentifier();
-            if (componentName != null) {
-                for (int i = 0; i < recentsView.getTaskViewCount(); i++) {
-                    TaskView taskView = recentsView.getTaskViewAt(i);
-                    if (recentsView.isTaskViewVisible(taskView)) {
-                        Task.TaskKey key = taskView.getTask().key;
-                        if (componentName.equals(key.getComponent()) && userId == key.userId) {
-                            return taskView;
-                        }
-                    }
-                }
-            }
-        }
-
-        if (targets == null) {
-            return null;
-        }
-        // Resolve the opening task id
-        int openingTaskId = -1;
-        for (RemoteAnimationTargetCompat target : targets) {
-            if (target.mode == MODE_OPENING) {
-                openingTaskId = target.taskId;
-                break;
-            }
-        }
-
-        // If there is no opening task id, fall back to the normal app icon launch animation
-        if (openingTaskId == -1) {
-            return null;
-        }
-
-        // If the opening task id is not currently visible in overview, then fall back to normal app
-        // icon launch animation
-        TaskView taskView = recentsView.getTaskView(openingTaskId);
-        if (taskView == null || !recentsView.isTaskViewVisible(taskView)) {
-            return null;
-        }
-        return taskView;
-    }
-
-    /**
-     * @return Animator that controls the window of the opening targets for the recents launch
-     * animation.
-     */
-    public static ValueAnimator getRecentsWindowAnimator(TaskView v, boolean skipViewChanges,
-            RemoteAnimationTargetCompat[] targets, final ClipAnimationHelper inOutHelper) {
-        ClipAnimationHelper.TransformParams params = new ClipAnimationHelper.TransformParams()
-                .setSyncTransactionApplier(new SyncRtSurfaceTransactionApplierCompat(v));
-        
-        final ValueAnimator appAnimator = ValueAnimator.ofFloat(0, 1);
-        appAnimator.setInterpolator(TOUCH_RESPONSE_INTERPOLATOR);
-        appAnimator.addUpdateListener(new MultiValueUpdateListener() {
-
-            // Defer fading out the view until after the app window gets faded in
-            final FloatProp mViewAlpha = new FloatProp(1f, 0f, 75, 75, LINEAR);
-            final FloatProp mTaskAlpha = new FloatProp(0f, 1f, 0, 75, LINEAR);
-
-            final RemoteAnimationTargetSet mTargetSet;
-
-            final RectF mThumbnailRect;
-
-            {
-                mTargetSet = new RemoteAnimationTargetSet(targets, MODE_OPENING);
-                inOutHelper.setTaskAlphaCallback((t, alpha) -> mTaskAlpha.value);
-
-                inOutHelper.prepareAnimation(true /* isOpening */);
-                inOutHelper.fromTaskThumbnailView(v.getThumbnail(), (RecentsView) v.getParent(),
-                        mTargetSet.apps.length == 0 ? null : mTargetSet.apps[0]);
-
-                mThumbnailRect = new RectF(inOutHelper.getTargetRect());
-                mThumbnailRect.offset(-v.getTranslationX(), -v.getTranslationY());
-                Utilities.scaleRectFAboutCenter(mThumbnailRect, 1 / v.getScaleX());
-            }
-
-            @Override
-            public void onUpdate(float percent) {
-                params.setProgress(1 - percent);
-                RectF taskBounds = inOutHelper.applyTransform(mTargetSet, params);
-                if (!skipViewChanges) {
-                    float scale = taskBounds.width() / mThumbnailRect.width();
-                    v.setScaleX(scale);
-                    v.setScaleY(scale);
-                    v.setTranslationX(taskBounds.centerX() - mThumbnailRect.centerX());
-                    v.setTranslationY(taskBounds.centerY() - mThumbnailRect.centerY());
-                    v.setAlpha(mViewAlpha.value);
-                }
-            }
-        });
-        return appAnimator;
-    }
-
     public static boolean taskIsATargetWithMode(RemoteAnimationTargetCompat[] targets,
             int taskId, int mode) {
         for (RemoteAnimationTargetCompat target : targets) {
diff --git a/src/com/android/launcher3/DeviceProfile.java b/src/com/android/launcher3/DeviceProfile.java
index 7aea870..01535b0 100644
--- a/src/com/android/launcher3/DeviceProfile.java
+++ b/src/com/android/launcher3/DeviceProfile.java
@@ -322,7 +322,7 @@
         // Workspace
         final boolean isVerticalLayout = isVerticalBarLayout();
         float invIconSizePx = isVerticalLayout ? inv.landscapeIconSize : inv.iconSize;
-        iconSizePx = (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale);
+        iconSizePx = Math.max(1, (int) (Utilities.pxFromDp(invIconSizePx, dm) * scale));
         iconTextSizePx = (int) (Utilities.pxFromSp(inv.iconTextSize, dm) * scale);
         iconDrawablePaddingPx = (int) (iconDrawablePaddingOriginalPx * scale);
 
diff --git a/src/com/android/launcher3/PagedView.java b/src/com/android/launcher3/PagedView.java
index 4b7d8b4..00da6fa 100644
--- a/src/com/android/launcher3/PagedView.java
+++ b/src/com/android/launcher3/PagedView.java
@@ -1024,8 +1024,8 @@
             return;
         }
 
-        if (amount < 0) {
-            super.scrollTo(amount, getScrollY());
+        if (overScrollAmount < 0) {
+            super.scrollTo(overScrollAmount, getScrollY());
         } else {
             super.scrollTo(mMaxScrollX + overScrollAmount, getScrollY());
         }