Merge "Wrapping folderLisners with weak reference, and storing it as an array" into ub-launcher3-calgary
diff --git a/src/com/android/launcher3/FolderInfo.java b/src/com/android/launcher3/FolderInfo.java
index 6c9d969..0dfe525 100644
--- a/src/com/android/launcher3/FolderInfo.java
+++ b/src/com/android/launcher3/FolderInfo.java
@@ -21,6 +21,7 @@
 
 import com.android.launcher3.compat.UserHandleCompat;
 
+import java.lang.ref.WeakReference;
 import java.util.ArrayList;
 
 /**
@@ -57,7 +58,11 @@
      */
     public ArrayList<ShortcutInfo> contents = new ArrayList<ShortcutInfo>();
 
-    ArrayList<FolderListener> listeners = new ArrayList<FolderListener>();
+    /**
+     * A collection of listeners for folder info changes. Since this listeners are implemented by
+     * the UI objects, using a WeakReference prevents context leaks.
+     */
+    private  WeakReference<FolderListener> mListener;
 
     public FolderInfo() {
         itemType = LauncherSettings.Favorites.ITEM_TYPE_FOLDER;
@@ -71,8 +76,9 @@
      */
     public void add(ShortcutInfo item, boolean animate) {
         contents.add(item);
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onAdd(item);
+        FolderListener listener = mListener == null ? null : mListener.get();
+        if (listener != null) {
+            listener.onAdd(item);
         }
         itemsChanged(animate);
     }
@@ -84,19 +90,13 @@
      */
     public void remove(ShortcutInfo item, boolean animate) {
         contents.remove(item);
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onRemove(item);
+        FolderListener listener = mListener == null ? null : mListener.get();
+        if (listener != null) {
+            listener.onRemove(item);
         }
         itemsChanged(animate);
     }
 
-    public void setTitle(CharSequence title) {
-        this.title = title;
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onTitleChanged(title);
-        }
-    }
-
     @Override
     void onAddToDatabase(Context context, ContentValues values) {
         super.onAddToDatabase(context, values);
@@ -105,33 +105,30 @@
 
     }
 
-    public void addListener(FolderListener listener) {
-        listeners.add(listener);
-    }
-
-    void removeListener(FolderListener listener) {
-        if (listeners.contains(listener)) {
-            listeners.remove(listener);
-        }
+    /**
+     * Registers a listener for info change events.
+     */
+    public void setListener(FolderListener listener) {
+        mListener = new WeakReference<>(listener);
     }
 
     public void itemsChanged(boolean animate) {
-        for (int i = 0; i < listeners.size(); i++) {
-            listeners.get(i).onItemsChanged(animate);
+        FolderListener listener = mListener == null ? null : mListener.get();
+        if (listener != null) {
+            listener.onItemsChanged(animate);
         }
     }
 
     @Override
     void unbind() {
         super.unbind();
-        listeners.clear();
+        mListener = null;
     }
 
     public interface FolderListener {
-        public void onAdd(ShortcutInfo item);
-        public void onRemove(ShortcutInfo item);
-        public void onTitleChanged(CharSequence title);
-        public void onItemsChanged(boolean animate);
+        void onAdd(ShortcutInfo item);
+        void onRemove(ShortcutInfo item);
+        void onItemsChanged(boolean animate);
     }
 
     @Override
diff --git a/src/com/android/launcher3/folder/Folder.java b/src/com/android/launcher3/folder/Folder.java
index 1ebe8fd..2ea1986 100644
--- a/src/com/android/launcher3/folder/Folder.java
+++ b/src/com/android/launcher3/folder/Folder.java
@@ -348,13 +348,14 @@
         mFolderName.setHint(sHintText);
         // Convert to a string here to ensure that no other state associated with the text field
         // gets saved.
-        String newTitle = mFolderName.getText().toString();
-        mInfo.setTitle(newTitle);
+        mInfo.title = mFolderName.getText().toString();
+        mFolderIcon.onTitleChanged(mInfo.title);
+
         LauncherModel.updateItemInDatabase(mLauncher, mInfo);
 
         if (commit) {
             sendCustomAccessibilityEvent(AccessibilityEvent.TYPE_WINDOW_STATE_CHANGED,
-                    getContext().getString(R.string.folder_renamed, newTitle));
+                    getContext().getString(R.string.folder_renamed, mInfo.title));
         }
 
         // This ensures that focus is gained every time the field is clicked, which selects all
@@ -448,7 +449,6 @@
 
         mItemsInvalidated = true;
         updateTextViewFocus();
-        mInfo.addListener(this);
 
         if (!sDefaultFolderName.contentEquals(mInfo.title)) {
             mFolderName.setText(mInfo.title);
@@ -1349,6 +1349,7 @@
                 mLauncher, item, mInfo.id, 0, item.cellX, item.cellY);
     }
 
+    @Override
     public void onRemove(ShortcutInfo item) {
         mItemsInvalidated = true;
         // If this item is being dragged from this open folder, we have already handled
@@ -1385,9 +1386,6 @@
         updateTextViewFocus();
     }
 
-    public void onTitleChanged(CharSequence title) {
-    }
-
     public ArrayList<View> getItemsInReadingOrder() {
         if (mItemsInvalidated) {
             mItemsInReadingOrder.clear();
diff --git a/src/com/android/launcher3/folder/FolderIcon.java b/src/com/android/launcher3/folder/FolderIcon.java
index 1e4eb7f..4a4f7cf 100644
--- a/src/com/android/launcher3/folder/FolderIcon.java
+++ b/src/com/android/launcher3/folder/FolderIcon.java
@@ -183,10 +183,9 @@
         folder.setFolderIcon(icon);
         folder.bind(folderInfo);
         icon.setFolder(folder);
-
-        folderInfo.addListener(icon);
-
         icon.setOnFocusChangeListener(launcher.mFocusHandler);
+
+        folderInfo.setListener(new MultiFolderListener(folder, icon));
         return icon;
     }
 
@@ -944,11 +943,13 @@
         requestLayout();
     }
 
+    @Override
     public void onAdd(ShortcutInfo item) {
         invalidate();
         requestLayout();
     }
 
+    @Override
     public void onRemove(ShortcutInfo item) {
         invalidate();
         requestLayout();
diff --git a/src/com/android/launcher3/folder/MultiFolderListener.java b/src/com/android/launcher3/folder/MultiFolderListener.java
new file mode 100644
index 0000000..1030112
--- /dev/null
+++ b/src/com/android/launcher3/folder/MultiFolderListener.java
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2016 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.launcher3.folder;
+
+import com.android.launcher3.FolderInfo.FolderListener;
+import com.android.launcher3.ShortcutInfo;
+
+/**
+ * An implementation of {@link FolderListener} which passes the events to 2 children.
+ */
+public class MultiFolderListener implements FolderListener {
+
+    private final FolderListener mListener1;
+    private final FolderListener mListener2;
+
+    public MultiFolderListener(FolderListener listener1, FolderListener listener2) {
+        mListener1 = listener1;
+        mListener2 = listener2;
+    }
+
+    @Override
+    public void onAdd(ShortcutInfo item) {
+        mListener1.onAdd(item);
+        mListener2.onAdd(item);
+    }
+
+    @Override
+    public void onRemove(ShortcutInfo item) {
+        mListener1.onRemove(item);
+        mListener2.onRemove(item);
+    }
+
+    @Override
+    public void onItemsChanged(boolean animate) {
+        mListener1.onItemsChanged(animate);
+        mListener2.onItemsChanged(animate);
+    }
+}