Merge "Don't rely on intent to call back from activity tracker" into sc-dev
diff --git a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
index 3a1a2f7..3a7d821 100644
--- a/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
+++ b/quickstep/src/com/android/launcher3/hybridhotseat/HotseatEduActivity.java
@@ -21,6 +21,7 @@
 import android.os.Bundle;
 
 import com.android.launcher3.BaseActivity;
+import com.android.launcher3.Launcher;
 import com.android.launcher3.uioverrides.QuickstepLauncher;
 import com.android.launcher3.util.ActivityTracker;
 
@@ -37,7 +38,8 @@
                 .addCategory(Intent.CATEGORY_HOME)
                 .setPackage(getPackageName())
                 .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        new HotseatActivityTracker<>().addToIntent(homeIntent);
+
+        Launcher.ACTIVITY_TRACKER.registerCallback(new HotseatActivityTracker());
         startActivity(homeIntent);
         finish();
     }
diff --git a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
index 8f42993..a9dacee 100644
--- a/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
+++ b/quickstep/src/com/android/quickstep/AbsSwipeUpHandler.java
@@ -349,6 +349,13 @@
         }
 
         if (mActivity != null) {
+            if (mStateCallback.hasStates(STATE_GESTURE_COMPLETED)) {
+                // If the activity has restarted between setting the page scroll settling callback
+                // and actually receiving the callback, just mark the gesture completed
+                mGestureState.setState(STATE_RECENTS_SCROLLING_FINISHED);
+                return true;
+            }
+
             // The launcher may have been recreated as a result of device rotation.
             int oldState = mStateCallback.getState() & ~LAUNCHER_UI_STATES;
             initStateCallbacks();
@@ -1717,13 +1724,12 @@
 
     /**
      * Registers a callback to run when the activity is ready.
-     * @param intent The intent that will be used to start the activity if it doesn't exist already.
      */
-    public void initWhenReady(Intent intent) {
+    public void initWhenReady() {
         // Preload the plan
         RecentsModel.INSTANCE.get(mContext).getTasks(null);
 
-        mActivityInitListener.register(intent);
+        mActivityInitListener.register();
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
index dbdd75f..2beef0a 100644
--- a/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
+++ b/quickstep/src/com/android/quickstep/OverviewCommandHelper.java
@@ -184,9 +184,7 @@
                 .newHandler(gestureState, cmd.createTime);
         interactionHandler.setGestureEndCallback(
                 () -> onTransitionComplete(cmd, interactionHandler));
-
-        Intent intent = new Intent(interactionHandler.getLaunchIntent());
-        interactionHandler.initWhenReady(intent);
+        interactionHandler.initWhenReady();
 
         RecentsAnimationListener recentAnimListener = new RecentsAnimationListener() {
             @Override
@@ -212,6 +210,7 @@
             cmd.mActiveCallbacks.addListener(recentAnimListener);
             mTaskAnimationManager.notifyRecentsAnimationState(recentAnimListener);
         } else {
+            Intent intent = new Intent(interactionHandler.getLaunchIntent());
             intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, gestureState.getGestureId());
             cmd.mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(
                     gestureState, intent, interactionHandler);
diff --git a/quickstep/src/com/android/quickstep/RecentsActivity.java b/quickstep/src/com/android/quickstep/RecentsActivity.java
index a70cd0b..3d66823 100644
--- a/quickstep/src/com/android/quickstep/RecentsActivity.java
+++ b/quickstep/src/com/android/quickstep/RecentsActivity.java
@@ -141,7 +141,7 @@
     @Override
     protected void onNewIntent(Intent intent) {
         super.onNewIntent(intent);
-        ACTIVITY_TRACKER.handleNewIntent(this, intent);
+        ACTIVITY_TRACKER.handleNewIntent(this);
     }
 
     /**
diff --git a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
index 9878d45..725c7c4 100644
--- a/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
+++ b/quickstep/src/com/android/quickstep/inputconsumers/OtherActivityInputConsumer.java
@@ -389,8 +389,7 @@
         mInteractionHandler = mHandlerFactory.newHandler(mGestureState, touchTimeMs);
         mInteractionHandler.setGestureEndCallback(this::onInteractionGestureFinished);
         mMotionPauseDetector.setOnMotionPauseListener(mInteractionHandler.getMotionPauseListener());
-        Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
-        mInteractionHandler.initWhenReady(intent);
+        mInteractionHandler.initWhenReady();
 
         if (mTaskAnimationManager.isRecentsAnimationRunning()) {
             mActiveCallbacks = mTaskAnimationManager.continueRecentsAnimation(mGestureState);
@@ -398,6 +397,7 @@
             mTaskAnimationManager.notifyRecentsAnimationState(mInteractionHandler);
             notifyGestureStarted(true /*isLikelyToStartNewTask*/);
         } else {
+            Intent intent = new Intent(mInteractionHandler.getLaunchIntent());
             intent.putExtra(INTENT_EXTRA_LOG_TRACE_ID, mGestureState.getGestureId());
             mActiveCallbacks = mTaskAnimationManager.startRecentsAnimation(mGestureState, intent,
                     mInteractionHandler);
diff --git a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
index dfb8c1d..b9879ab 100644
--- a/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
+++ b/quickstep/src/com/android/quickstep/util/ActivityInitListener.java
@@ -26,7 +26,8 @@
 
 import java.util.function.BiPredicate;
 
-public class ActivityInitListener<T extends BaseActivity> implements SchedulerCallback<T> {
+public class ActivityInitListener<T extends BaseActivity> implements
+        SchedulerCallback<T> {
 
     private BiPredicate<T, Boolean> mOnInitListener;
     private final ActivityTracker<T> mActivityTracker;
@@ -47,6 +48,7 @@
     @Override
     public final boolean init(T activity, boolean alreadyOnHome) {
         if (!mIsRegistered) {
+            // Don't receive any more updates
             return false;
         }
         return handleInit(activity, alreadyOnHome);
@@ -59,18 +61,17 @@
     /**
      * Registers the activity-created listener. If the activity is already created, then the
      * callback provided in the constructor will be called synchronously.
-     * @param intent The intent that will be used to initialize the activity, if the activity
-     *               doesn't already exist. We add the callback as an extra on this intent.
      */
-    public void register(Intent intent) {
+    public void register() {
         mIsRegistered = true;
-        mActivityTracker.runCallbackWhenActivityExists(this, intent);
+        mActivityTracker.registerCallback(this);
     }
 
     /**
      * After calling this, we won't {@link #init} even when the activity is ready.
      */
     public void unregister() {
+        mActivityTracker.unregisterCallback(this);
         mIsRegistered = false;
         mOnInitListener = null;
     }
@@ -82,9 +83,9 @@
      */
     public void registerAndStartActivity(Intent intent, RemoteAnimationProvider animProvider,
             Context context, Handler handler, long duration) {
-        mIsRegistered = true;
+        register();
 
         Bundle options = animProvider.toActivityOptions(handler, duration, context).toBundle();
-        context.startActivity(addToIntent(new Intent(intent)), options);
+        context.startActivity(new Intent(intent), options);
     }
 }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index 30fe6ca..c5d280d 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -1478,7 +1478,7 @@
         boolean shouldMoveToDefaultScreen = alreadyOnHome && isInState(NORMAL)
                 && AbstractFloatingView.getTopOpenView(this) == null;
         boolean isActionMain = Intent.ACTION_MAIN.equals(intent.getAction());
-        boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this, intent);
+        boolean internalStateHandled = ACTIVITY_TRACKER.handleNewIntent(this);
         hideKeyboard();
         if (isActionMain) {
             if (!internalStateHandled) {
diff --git a/src/com/android/launcher3/dragndrop/AddItemActivity.java b/src/com/android/launcher3/dragndrop/AddItemActivity.java
index 01f1282..1503167 100644
--- a/src/com/android/launcher3/dragndrop/AddItemActivity.java
+++ b/src/com/android/launcher3/dragndrop/AddItemActivity.java
@@ -212,7 +212,7 @@
                         .addCategory(Intent.CATEGORY_HOME)
                         .setPackage(getPackageName())
                         .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
-        Launcher.ACTIVITY_TRACKER.runCallbackWhenActivityExists(listener, homeIntent);
+        Launcher.ACTIVITY_TRACKER.registerCallback(listener);
         startActivity(homeIntent,
                 ActivityOptions.makeCustomAnimation(this, 0, android.R.anim.fade_out)
                         .toBundle());
diff --git a/src/com/android/launcher3/states/RotationHelper.java b/src/com/android/launcher3/states/RotationHelper.java
index 5832711..87871b1 100644
--- a/src/com/android/launcher3/states/RotationHelper.java
+++ b/src/com/android/launcher3/states/RotationHelper.java
@@ -25,6 +25,7 @@
 import com.android.launcher3.BaseActivity;
 import com.android.launcher3.DeviceProfile;
 import com.android.launcher3.Utilities;
+import com.android.launcher3.util.ActivityTracker;
 import com.android.launcher3.util.UiThreadHelper;
 
 /**
@@ -50,7 +51,7 @@
 
     /**
      * Rotation request made by
-     * {@link com.android.launcher3.util.ActivityTracker.SchedulerCallback}.
+     * {@link ActivityTracker.SchedulerCallback}.
      * This supersedes any other request.
      */
     private int mStateHandlerRequest = REQUEST_NONE;
diff --git a/src/com/android/launcher3/util/ActivityTracker.java b/src/com/android/launcher3/util/ActivityTracker.java
index b5b9c2f..7af1a13 100644
--- a/src/com/android/launcher3/util/ActivityTracker.java
+++ b/src/com/android/launcher3/util/ActivityTracker.java
@@ -15,15 +15,14 @@
  */
 package com.android.launcher3.util;
 
-import android.content.Intent;
-import android.os.Bundle;
-import android.os.IBinder;
-
 import androidx.annotation.Nullable;
 
 import com.android.launcher3.BaseActivity;
 
 import java.lang.ref.WeakReference;
+import java.util.ArrayList;
+import java.util.HashSet;
+import java.util.concurrent.CopyOnWriteArrayList;
 
 /**
  * Helper class to statically track activity creation
@@ -32,8 +31,7 @@
 public final class ActivityTracker<T extends BaseActivity> {
 
     private WeakReference<T> mCurrentActivity = new WeakReference<>(null);
-
-    private static final String EXTRA_SCHEDULER_CALLBACK = "launcher.scheduler_callback";
+    private CopyOnWriteArrayList<SchedulerCallback<T>> mCallbacks = new CopyOnWriteArrayList<>();
 
     @Nullable
     public <R extends T> R getCreatedActivity() {
@@ -47,43 +45,50 @@
     }
 
     /**
-     * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the activity is ready.
-     * If the activity is already created, this is called immediately, otherwise we add the
-     * callback as an extra on the intent, and will call init() when we get handleIntent().
+     * Call {@link SchedulerCallback#init(BaseActivity, boolean)} when the
+     * activity is ready. If the activity is already created, this is called immediately.
+     *
+     * The tracker maintains a strong ref to the callback, so it is up to the caller to return
+     * {@code false} in the callback OR to unregister the callback explicitly.
+     *
      * @param callback The callback to call init() on when the activity is ready.
-     * @param intent The intent that will be used to initialize the activity, if the activity
-     *               doesn't already exist. We add the callback as an extra on this intent.
      */
-    public void runCallbackWhenActivityExists(SchedulerCallback<T> callback, Intent intent) {
+    public void registerCallback(SchedulerCallback<T> callback) {
         T activity = mCurrentActivity.get();
+        mCallbacks.add(callback);
         if (activity != null) {
-            callback.init(activity, activity.isStarted());
-        } else {
-            callback.addToIntent(intent);
+            if (!callback.init(activity, activity.isStarted())) {
+                unregisterCallback(callback);
+            }
         }
     }
 
+    /**
+     * Unregisters a registered callback.
+     */
+    public void unregisterCallback(SchedulerCallback<T> callback) {
+        mCallbacks.remove(callback);
+    }
+
     public boolean handleCreate(T activity) {
         mCurrentActivity = new WeakReference<>(activity);
-        return handleIntent(activity, activity.getIntent(), false);
+        return handleIntent(activity, false /* alreadyOnHome */);
     }
 
-    public boolean handleNewIntent(T activity, Intent intent) {
-        return handleIntent(activity, intent, activity.isStarted());
+    public boolean handleNewIntent(T activity) {
+        return handleIntent(activity, activity.isStarted());
     }
 
-    private boolean handleIntent(T activity, Intent intent, boolean alreadyOnHome) {
-        if (intent != null && intent.getExtras() != null) {
-            IBinder stateBinder = intent.getExtras().getBinder(EXTRA_SCHEDULER_CALLBACK);
-            SchedulerCallback<T> handler = ObjectWrapper.unwrap(stateBinder);
-            if (handler != null) {
-                if (!handler.init(activity, alreadyOnHome)) {
-                    intent.getExtras().remove(EXTRA_SCHEDULER_CALLBACK);
-                }
-                return true;
+    private boolean handleIntent(T activity, boolean alreadyOnHome) {
+        boolean handled = false;
+        for (SchedulerCallback<T> cb : mCallbacks) {
+            if (!cb.init(activity, alreadyOnHome)) {
+                // Callback doesn't want any more updates
+                unregisterCallback(cb);
             }
+            handled = true;
         }
-        return false;
+        return handled;
     }
 
     public interface SchedulerCallback<T extends BaseActivity> {
@@ -94,17 +99,5 @@
          * @return Whether to continue receiving callbacks (i.e. if the activity is recreated).
          */
         boolean init(T activity, boolean alreadyOnHome);
-
-        /**
-         * Adds this callback as an extra on the intent, so we can retrieve it in handleIntent() and
-         * call {@link #init}. The intent should be used to start the activity after calling this
-         * method in order for us to get handleIntent().
-         */
-        default Intent addToIntent(Intent intent) {
-            Bundle extras = new Bundle();
-            extras.putBinder(EXTRA_SCHEDULER_CALLBACK, ObjectWrapper.wrap(this));
-            intent.putExtras(extras);
-            return intent;
-        }
     }
 }