Adding support for result callback when starting uninstall-application activity

Change-Id: Ieaca4fbd0ae0156f24c8863ccbef61d4d6d30ba1
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index d444640..191becf 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -16,12 +16,19 @@
 
 package com.android.launcher3;
 
+import android.content.ActivityNotFoundException;
 import android.content.ComponentName;
 import android.content.Context;
 import android.util.AttributeSet;
+import android.util.Log;
+import android.widget.Toast;
+
+import com.android.launcher3.compat.LauncherAppsCompat;
 
 public class InfoDropTarget extends UninstallDropTarget {
 
+    private static final String TAG = "InfoDropTarget";
+
     public InfoDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
     }
@@ -39,10 +46,19 @@
         setDrawable(R.drawable.ic_info_launcher);
     }
 
+    @Override
+    void completeDrop(DragObject d) {
+        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
+                ? (DropTargetResultCallback) d.dragSource : null;
+        startDetailsActivityForInfo(d.dragInfo, mLauncher, callback);
+    }
+
     /**
      * @return Whether the activity was started.
      */
-    public static boolean startDetailsActivityForInfo(ItemInfo info, Launcher launcher) {
+    public static boolean startDetailsActivityForInfo(
+            ItemInfo info, Launcher launcher, DropTargetResultCallback callback) {
+        boolean result = false;
         ComponentName componentName = null;
         if (info instanceof AppInfo) {
             componentName = ((AppInfo) info).componentName;
@@ -54,23 +70,28 @@
             componentName = ((LauncherAppWidgetInfo) info).providerName;
         }
         if (componentName != null) {
-            launcher.startApplicationDetailsActivity(componentName, info.user);
-            return true;
+            try {
+                LauncherAppsCompat.getInstance(launcher)
+                        .showAppDetailsForProfile(componentName, info.user);
+                result = true;
+            } catch (SecurityException | ActivityNotFoundException e) {
+                Toast.makeText(launcher, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
+                Log.e(TAG, "Unable to launch settings", e);
+            }
         }
-        return false;
-    }
 
-    @Override
-    protected boolean startActivityWithUninstallAffordance(DragObject d) {
-        return startDetailsActivityForInfo(d.dragInfo, mLauncher);
+        if (callback != null) {
+            sendUninstallResult(launcher, result, componentName, info.user, callback);
+        }
+        return result;
     }
 
     @Override
     protected boolean supportsDrop(DragSource source, ItemInfo info) {
-        return source.supportsAppInfoDropTarget() && supportsDrop(getContext(), info);
+        return source.supportsAppInfoDropTarget() && supportsDrop(info);
     }
 
-    public static boolean supportsDrop(Context context, ItemInfo info) {
+    public static boolean supportsDrop(ItemInfo info) {
         return info instanceof AppInfo || info instanceof ShortcutInfo
                 || info instanceof PendingAddItemInfo || info instanceof LauncherAppWidgetInfo;
     }
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index fd614a4..60211e2 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -57,7 +57,6 @@
 import android.graphics.Rect;
 import android.graphics.drawable.ColorDrawable;
 import android.graphics.drawable.Drawable;
-import android.net.Uri;
 import android.os.AsyncTask;
 import android.os.Build;
 import android.os.Bundle;
@@ -108,9 +107,9 @@
 import com.android.launcher3.dragndrop.DragView;
 import com.android.launcher3.folder.Folder;
 import com.android.launcher3.folder.FolderIcon;
+import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.logging.UserEventLogger;
 import com.android.launcher3.model.WidgetsModel;
-import com.android.launcher3.logging.LoggerUtils;
 import com.android.launcher3.userevent.nano.LauncherLogProto;
 import com.android.launcher3.util.ComponentKey;
 import com.android.launcher3.util.LongArrayMap;
@@ -2828,43 +2827,6 @@
         }
     }
 
-    void startApplicationDetailsActivity(ComponentName componentName, UserHandleCompat user) {
-        try {
-            LauncherAppsCompat launcherApps = LauncherAppsCompat.getInstance(this);
-            launcherApps.showAppDetailsForProfile(componentName, user);
-        } catch (SecurityException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Launcher does not have permission to launch settings");
-        } catch (ActivityNotFoundException e) {
-            Toast.makeText(this, R.string.activity_not_found, Toast.LENGTH_SHORT).show();
-            Log.e(TAG, "Unable to launch settings");
-        }
-    }
-
-    // returns true if the activity was started
-    boolean startApplicationUninstallActivity(ComponentName componentName, int flags,
-            UserHandleCompat user) {
-        if ((flags & AppInfo.DOWNLOADED_FLAG) == 0) {
-            // System applications cannot be installed. For now, show a toast explaining that.
-            // We may give them the option of disabling apps this way.
-            int messageId = R.string.uninstall_system_app_text;
-            Toast.makeText(this, messageId, Toast.LENGTH_SHORT).show();
-            return false;
-        } else {
-            String packageName = componentName.getPackageName();
-            String className = componentName.getClassName();
-            Intent intent = new Intent(
-                    Intent.ACTION_DELETE, Uri.fromParts("package", packageName, className));
-            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK |
-                    Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
-            if (user != null) {
-                user.addToIntent(intent, Intent.EXTRA_USER);
-            }
-            startActivity(intent);
-            return true;
-        }
-    }
-
     private boolean startActivity(View v, Intent intent, Object tag) {
         intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
         try {
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 7388161..9153943 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -3,14 +3,16 @@
 import android.annotation.TargetApi;
 import android.content.ComponentName;
 import android.content.Context;
+import android.content.Intent;
+import android.net.Uri;
 import android.os.Build;
 import android.os.Bundle;
 import android.os.UserManager;
 import android.util.AttributeSet;
 import android.util.Pair;
+import android.widget.Toast;
 
 import com.android.launcher3.compat.UserHandleCompat;
-import com.android.launcher3.util.Thunk;
 
 public class UninstallDropTarget extends ButtonDropTarget {
 
@@ -72,63 +74,89 @@
     @Override
     public void onDrop(DragObject d) {
         // Differ item deletion
-        if (d.dragSource instanceof UninstallSource) {
-            ((UninstallSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
+        if (d.dragSource instanceof DropTargetSource) {
+            ((DropTargetSource) d.dragSource).deferCompleteDropAfterUninstallActivity();
         }
         super.onDrop(d);
     }
 
     @Override
     void completeDrop(final DragObject d) {
-        final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(d.dragInfo);
-        final UserHandleCompat user = d.dragInfo.user;
-        if (startActivityWithUninstallAffordance(d)) {
-
-            final Runnable checkIfUninstallWasSuccess = new Runnable() {
-                @Override
-                public void run() {
-                    boolean uninstallSuccessful = false;
-                    if (componentInfo != null) {
-                        String packageName = componentInfo.first.getPackageName();
-                        uninstallSuccessful = !AllAppsList.packageHasActivities(
-                                getContext(), packageName, user);
-                    }
-                    sendUninstallResult(d.dragSource, uninstallSuccessful);
-                }
-            };
-            mLauncher.addOnResumeCallback(checkIfUninstallWasSuccess);
-        } else {
-            sendUninstallResult(d.dragSource, false);
-        }
-    }
-
-    protected boolean startActivityWithUninstallAffordance(DragObject d) {
-        return startUninstallActivity(mLauncher, d.dragInfo);
+        DropTargetResultCallback callback = d.dragSource instanceof DropTargetResultCallback
+                ? (DropTargetResultCallback) d.dragSource : null;
+        startUninstallActivity(mLauncher, d.dragInfo, callback);
     }
 
     public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
-        final Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
-        final UserHandleCompat user = info.user;
-        return launcher.startApplicationUninstallActivity(
-                componentInfo.first, componentInfo.second, user);
+        return startUninstallActivity(launcher, info, null);
     }
 
-    @Thunk void sendUninstallResult(DragSource target, boolean result) {
-        if (target instanceof UninstallSource) {
-            ((UninstallSource) target).onUninstallActivityReturned(result);
+    public static boolean startUninstallActivity(
+            final Launcher launcher, ItemInfo info, DropTargetResultCallback callback) {
+        Pair<ComponentName, Integer> componentInfo = getAppInfoFlags(info);
+        ComponentName cn = componentInfo.first;
+
+        final boolean isUninstallable;
+        if ((componentInfo.second & AppInfo.DOWNLOADED_FLAG) == 0) {
+            // System applications cannot be installed. For now, show a toast explaining that.
+            // We may give them the option of disabling apps this way.
+            Toast.makeText(launcher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+            isUninstallable = false;
+        } else {
+            Intent intent = new Intent(Intent.ACTION_DELETE,
+                    Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
+                    .setFlags(Intent.FLAG_ACTIVITY_NEW_TASK
+                            | Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
+            info.user.addToIntent(intent, Intent.EXTRA_USER);
+            launcher.startActivity(intent);
+            isUninstallable = true;
         }
+        if (callback != null) {
+            sendUninstallResult(
+                    launcher, isUninstallable, componentInfo.first, info.user, callback);
+        }
+        return isUninstallable;
+    }
+
+    /**
+     * Notifies the {@param callback} whether the uninstall was successful or not.
+     *
+     * Since there is no direct callback for an uninstall request, we check the package existence
+     * when the launch resumes next time. This assumes that the uninstall activity will finish only
+     * after the task is completed
+     */
+    protected static void sendUninstallResult(
+            final Launcher launcher, boolean activityStarted,
+            final ComponentName cn, final UserHandleCompat user,
+            final DropTargetResultCallback callback) {
+        if (activityStarted)  {
+            final Runnable checkIfUninstallWasSuccess = new Runnable() {
+                @Override
+                public void run() {
+                    String packageName = cn.getPackageName();
+                    boolean uninstallSuccessful = !AllAppsList.packageHasActivities(
+                            launcher, packageName, user);
+                    callback.onDragObjectRemoved(uninstallSuccessful);
+                }
+            };
+            launcher.addOnResumeCallback(checkIfUninstallWasSuccess);
+        } else {
+            callback.onDragObjectRemoved(false);
+        }
+    }
+
+    public interface DropTargetResultCallback {
+        /**
+         * A drag operation was complete.
+         * @param isRemoved true if the drag object should be removed, false otherwise.
+         */
+        void onDragObjectRemoved(boolean isRemoved);
     }
 
     /**
      * Interface defining an object that can provide uninstallable drag objects.
      */
-    public interface UninstallSource {
-
-        /**
-         * A pending uninstall operation was complete.
-         * @param result true if uninstall was successful, false otherwise.
-         */
-        void onUninstallActivityReturned(boolean result);
+    public interface DropTargetSource extends DropTargetResultCallback {
 
         /**
          * Indicates that an uninstall request are made and the actual result may come
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 80ce87f..bd5cd8a 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -57,7 +57,7 @@
 
 import com.android.launcher3.Launcher.CustomContentCallbacks;
 import com.android.launcher3.Launcher.LauncherOverlay;
-import com.android.launcher3.UninstallDropTarget.UninstallSource;
+import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.accessibility.OverviewScreenAccessibilityDelegate;
@@ -92,7 +92,7 @@
 public class Workspace extends PagedView
         implements DropTarget, DragSource, DragScroller, View.OnTouchListener,
         DragController.DragListener, LauncherTransitionable, ViewGroup.OnHierarchyChangeListener,
-        Insettable, UninstallSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
+        Insettable, DropTargetSource, AccessibilityDragSource, Stats.LaunchSourceProvider {
     private static final String TAG = "Launcher.Workspace";
 
     private static boolean ENFORCE_DRAG_EVENT_ORDER = false;
@@ -3664,7 +3664,7 @@
 
     /// maybe move this into a smaller part
     @Override
-    public void onUninstallActivityReturned(boolean success) {
+    public void onDragObjectRemoved(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
         if (mDeferredAction != null) {
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 8560b21..c699479 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -102,7 +102,7 @@
         if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
             info.addAction(mActions.get(UNINSTALL));
         }
-        if (InfoDropTarget.supportsDrop(host.getContext(), item)) {
+        if (InfoDropTarget.supportsDrop(item)) {
             info.addAction(mActions.get(INFO));
         }
 
@@ -137,7 +137,7 @@
             DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
             return true;
         } else if (action == INFO) {
-            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher);
+            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null);
             return true;
         } else if (action == UNINSTALL) {
             return UninstallDropTarget.startUninstallActivity(mLauncher, item);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 9fdb295..e66523d 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -71,9 +71,8 @@
 import com.android.launcher3.R;
 import com.android.launcher3.ShortcutInfo;
 import com.android.launcher3.Stats;
-import com.android.launcher3.UninstallDropTarget.UninstallSource;
+import com.android.launcher3.UninstallDropTarget.DropTargetSource;
 import com.android.launcher3.Utilities;
-import com.android.launcher3.Workspace;
 import com.android.launcher3.Workspace.ItemOperator;
 import com.android.launcher3.accessibility.LauncherAccessibilityDelegate.AccessibilityDragSource;
 import com.android.launcher3.config.FeatureFlags;
@@ -92,7 +91,7 @@
  */
 public class Folder extends LinearLayout implements DragSource, View.OnClickListener,
         View.OnLongClickListener, DropTarget, FolderListener, TextView.OnEditorActionListener,
-        View.OnFocusChangeListener, DragListener, UninstallSource, AccessibilityDragSource,
+        View.OnFocusChangeListener, DragListener, DropTargetSource, AccessibilityDragSource,
         Stats.LaunchSourceProvider {
     private static final String TAG = "Launcher.Folder";
 
@@ -980,7 +979,7 @@
     }
 
     @Override
-    public void onUninstallActivityReturned(boolean success) {
+    public void onDragObjectRemoved(boolean success) {
         mDeferDropAfterUninstall = false;
         mUninstallSuccessful = success;
         if (mDeferredAction != null) {