Adding support for listening to end callbacks when launching an intent from Launcher

Also moving various state handling to these end callbacks enstead of relying on resume

Bug: 265134143
Test: Verified that the end callback is received
Change-Id: I326a99c80154d244c0e49f678717c476602b6240
diff --git a/src/com/android/launcher3/BaseDraggingActivity.java b/src/com/android/launcher3/BaseDraggingActivity.java
index 45b03c2..8876a1b 100644
--- a/src/com/android/launcher3/BaseDraggingActivity.java
+++ b/src/com/android/launcher3/BaseDraggingActivity.java
@@ -156,6 +156,13 @@
     }
 
     @Override
+    public ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+        ActivityOptionsWrapper wrapper = super.makeDefaultActivityOptions(splashScreenStyle);
+        addOnResumeCallback(wrapper.onEndCallback::executeAllAndDestroy);
+        return wrapper;
+    }
+
+    @Override
     protected void onStart() {
         super.onStart();
 
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index ffd56cc..faf5c6a 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2154,30 +2154,38 @@
     }
 
     @Override
-    public boolean startActivitySafely(View v, Intent intent, ItemInfo item) {
+    public RunnableList startActivitySafely(View v, Intent intent, ItemInfo item) {
         if (!hasBeenResumed()) {
+            RunnableList result = new RunnableList();
             // Workaround an issue where the WM launch animation is clobbered when finishing the
             // recents animation into launcher. Defer launching the activity until Launcher is
             // next resumed.
-            addOnResumeCallback(() -> startActivitySafely(v, intent, item));
+            addOnResumeCallback(() -> {
+                RunnableList actualResult = startActivitySafely(v, intent, item);
+                if (actualResult != null) {
+                    actualResult.add(result::executeAllAndDestroy);
+                } else {
+                    result.executeAllAndDestroy();
+                }
+            });
             if (mOnDeferredActivityLaunchCallback != null) {
                 mOnDeferredActivityLaunchCallback.run();
                 mOnDeferredActivityLaunchCallback = null;
             }
-            return true;
+            return result;
         }
 
-        boolean success = super.startActivitySafely(v, intent, item);
-        if (success && v instanceof BubbleTextView) {
+        RunnableList result = super.startActivitySafely(v, intent, item);
+        if (result != null && v instanceof BubbleTextView) {
             // This is set to the view that launched the activity that navigated the user away
             // from launcher. Since there is no callback for when the activity has finished
             // launching, enable the press state and keep this reference to reset the press
             // state when we return to launcher.
             BubbleTextView btv = (BubbleTextView) v;
             btv.setStayPressed(true);
-            addOnResumeCallback(() -> btv.setStayPressed(false));
+            result.add(() -> btv.setStayPressed(false));
         }
-        return success;
+        return result;
     }
 
     boolean isHotseatLayout(View layout) {
diff --git a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
index 714304b..64fd237 100644
--- a/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
+++ b/src/com/android/launcher3/allapps/search/DefaultSearchAdapterProvider.java
@@ -62,7 +62,8 @@
         if (mHighlightedView instanceof BubbleTextView
                 && mHighlightedView.getTag() instanceof ItemInfo) {
             ItemInfo itemInfo = (ItemInfo) mHighlightedView.getTag();
-            return mLauncher.startActivitySafely(mHighlightedView, itemInfo.getIntent(), itemInfo);
+            return mLauncher.startActivitySafely(
+                    mHighlightedView, itemInfo.getIntent(), itemInfo) != null;
         }
         return false;
     }
diff --git a/src/com/android/launcher3/views/ActivityContext.java b/src/com/android/launcher3/views/ActivityContext.java
index 31b1934..515a2d8 100644
--- a/src/com/android/launcher3/views/ActivityContext.java
+++ b/src/com/android/launcher3/views/ActivityContext.java
@@ -15,6 +15,9 @@
  */
 package com.android.launcher3.views;
 
+import static android.window.SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR;
+
+import static com.android.launcher3.LauncherSettings.Animation.DEFAULT_NO_ICON;
 import static com.android.launcher3.logging.KeyboardStateManager.KeyboardState.HIDE;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_ALLAPPS_KEYBOARD_CLOSED;
 import static com.android.launcher3.logging.StatsLogManager.LauncherEvent.LAUNCHER_APP_LAUNCH_TAP;
@@ -45,7 +48,6 @@
 import android.view.WindowInsetsController;
 import android.view.inputmethod.InputMethodManager;
 import android.widget.Toast;
-import android.window.SplashScreen;
 
 import androidx.annotation.NonNull;
 import androidx.annotation.Nullable;
@@ -289,27 +291,27 @@
         }
     }
 
-
     /**
      * Sends a pending intent animating from a view.
      *
      * @param v View to animate.
      * @param intent The pending intent being launched.
      * @param item Item associated with the view.
-     * @return {@code true} if the intent is sent successfully.
+     * @return RunnableList for listening for animation finish if the activity was properly
+     *         or started, {@code null} if the launch finished
      */
-    default boolean sendPendingIntentWithAnimation(
+    default RunnableList sendPendingIntentWithAnimation(
             @NonNull View v, PendingIntent intent, @Nullable ItemInfo item) {
-        Bundle optsBundle = getActivityLaunchOptions(v, item).toBundle();
+        ActivityOptionsWrapper options = getActivityLaunchOptions(v, item);
         try {
-            intent.send(null, 0, null, null, null, null, optsBundle);
-            return true;
+            intent.send(null, 0, null, null, null, null, options.toBundle());
+            return options.onEndCallback;
         } catch (PendingIntent.CanceledException e) {
             Toast.makeText(v.getContext(),
                     v.getContext().getResources().getText(R.string.shortcut_not_available),
                     Toast.LENGTH_SHORT).show();
         }
-        return false;
+        return null;
     }
 
     /**
@@ -318,28 +320,23 @@
      * @param v View starting the activity.
      * @param intent Base intent being launched.
      * @param item Item associated with the view.
-     * @return {@code true} if the activity starts successfully.
+     * @return RunnableList for listening for animation finish if the activity was properly
+     *         or started, {@code null} if the launch finished
      */
-    default boolean startActivitySafely(
+    default RunnableList startActivitySafely(
             View v, Intent intent, @Nullable ItemInfo item) {
         Preconditions.assertUIThread();
         Context context = (Context) this;
         if (isAppBlockedForSafeMode() && !PackageManagerHelper.isSystemApp(context, intent)) {
             Toast.makeText(context, R.string.safemode_shortcut_error, Toast.LENGTH_SHORT).show();
-            return false;
+            return null;
         }
 
-        Bundle optsBundle = null;
-        if (v != null) {
-            optsBundle = getActivityLaunchOptions(v, item).toBundle();
-        } else if (android.os.Build.VERSION.SDK_INT >= 33
-                && item != null
-                && item.animationType == LauncherSettings.Animation.DEFAULT_NO_ICON) {
-            optsBundle = ActivityOptions.makeBasic()
-                    .setSplashScreenStyle(SplashScreen.SPLASH_SCREEN_STYLE_SOLID_COLOR).toBundle();
-        }
+        ActivityOptionsWrapper options = v != null ? getActivityLaunchOptions(v, item)
+                : makeDefaultActivityOptions(item != null && item.animationType == DEFAULT_NO_ICON
+                        ? SPLASH_SCREEN_STYLE_SOLID_COLOR : -1 /* SPLASH_SCREEN_STYLE_UNDEFINED */);
         UserHandle user = item == null ? null : item.user;
-
+        Bundle optsBundle = options.toBundle();
         // Prepare intent
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         if (v != null) {
@@ -364,12 +361,12 @@
                 InstanceId instanceId = new InstanceIdSequence().newInstanceId();
                 logAppLaunch(getStatsLogManager(), item, instanceId);
             }
-            return true;
+            return options.onEndCallback;
         } catch (NullPointerException | ActivityNotFoundException | SecurityException e) {
             Toast.makeText(context, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
             Log.e(TAG, "Unable to launch. tag=" + item + " intent=" + intent, e);
         }
-        return false;
+        return null;
     }
 
     /** Returns {@code true} if an app launch is blocked due to safe mode. */
@@ -417,6 +414,17 @@
     }
 
     /**
+     * Creates a default activity option and we do not want association with any launcher element.
+     */
+    default ActivityOptionsWrapper makeDefaultActivityOptions(int splashScreenStyle) {
+        ActivityOptions options = ActivityOptions.makeBasic();
+        if (Utilities.ATLEAST_T) {
+            options.setSplashScreenStyle(splashScreenStyle);
+        }
+        return new ActivityOptionsWrapper(options, new RunnableList());
+    }
+
+    /**
      * Safely launches an intent for a shortcut.
      *
      * @param intent Intent to start.
diff --git a/src/com/android/launcher3/views/OptionsPopupView.java b/src/com/android/launcher3/views/OptionsPopupView.java
index 64ad390..ba6165f 100644
--- a/src/com/android/launcher3/views/OptionsPopupView.java
+++ b/src/com/android/launcher3/views/OptionsPopupView.java
@@ -281,7 +281,7 @@
         if (!TextUtils.isEmpty(pickerPackage)) {
             intent.setPackage(pickerPackage);
         }
-        return launcher.startActivitySafely(v, intent, placeholderInfo(intent));
+        return launcher.startActivitySafely(v, intent, placeholderInfo(intent)) != null;
     }
 
     static WorkspaceItemInfo placeholderInfo(Intent intent) {