Merge "Properly manage lifecycle of suggestion cursor" into jb-dev
diff --git a/src/com/android/contacts/list/JoinContactListFragment.java b/src/com/android/contacts/list/JoinContactListFragment.java
index f8fc4cd..1c526a5 100644
--- a/src/com/android/contacts/list/JoinContactListFragment.java
+++ b/src/com/android/contacts/list/JoinContactListFragment.java
@@ -16,6 +16,7 @@
 package com.android.contacts.list;
 
 import com.android.contacts.R;
+import com.android.contacts.list.JoinContactLoader.JoinContactLoaderResult;
 
 import android.app.Activity;
 import android.app.LoaderManager.LoaderCallbacks;
@@ -78,13 +79,14 @@
                     break;
                 }
                 case JoinContactListAdapter.PARTITION_ALL_CONTACTS: {
-                    Cursor suggestionsCursor = ((JoinContactLoader) loader).getSuggestionsCursor();
+                    Cursor suggestionsCursor = ((JoinContactLoaderResult) data).suggestionCursor;
                     onContactListLoaded(suggestionsCursor, data);
                     break;
                 }
             }
         }
 
+        @Override
         public void onLoaderReset(Loader<Cursor> loader) {
         }
     };
diff --git a/src/com/android/contacts/list/JoinContactLoader.java b/src/com/android/contacts/list/JoinContactLoader.java
index c43560e..beb5208 100644
--- a/src/com/android/contacts/list/JoinContactLoader.java
+++ b/src/com/android/contacts/list/JoinContactLoader.java
@@ -18,19 +18,46 @@
 import android.content.Context;
 import android.content.CursorLoader;
 import android.database.Cursor;
-import android.database.MatrixCursor;
+import android.database.CursorWrapper;
 import android.net.Uri;
-import android.util.Log;
 
 /**
  * A specialized loader for the Join Contacts UI.  It executes two queries:
  * join suggestions and (optionally) the full contact list.
+ *
+ * This loader also loads the "suggestion" cursor, which can be accessed with:
+ * {@code ((JoinContactLoaderResult) result).suggestionCursor }
  */
 public class JoinContactLoader extends CursorLoader {
 
     private String[] mProjection;
     private Uri mSuggestionUri;
-    private Cursor mSuggestionsCursor;
+
+    /**
+     * Actual returned class.  It's guaranteed that this loader always returns an instance of this
+     * class.  This class is needed to tie the lifecycle of the second cursor to that of the
+     * primary one.
+     *
+     * Note we can't change the result type of this loader itself, because CursorLoader
+     * extends AsyncTaskLoader<Cursor>, not AsyncTaskLoader<? extends Cursor>
+     */
+    public static class JoinContactLoaderResult extends CursorWrapper {
+        public final Cursor suggestionCursor;
+
+        public JoinContactLoaderResult(Cursor baseCursor, Cursor suggestionCursor) {
+            super(baseCursor);
+            this.suggestionCursor = suggestionCursor;
+        }
+
+        @Override
+        public void close() {
+            try {
+                suggestionCursor.close();
+            } finally {
+                super.close();
+            }
+        }
+    }
 
     public JoinContactLoader(Context context) {
         super(context, null, null, null, null, null);
@@ -46,16 +73,12 @@
         this.mProjection = projection;
     }
 
-    public Cursor getSuggestionsCursor() {
-        return mSuggestionsCursor;
-    }
-
     @Override
     public Cursor loadInBackground() {
         // First execute the suggestions query, then call super.loadInBackground
         // to load the entire list
-        mSuggestionsCursor = getContext().getContentResolver()
+        final Cursor suggestionsCursor = getContext().getContentResolver()
                 .query(mSuggestionUri, mProjection, null, null, null);
-        return super.loadInBackground();
+        return new JoinContactLoaderResult(super.loadInBackground(), suggestionsCursor);
     }
 }
\ No newline at end of file