Caching the uninstall disabled state for each userHandle

> Removing static access to varios drop targets
> Creating a cache at UI level with 5sec timeout

Bug: 67104426
Change-Id: Ide6e2e0c01606f9b5fb9281f95dc009873c18fb9
diff --git a/src/com/android/launcher3/ButtonDropTarget.java b/src/com/android/launcher3/ButtonDropTarget.java
index 1a1c319..5c8b080 100644
--- a/src/com/android/launcher3/ButtonDropTarget.java
+++ b/src/com/android/launcher3/ButtonDropTarget.java
@@ -201,6 +201,10 @@
 
     protected abstract boolean supportsDrop(ItemInfo info);
 
+    public boolean supportsAccessibilityDrop(ItemInfo info) {
+        return supportsDrop(info);
+    }
+
     @Override
     public boolean isDropEnabled() {
         return mActive && (mAccessibleDrag ||
@@ -241,9 +245,13 @@
                 DragLayer.ANIMATION_END_DISAPPEAR, null);
     }
 
+    public abstract int getAccessibilityAction();
+
     @Override
     public void prepareAccessibilityDrop() { }
 
+    public abstract void onAccessibilityDrop(View view, ItemInfo item);
+
     public abstract void completeDrop(DragObject d);
 
     @Override
diff --git a/src/com/android/launcher3/DeleteDropTarget.java b/src/com/android/launcher3/DeleteDropTarget.java
index fdd4f34..c12ea57 100644
--- a/src/com/android/launcher3/DeleteDropTarget.java
+++ b/src/com/android/launcher3/DeleteDropTarget.java
@@ -21,6 +21,7 @@
 import android.util.AttributeSet;
 import android.view.View;
 
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.folder.Folder;
 
@@ -49,14 +50,22 @@
         setTextBasedOnDragSource(dragObject.dragInfo);
     }
 
-    /** @return true for items that should have a "Remove" action in accessibility. */
-    public static boolean supportsAccessibleDrop(ItemInfo info) {
+    /**
+     * @return true for items that should have a "Remove" action in accessibility.
+     */
+    @Override
+    public boolean supportsAccessibilityDrop(ItemInfo info) {
         return (info instanceof ShortcutInfo)
                 || (info instanceof LauncherAppWidgetInfo)
                 || (info instanceof FolderInfo);
     }
 
     @Override
+    public int getAccessibilityAction() {
+        return LauncherAccessibilityDelegate.REMOVE;
+    }
+
+    @Override
     protected boolean supportsDrop(ItemInfo info) {
         return true;
     }
@@ -77,19 +86,21 @@
     public void completeDrop(DragObject d) {
         ItemInfo item = d.dragInfo;
         if ((d.dragSource instanceof Workspace) || (d.dragSource instanceof Folder)) {
-            removeWorkspaceOrFolderItem(mLauncher, item, null);
+            onAccessibilityDrop(null, item);
         }
     }
 
     /**
      * Removes the item from the workspace. If the view is not null, it also removes the view.
      */
-    public static void removeWorkspaceOrFolderItem(Launcher launcher, ItemInfo item, View view) {
+    @Override
+    public void onAccessibilityDrop(View view, ItemInfo item) {
         // Remove the item from launcher and the db, we can ignore the containerInfo in this call
         // because we already remove the drag view from the folder (if the drag originated from
         // a folder) in Folder.beginDrag()
-        launcher.removeItem(view, item, true /* deleteFromDb */);
-        launcher.getWorkspace().stripEmptyScreens();
-        launcher.getDragLayer().announceForAccessibility(launcher.getString(R.string.item_removed));
+        mLauncher.removeItem(view, item, true /* deleteFromDb */);
+        mLauncher.getWorkspace().stripEmptyScreens();
+        mLauncher.getDragLayer()
+                .announceForAccessibility(getContext().getString(R.string.item_removed));
     }
 }
diff --git a/src/com/android/launcher3/DropTargetBar.java b/src/com/android/launcher3/DropTargetBar.java
index 29a1349..2f8374a 100644
--- a/src/com/android/launcher3/DropTargetBar.java
+++ b/src/com/android/launcher3/DropTargetBar.java
@@ -16,6 +16,9 @@
 
 package com.android.launcher3;
 
+import static com.android.launcher3.AlphaUpdateListener.updateVisibility;
+import static com.android.launcher3.Utilities.isAccessibilityEnabled;
+
 import android.animation.TimeInterpolator;
 import android.content.Context;
 import android.util.AttributeSet;
@@ -23,13 +26,14 @@
 import android.view.ViewDebug;
 import android.view.ViewGroup;
 import android.view.ViewPropertyAnimator;
-import android.view.accessibility.AccessibilityManager;
 import android.view.animation.AccelerateInterpolator;
 import android.widget.LinearLayout;
 
 import com.android.launcher3.dragndrop.DragController;
 import com.android.launcher3.dragndrop.DragOptions;
 
+import java.util.ArrayList;
+
 /*
  * The top bar containing various drop targets: Delete/App Info/Uninstall.
  */
@@ -42,10 +46,7 @@
 
         @Override
         public void run() {
-            AccessibilityManager am = (AccessibilityManager)
-                    getContext().getSystemService(Context.ACCESSIBILITY_SERVICE);
-            boolean accessibilityEnabled = am.isEnabled();
-            AlphaUpdateListener.updateVisibility(DropTargetBar.this, accessibilityEnabled);
+            updateVisibility(DropTargetBar.this, isAccessibilityEnabled(getContext()));
         }
     };
 
@@ -55,6 +56,7 @@
     @ViewDebug.ExportedProperty(category = "launcher")
     protected boolean mVisible = false;
 
+    private ButtonDropTarget[] mDropTargets;
     private ViewPropertyAnimator mCurrentAnimation;
 
     public DropTargetBar(Context context, AttributeSet attrs) {
@@ -75,7 +77,27 @@
 
     public void setup(DragController dragController) {
         dragController.addDragListener(this);
-        setupButtonDropTarget(this, dragController);
+        ArrayList<ButtonDropTarget> outList = new ArrayList<>();
+        findDropTargets(this, outList);
+
+        mDropTargets = new ButtonDropTarget[outList.size()];
+        for (int i = 0; i < mDropTargets.length; i++) {
+            mDropTargets[i] = outList.get(i);
+            mDropTargets[i].setDropTargetBar(this);
+            dragController.addDragListener(mDropTargets[i]);
+            dragController.addDropTarget(mDropTargets[i]);
+        }
+    }
+
+    private static void findDropTargets(View view, ArrayList<ButtonDropTarget> outTargets) {
+        if (view instanceof ButtonDropTarget) {
+            outTargets.add((ButtonDropTarget) view);
+        } else if (view instanceof ViewGroup) {
+            ViewGroup vg = (ViewGroup) view;
+            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
+                findDropTargets(vg.getChildAt(i), outTargets);
+            }
+        }
     }
 
     @Override
@@ -130,20 +152,6 @@
         return result;
     }
 
-    private void setupButtonDropTarget(View view, DragController dragController) {
-        if (view instanceof ButtonDropTarget) {
-            ButtonDropTarget bdt = (ButtonDropTarget) view;
-            bdt.setDropTargetBar(this);
-            dragController.addDragListener(bdt);
-            dragController.addDropTarget(bdt);
-        } else if (view instanceof ViewGroup) {
-            ViewGroup vg = (ViewGroup) view;
-            for (int i = vg.getChildCount() - 1; i >= 0; i--) {
-                setupButtonDropTarget(vg.getChildAt(i), dragController);
-            }
-        }
-    }
-
     private void animateToVisibility(boolean isVisible) {
         if (mVisible != isVisible) {
             mVisible = isVisible;
@@ -190,4 +198,8 @@
             mDeferOnDragEnd = false;
         }
     }
+
+    public ButtonDropTarget[] getDropTargets() {
+        return mDropTargets;
+    }
 }
diff --git a/src/com/android/launcher3/InfoDropTarget.java b/src/com/android/launcher3/InfoDropTarget.java
index f78cde5..289242f 100644
--- a/src/com/android/launcher3/InfoDropTarget.java
+++ b/src/com/android/launcher3/InfoDropTarget.java
@@ -26,6 +26,7 @@
 import android.util.Log;
 import android.widget.Toast;
 
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.util.Themes;
 
@@ -49,8 +50,8 @@
     }
 
     @Override
-    protected ComponentName performDropAction(DragObject d) {
-        return performDropAction(mLauncher, d.dragInfo, null, null);
+    protected ComponentName performDropAction(ItemInfo item) {
+        return performDropAction(mLauncher, item, null, null);
     }
 
     /**
@@ -96,13 +97,15 @@
     }
 
     @Override
-    protected boolean supportsDrop(ItemInfo info) {
-        return supportsDrop(getContext(), info);
+    public int getAccessibilityAction() {
+        return LauncherAccessibilityDelegate.INFO;
     }
 
-    public static boolean supportsDrop(Context context, ItemInfo info) {
+    @Override
+    protected boolean supportsDrop(ItemInfo info) {
         // Only show the App Info drop target if developer settings are enabled.
-        boolean developmentSettingsEnabled = Settings.Global.getInt(context.getContentResolver(),
+        boolean developmentSettingsEnabled = Settings.Global.getInt(
+                getContext().getContentResolver(),
                 Settings.Global.DEVELOPMENT_SETTINGS_ENABLED, 0) == 1;
         if (!developmentSettingsEnabled) {
             return false;
diff --git a/src/com/android/launcher3/UninstallDropTarget.java b/src/com/android/launcher3/UninstallDropTarget.java
index 4393819..8e83a30 100644
--- a/src/com/android/launcher3/UninstallDropTarget.java
+++ b/src/com/android/launcher3/UninstallDropTarget.java
@@ -10,22 +10,28 @@
 import android.os.Bundle;
 import android.os.UserHandle;
 import android.os.UserManager;
+import android.util.ArrayMap;
 import android.util.AttributeSet;
 import android.util.Log;
 import android.view.View;
 import android.widget.Toast;
 
 import com.android.launcher3.Launcher.OnResumeCallback;
+import com.android.launcher3.accessibility.LauncherAccessibilityDelegate;
 import com.android.launcher3.compat.LauncherAppsCompat;
 import com.android.launcher3.dragndrop.DragOptions;
 import com.android.launcher3.userevent.nano.LauncherLogProto.Target;
 
 import java.net.URISyntaxException;
 
-public class UninstallDropTarget extends ButtonDropTarget {
+public class UninstallDropTarget extends ButtonDropTarget implements OnAlarmListener {
 
     private static final String TAG = "UninstallDropTarget";
-    private static Boolean sUninstallDisabled;
+
+    private static final long CACHE_EXPIRE_TIMEOUT = 5000;
+    private final ArrayMap<UserHandle, Boolean> mUninstallDisabledCache = new ArrayMap<>(1);
+
+    private final Alarm mCacheExpireAlarm;
 
     public UninstallDropTarget(Context context, AttributeSet attrs) {
         this(context, attrs, 0);
@@ -33,6 +39,9 @@
 
     public UninstallDropTarget(Context context, AttributeSet attrs, int defStyle) {
         super(context, attrs, defStyle);
+
+        mCacheExpireAlarm = new Alarm();
+        mCacheExpireAlarm.setOnAlarmListener(this);
     }
 
     @Override
@@ -48,18 +57,29 @@
     }
 
     @Override
-    protected boolean supportsDrop(ItemInfo info) {
-        return supportsDrop(getContext(), info);
+    public void onAlarm(Alarm alarm) {
+        mUninstallDisabledCache.clear();
     }
 
-    public static boolean supportsDrop(Context context, ItemInfo info) {
-        if (sUninstallDisabled == null) {
-            UserManager userManager = (UserManager) context.getSystemService(Context.USER_SERVICE);
-            Bundle restrictions = userManager.getUserRestrictions();
-            sUninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
+    @Override
+    public int getAccessibilityAction() {
+        return LauncherAccessibilityDelegate.UNINSTALL;
+    }
+
+    @Override
+    protected boolean supportsDrop(ItemInfo info) {
+        Boolean uninstallDisabled = mUninstallDisabledCache.get(info.user);
+        if (uninstallDisabled == null) {
+            UserManager userManager =
+                    (UserManager) getContext().getSystemService(Context.USER_SERVICE);
+            Bundle restrictions = userManager.getUserRestrictions(info.user);
+            uninstallDisabled = restrictions.getBoolean(UserManager.DISALLOW_APPS_CONTROL, false)
                     || restrictions.getBoolean(UserManager.DISALLOW_UNINSTALL_APPS, false);
+            mUninstallDisabledCache.put(info.user, uninstallDisabled);
         }
-        if (sUninstallDisabled) {
+        // Cancel any pending alarm and set cache expiry after some time
+        mCacheExpireAlarm.setAlarm(CACHE_EXPIRE_TIMEOUT);
+        if (uninstallDisabled) {
             return false;
         }
 
@@ -69,13 +89,13 @@
                 return (appInfo.isSystemApp & AppInfo.FLAG_SYSTEM_NO) != 0;
             }
         }
-        return getUninstallTarget(context, info) != null;
+        return getUninstallTarget(info) != null;
     }
 
     /**
      * @return the component name that should be uninstalled or null.
      */
-    private static ComponentName getUninstallTarget(Context context, ItemInfo item) {
+    private ComponentName getUninstallTarget(ItemInfo item) {
         Intent intent = null;
         UserHandle user = null;
         if (item != null &&
@@ -84,7 +104,7 @@
             user = item.user;
         }
         if (intent != null) {
-            LauncherActivityInfo info = LauncherAppsCompat.getInstance(context)
+            LauncherActivityInfo info = LauncherAppsCompat.getInstance(mLauncher)
                     .resolveActivity(intent, user);
             if (info != null
                     && (info.getApplicationInfo().flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
@@ -103,7 +123,7 @@
 
     @Override
     public void completeDrop(final DragObject d) {
-        ComponentName target = performDropAction(d);
+        ComponentName target = performDropAction(d.dragInfo);
         if (d.dragSource instanceof DeferredOnComplete) {
             DeferredOnComplete deferred = (DeferredOnComplete) d.dragSource;
             if (target != null) {
@@ -119,27 +139,19 @@
      * Performs the drop action and returns the target component for the dragObject or null if
      * the action was not performed.
      */
-    protected ComponentName performDropAction(DragObject d) {
-        return performDropAction(mLauncher, d.dragInfo);
-    }
-
-    /**
-     * Performs the drop action and returns the target component for the dragObject or null if
-     * the action was not performed.
-     */
-    private static ComponentName performDropAction(Context context, ItemInfo info) {
-        ComponentName cn = getUninstallTarget(context, info);
+    protected ComponentName performDropAction(ItemInfo info) {
+        ComponentName cn = getUninstallTarget(info);
         if (cn == null) {
             // 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(context, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
+            Toast.makeText(mLauncher, R.string.uninstall_system_app_text, Toast.LENGTH_SHORT).show();
             return null;
         }
         try {
-            Intent i = Intent.parseUri(context.getString(R.string.delete_package_intent), 0)
+            Intent i = Intent.parseUri(mLauncher.getString(R.string.delete_package_intent), 0)
                     .setData(Uri.fromParts("package", cn.getPackageName(), cn.getClassName()))
                     .putExtra(Intent.EXTRA_USER, info.user);
-            context.startActivity(i);
+            mLauncher.startActivity(i);
             return cn;
         } catch (URISyntaxException e) {
             Log.e(TAG, "Failed to parse intent to start uninstall activity for item=" + info);
@@ -147,8 +159,9 @@
         }
     }
 
-    public static boolean startUninstallActivity(Launcher launcher, ItemInfo info) {
-        return performDropAction(launcher, info) != null;
+    @Override
+    public void onAccessibilityDrop(View view, ItemInfo item) {
+        performDropAction(item);
     }
 
     /**
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index 583492e..f17818a 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -17,6 +17,7 @@
 import com.android.launcher3.AppInfo;
 import com.android.launcher3.AppWidgetResizeFrame;
 import com.android.launcher3.BubbleTextView;
+import com.android.launcher3.ButtonDropTarget;
 import com.android.launcher3.CellLayout;
 import com.android.launcher3.DeleteDropTarget;
 import com.android.launcher3.DropTarget.DragObject;
@@ -45,9 +46,9 @@
 
     private static final String TAG = "LauncherAccessibilityDelegate";
 
-    protected static final int REMOVE = R.id.action_remove;
-    protected static final int INFO = R.id.action_info;
-    protected static final int UNINSTALL = R.id.action_uninstall;
+    public static final int REMOVE = R.id.action_remove;
+    public static final int INFO = R.id.action_info;
+    public static final int UNINSTALL = R.id.action_uninstall;
     protected static final int ADD_TO_WORKSPACE = R.id.action_add_to_workspace;
     protected static final int MOVE = R.id.action_move;
     protected static final int MOVE_TO_WORKSPACE = R.id.action_move_to_workspace;
@@ -108,14 +109,10 @@
             info.addAction(mActions.get(DEEP_SHORTCUTS));
         }
 
-        if (DeleteDropTarget.supportsAccessibleDrop(item)) {
-            info.addAction(mActions.get(REMOVE));
-        }
-        if (UninstallDropTarget.supportsDrop(host.getContext(), item)) {
-            info.addAction(mActions.get(UNINSTALL));
-        }
-        if (InfoDropTarget.supportsDrop(host.getContext(), item)) {
-            info.addAction(mActions.get(INFO));
+        for (ButtonDropTarget target : mLauncher.getDropTargetBar().getDropTargets()) {
+            if (target.supportsAccessibilityDrop(item)) {
+                info.addAction(mActions.get(target.getAccessibilityAction()));
+            }
         }
 
         // Do not add move actions for keyboard request as this uses virtual nodes.
@@ -148,15 +145,7 @@
     }
 
     public boolean performAction(final View host, final ItemInfo item, int action) {
-        if (action == REMOVE) {
-            DeleteDropTarget.removeWorkspaceOrFolderItem(mLauncher, item, host);
-            return true;
-        } else if (action == INFO) {
-            InfoDropTarget.startDetailsActivityForInfo(item, mLauncher, null, null);
-            return true;
-        } else if (action == UNINSTALL) {
-            return UninstallDropTarget.startUninstallActivity(mLauncher, item);
-        } else if (action == MOVE) {
+        if (action == MOVE) {
             beginAccessibleDrag(host, item);
         } else if (action == ADD_TO_WORKSPACE) {
             final int[] coordinates = new int[2];
@@ -231,6 +220,13 @@
             return true;
         } else if (action == DEEP_SHORTCUTS) {
             return PopupContainerWithArrow.showForIcon((BubbleTextView) host) != null;
+        } else {
+            for (ButtonDropTarget dropTarget : mLauncher.getDropTargetBar().getDropTargets()) {
+                if (action == dropTarget.getAccessibilityAction()) {
+                    dropTarget.onAccessibilityDrop(host, item);
+                    return true;
+                }
+            }
         }
         return false;
     }