Fixing folder icon not getting redrawn when the contents change

> Removing parent from ItemOperator as inconsistant values were
being passed in workspace and folderPagedView
> Fixing itemChanged causing multiple redraws, by passing a
'animate' parameter to explicitely request animation

Bug:27740161
Bug:28015426

Change-Id: Ide7b266bde9aad5f450a3f808a59182fe01a5110
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 861a935..6c9d969 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -69,12 +69,12 @@
      *
      * @param item
      */
-    public void add(ShortcutInfo item) {
+    public void add(ShortcutInfo item, boolean animate) {
         contents.add(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onAdd(item);
         }
-        itemsChanged();
+        itemsChanged(animate);
     }
 
     /**
@@ -82,12 +82,12 @@
      *
      * @param item
      */
-    public void remove(ShortcutInfo item) {
+    public void remove(ShortcutInfo item, boolean animate) {
         contents.remove(item);
         for (int i = 0; i < listeners.size(); i++) {
             listeners.get(i).onRemove(item);
         }
-        itemsChanged();
+        itemsChanged(animate);
     }
 
     public void setTitle(CharSequence title) {
@@ -115,9 +115,9 @@
         }
     }
 
-    void itemsChanged() {
+    public void itemsChanged(boolean animate) {
         for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onItemsChanged();
+            listeners.get(i).onItemsChanged(animate);
         }
     }
 
@@ -131,7 +131,7 @@
         public void onAdd(ShortcutInfo item);
         public void onRemove(ShortcutInfo item);
         public void onTitleChanged(CharSequence title);
-        public void onItemsChanged();
+        public void onItemsChanged(boolean animate);
     }
 
     @Override
diff --git a/src/com/android/launcher3/Launcher.java b/src/com/android/launcher3/Launcher.java
index b53a1b0..445831c 100644
--- a/src/com/android/launcher3/Launcher.java
+++ b/src/com/android/launcher3/Launcher.java
@@ -2379,7 +2379,7 @@
             // Remove the shortcut from the folder before removing it from launcher
             View folderIcon = mWorkspace.getHomescreenIconByItemId(itemInfo.container);
             if (folderIcon instanceof FolderIcon) {
-                ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo);
+                ((FolderInfo) folderIcon.getTag()).remove((ShortcutInfo) itemInfo, true);
             } else {
                 mWorkspace.removeWorkspaceItem(v);
             }
diff --git a/src/com/android/launcher3/LauncherModel.java b/src/com/android/launcher3/LauncherModel.java
index 60c71cf..f2b307b 100644
--- a/src/com/android/launcher3/LauncherModel.java
+++ b/src/com/android/launcher3/LauncherModel.java
@@ -1986,7 +1986,7 @@
                                         // Item is in a user folder
                                         FolderInfo folderInfo =
                                                 findOrMakeFolder(sBgFolders, container);
-                                        folderInfo.add(info);
+                                        folderInfo.add(info, false);
                                         break;
                                     }
                                     sBgItemsIdMap.put(info.id, info);
diff --git a/src/com/android/launcher3/Workspace.java b/src/com/android/launcher3/Workspace.java
index 7cceba4..f04244f 100644
--- a/src/com/android/launcher3/Workspace.java
+++ b/src/com/android/launcher3/Workspace.java
@@ -3873,7 +3873,7 @@
         return getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return info != null && info.id == id;
             }
         });
@@ -3883,7 +3883,7 @@
         return getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return info == tag;
             }
         });
@@ -3893,7 +3893,7 @@
         return (LauncherAppWidgetHostView) getFirstMatch(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 return (info instanceof LauncherAppWidgetInfo) &&
                         ((LauncherAppWidgetInfo) info).appWidgetId == appWidgetId;
             }
@@ -3904,8 +3904,8 @@
         final View[] value = new View[1];
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
-                if (operator.evaluate(info, v, parent)) {
+            public boolean evaluate(ItemInfo info, View v) {
+                if (operator.evaluate(info, v)) {
                     value[0] = v;
                     return true;
                 }
@@ -3918,7 +3918,7 @@
     void clearDropTargets() {
         mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (v instanceof DropTarget) {
                     mDragController.removeDropTarget((DropTarget) v);
                 }
@@ -4014,7 +4014,7 @@
             for (FolderInfo folder : folderAppsToRemove.keySet()) {
                 ArrayList<ShortcutInfo> appsToRemove = folderAppsToRemove.get(folder);
                 for (ShortcutInfo info : appsToRemove) {
-                    folder.remove(info);
+                    folder.remove(info, false);
                 }
             }
 
@@ -4044,10 +4044,9 @@
          *
          * @param info info for the shortcut
          * @param view view for the shortcut
-         * @param parent containing folder, or null
          * @return true if done, false to continue the map
          */
-        public boolean evaluate(ItemInfo info, View view, View parent);
+        public boolean evaluate(ItemInfo info, View view);
     }
 
     /**
@@ -4074,12 +4073,12 @@
                     for (int childIdx = 0; childIdx < childCount; childIdx++) {
                         View child = folderChildren.get(childIdx);
                         info = (ItemInfo) child.getTag();
-                        if (op.evaluate(info, child, folder)) {
+                        if (op.evaluate(info, child)) {
                             return;
                         }
                     }
                 } else {
-                    if (op.evaluate(info, item, null)) {
+                    if (op.evaluate(info, item)) {
                         return;
                     }
                 }
@@ -4088,10 +4087,19 @@
     }
 
     void updateShortcuts(ArrayList<ShortcutInfo> shortcuts) {
-        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(shortcuts);
+        int total  = shortcuts.size();
+        final HashSet<ShortcutInfo> updates = new HashSet<ShortcutInfo>(total);
+        final HashSet<Long> folderIds = new HashSet<>();
+
+        for (int i = 0; i < total; i++) {
+            ShortcutInfo s = shortcuts.get(i);
+            updates.add(s);
+            folderIds.add(s.container);
+        }
+
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof ShortcutInfo && v instanceof BubbleTextView &&
                         updates.contains(info)) {
                     ShortcutInfo si = (ShortcutInfo) info;
@@ -4101,10 +4109,18 @@
                             && ((PreloadIconDrawable) oldIcon).hasNotCompleted();
                     shortcut.applyFromShortcutInfo(si, mIconCache,
                             si.isPromise() != oldPromiseState);
+                }
+                // process all the shortcuts
+                return false;
+            }
+        });
 
-                    if (parent != null) {
-                        parent.invalidate();
-                    }
+        // Update folder icons
+        mapOverItems(MAP_NO_RECURSE, new ItemOperator() {
+            @Override
+            public boolean evaluate(ItemInfo info, View v) {
+                if (info instanceof FolderInfo && folderIds.contains(info.id)) {
+                    ((FolderInfo) info).itemsChanged(false);
                 }
                 // process all the shortcuts
                 return false;
@@ -4122,7 +4138,7 @@
     public void updateRestoreItems(final HashSet<ItemInfo> updates) {
         mapOverItems(MAP_RECURSE, new ItemOperator() {
             @Override
-            public boolean evaluate(ItemInfo info, View v, View parent) {
+            public boolean evaluate(ItemInfo info, View v) {
                 if (info instanceof ShortcutInfo && v instanceof BubbleTextView
                         && updates.contains(info)) {
                     ((BubbleTextView) v).applyState(false);
diff --git a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
index c699479..aa6e08e 100644
--- a/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
+++ b/src/com/android/launcher3/accessibility/LauncherAccessibilityDelegate.java
@@ -174,7 +174,7 @@
             Folder folder = mLauncher.getWorkspace().getOpenFolder();
             mLauncher.closeFolder(folder, true);
             ShortcutInfo info = (ShortcutInfo) item;
-            folder.getInfo().remove(info);
+            folder.getInfo().remove(info, false);
 
             final int[] coordinates = new int[2];
             final long screenId = findSpaceOnWorkspace(item, coordinates);
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 087670e..09a92a9 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -300,7 +300,7 @@
             mCurrentDragView = v;
 
             mContent.removeItem(mCurrentDragView);
-            mInfo.remove(mCurrentDragInfo);
+            mInfo.remove(mCurrentDragInfo, true);
             mDragInProgress = true;
             mItemAddedBackToSelfViaIcon = false;
         }
@@ -426,8 +426,9 @@
         // If our folder has too many items we prune them from the list. This is an issue
         // when upgrading from the old Folders implementation which could contain an unlimited
         // number of items.
+        // TODO: Remove this, as with multi-page folders, there will never be any overflow
         for (ShortcutInfo item: overflow) {
-            mInfo.remove(item);
+            mInfo.remove(item, false);
             LauncherModel.deleteItemFromDatabase(mLauncher, item);
         }
 
@@ -1330,7 +1331,7 @@
 
         // Temporarily suppress the listener, as we did all the work already here.
         mSuppressOnAdd = true;
-        mInfo.add(si);
+        mInfo.add(si, false);
         mSuppressOnAdd = false;
         // Clear the drag info, as it is no longer being dragged.
         mCurrentDragInfo = null;
@@ -1390,13 +1391,14 @@
         return mContent.iterateOverItems(new ItemOperator() {
 
             @Override
-            public boolean evaluate(ItemInfo info, View view, View parent) {
+            public boolean evaluate(ItemInfo info, View view) {
                 return info == item;
             }
         });
     }
 
-    public void onItemsChanged() {
+    @Override
+    public void onItemsChanged(boolean animate) {
         updateTextViewFocus();
     }
 
@@ -1409,7 +1411,7 @@
             mContent.iterateOverItems(new ItemOperator() {
 
                 @Override
-                public boolean evaluate(ItemInfo info, View view, View parent) {
+                public boolean evaluate(ItemInfo info, View view) {
                     mItemsInReadingOrder.add(view);
                     return false;
                 }
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index b7e11f1..d76608a 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -224,7 +224,7 @@
     }
 
     public void addItem(ShortcutInfo item) {
-        mInfo.add(item);
+        mInfo.add(item, true);
     }
 
     public void onDragEnter(ItemInfo dragInfo) {
@@ -937,8 +937,9 @@
         }
     }
 
-    public void onItemsChanged() {
-        updateItemDrawingParams(true);
+    @Override
+    public void onItemsChanged(boolean animate) {
+        updateItemDrawingParams(animate);
         invalidate();
         requestLayout();
     }
diff --git a/src/com/android/launcher3/folder/FolderPagedView.java b/src/com/android/launcher3/folder/FolderPagedView.java
index c25444e..1af1485 100644
--- a/src/com/android/launcher3/folder/FolderPagedView.java
+++ b/src/com/android/launcher3/folder/FolderPagedView.java
@@ -454,7 +454,7 @@
             for (int j = 0; j < page.getCountY(); j++) {
                 for (int i = 0; i < page.getCountX(); i++) {
                     View v = page.getChildAt(i, j);
-                    if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v, this)) {
+                    if ((v != null) && op.evaluate((ItemInfo) v.getTag(), v)) {
                         return v;
                     }
                 }
diff --git a/src/com/android/launcher3/util/ManagedProfileHeuristic.java b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
index fb9bbb2..3925c40 100644
--- a/src/com/android/launcher3/util/ManagedProfileHeuristic.java
+++ b/src/com/android/launcher3/util/ManagedProfileHeuristic.java
@@ -188,7 +188,7 @@
                 @Override
                 public void run() {
                     for (ShortcutInfo info : shortcuts) {
-                        workFolder.add(info);
+                        workFolder.add(info, false);
                     }
                 }
             });
@@ -200,7 +200,7 @@
 
             // Add all shortcuts before adding it to the UI, as an empty folder might get deleted.
             for (ShortcutInfo info : mWorkFolderApps) {
-                workFolder.add(info);
+                workFolder.add(info, false);
             }
 
             // Add the item to home screen and DB. This also generates an item id synchronously.