Merge "Improve threaded behavior in ConversationCursor"
diff --git a/src/com/android/mail/browse/ConversationCursor.java b/src/com/android/mail/browse/ConversationCursor.java
index edba7b6..af9e0ab 100644
--- a/src/com/android/mail/browse/ConversationCursor.java
+++ b/src/com/android/mail/browse/ConversationCursor.java
@@ -36,7 +36,6 @@
import com.android.mail.providers.Conversation;
import com.android.mail.providers.UIProvider;
import com.android.mail.providers.UIProvider.ConversationOperations;
-import com.android.mail.utils.LogUtils;
import com.google.common.annotations.VisibleForTesting;
import java.util.ArrayList;
@@ -107,13 +106,8 @@
// Parameters passed to the underlying query
private static Uri qUri;
private static String[] qProjection;
- private static String qSelection;
- private static String[] qSelectionArgs;
- private static String qSortOrder;
private ConversationCursor(Cursor cursor, Activity activity, String messageListColumn) {
- sActivity = activity;
- mResolver = activity.getContentResolver();
sConversationCursor = this;
// If we have an existing underlying cursor, make sure it's closed
if (sUnderlyingCursor != null) {
@@ -134,20 +128,6 @@
}
/**
- * Determine if two strings (which might be null) are the same
- * @param str1 a String or null
- * @param str2 a String or null
- * @return true if the strings are both null or equals
- */
- private static boolean nullOrEquals(String str1, String str2) {
- if (str1 == null) {
- return str2 == null;
- } else {
- return str1.equals(str2);
- }
- }
-
- /**
* Create a ConversationCursor; this should be called by the ListActivity using that cursor
* @param activity the activity creating the cursor
* @param messageListColumn the column used for individual cursor items
@@ -160,40 +140,49 @@
*/
public static ConversationCursor create(Activity activity, String messageListColumn, Uri uri,
String[] projection, String selection, String[] selectionArgs, String sortOrder) {
- // First, let's see if we already have a cursor
- if (sConversationCursor != null) {
- // If it's the same, just clean up
- if (qUri.equals(uri) && !sRefreshRequired && !sRefreshInProgress &&
- nullOrEquals(selection, qSelection) && nullOrEquals(sortOrder, qSortOrder)) {
- if (sRefreshReady) {
- // If we already have a refresh ready, just sync() it
- LogUtils.d(TAG, "Create with refreshed cursor; sync");
- sConversationCursor.sync();
-
- } else {
- // Position the cursor before the first item (as it would be if new), reset
- // the cache, and return as new
- LogUtils.d(TAG, "Create without refresh; reset cursor");
- sConversationCursor.moveToPosition(-1);
- sConversationCursor.mPosition = -1;
- synchronized (sCacheMapLock) {
- sCacheMap.clear();
+ sActivity = activity;
+ mResolver = activity.getContentResolver();
+ if (selection != null || sortOrder != null) {
+ throw new IllegalArgumentException(
+ "Selection and sort order aren't allowed in ConversationCursors");
+ }
+ synchronized (sCacheMapLock) {
+ // First, let's see if we already have a cursor
+ if (sConversationCursor != null) {
+ // If it's the same, just clean up
+ if (qUri.equals(uri) && !sRefreshRequired && !sRefreshInProgress) {
+ if (sRefreshReady) {
+ // If we already have a refresh ready, just sync() it
+ Log.d(TAG, "Create: refreshed cursor ready, sync");
+ sConversationCursor.sync();
+ } else {
+ // Position the cursor before the first item (as it would be if new), reset
+ // the cache, and return as new
+ Log.d(TAG, "Create: cursor good, reset position and clear map");
+ sConversationCursor.moveToPosition(-1);
+ sConversationCursor.mPosition = -1;
+ synchronized (sCacheMapLock) {
+ sCacheMap.clear();
+ }
}
+ } else {
+ // Set qUri/qProjection these in case they changed
+ Log.d(TAG, "Create: new query or refresh needed, query/sync");
+ sRequeryCursor = doQuery(uri, projection);
+ sConversationCursor.sync();
}
return sConversationCursor;
- } else {
- LogUtils.d(TAG, "Create with new query; closing existing ConversationCursor");
- sConversationCursor.close();
}
+ // Create new ConversationCursor
+ Log.d(TAG, "Create: initial creation");
+ return new ConversationCursor(doQuery(uri, projection), activity, messageListColumn);
}
+ }
+
+ private static Cursor doQuery(Uri uri, String[] projection) {
qUri = uri;
qProjection = projection;
- qSelection = selection;
- qSelectionArgs = selectionArgs;
- qSortOrder = sortOrder;
- Cursor cursor = activity.getContentResolver().query(uri, projection, selection,
- selectionArgs, sortOrder);
- return new ConversationCursor(cursor, activity, messageListColumn);
+ return mResolver.query(qUri, qProjection, null, null, null);
}
/**
@@ -416,16 +405,20 @@
// NOTE: We don't like the name (it implies syncing with the server); suggestions gladly
// taken - reset? syncToUnderlying? completeRefresh? align?
public void sync() {
- if (DEBUG) {
- Log.d(TAG, "[sync() called]");
+ synchronized (sCacheMapLock) {
+ if (DEBUG) {
+ Log.d(TAG, "[sync() called]");
+ }
+ if (sRequeryCursor == null) {
+ // This can happen during an animated deletion, if the UI isn't keeping track
+ // If we have no new data, this is a noop
+ Log.w(TAG, "UI calling sync() out of sequence");
+ }
+ resetCursor(sRequeryCursor);
+ sRequeryCursor = null;
+ sRefreshInProgress = false;
+ sRefreshReady = false;
}
- if (sRequeryCursor == null) {
- throw new IllegalStateException("Can't swap cursors; no requery done");
- }
- resetCursor(sRequeryCursor);
- sRequeryCursor = null;
- sRefreshInProgress = false;
- sRefreshReady = false;
}
public boolean isRefreshRequired() {
@@ -446,6 +439,7 @@
synchronized(sCacheMapLock) {
// Mark the requery closed
sRefreshInProgress = false;
+ sRefreshReady = false;
// If we have the cursor, close it; otherwise, it will get closed when the query
// finishes (it checks sRequeryInProgress)
if (sRequeryCursor != null) {
@@ -533,12 +527,12 @@
@Override
public void run() {
// Get new data
- sRequeryCursor =
- mResolver.query(qUri, qProjection, qSelection, qSelectionArgs, qSortOrder);
+ sRequeryCursor = doQuery(qUri, qProjection);
// Make sure window is full
synchronized(sCacheMapLock) {
if (sRefreshInProgress) {
sRequeryCursor.getCount();
+ sRefreshReady = true;
sActivity.runOnUiThread(new Runnable() {
@Override
public void run() {
@@ -551,7 +545,6 @@
listener.onRefreshReady();
}
}
- sRefreshReady = true;
}
}});
} else {