diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java index c02449b03..2e0e0f32c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java @@ -20,8 +20,8 @@ import org.thoughtcrime.securesms.util.ViewUtil; import java.util.Collection; import java.util.Collections; -import java.util.HashMap; import java.util.HashSet; +import java.util.LinkedHashMap; import java.util.List; import java.util.Locale; import java.util.Map; @@ -42,7 +42,7 @@ class ConversationListAdapter extends PagedListAdapter batchSet = Collections.synchronizedMap(new HashMap<>()); + private final Map batchSet = Collections.synchronizedMap(new LinkedHashMap<>()); private boolean batchMode = false; private final Set typingSet = new HashSet<>(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java index 0cc7bdf2f..b6bcb40b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListFragment.java @@ -129,6 +129,7 @@ import org.whispersystems.libsignal.util.guava.Optional; import java.util.Collections; import java.util.HashSet; +import java.util.LinkedHashSet; import java.util.List; import java.util.Locale; import java.util.Objects; @@ -738,10 +739,10 @@ public class ConversationListFragment extends MainFragment implements ActionMode } private void handlePinAllSelected() { - final Set toPin = new HashSet<>(Stream.of(defaultAdapter.getBatchSelection()) - .filterNot(conversation -> conversation.getThreadRecord().isPinned()) - .map(conversation -> conversation.getThreadRecord().getThreadId()) - .toList()); + final Set toPin = new LinkedHashSet<>(Stream.of(defaultAdapter.getBatchSelection()) + .filterNot(conversation -> conversation.getThreadRecord().isPinned()) + .map(conversation -> conversation.getThreadRecord().getThreadId()) + .toList()); if (toPin.size() + viewModel.getPinnedCount() > MAXIMUM_PINNED_CONVERSATIONS) { Snackbar.make(fab, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java index 640e93d23..2c43fe55e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -623,9 +623,19 @@ public class ThreadDatabase extends Database { } public Cursor getUnarchivedConversationList(boolean pinned, long offset, long limit) { - SQLiteDatabase db = databaseHelper.getReadableDatabase(); - String query = createQuery(ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + PINNED + " = ?", offset, limit, false); - Cursor cursor = db.rawQuery(query, new String[]{pinned ? "1" : "0"}); + SQLiteDatabase db = databaseHelper.getReadableDatabase(); + String pinnedWhere = PINNED + (pinned ? " != 0" : " = 0"); + String where = ARCHIVED + " = 0 AND " + MESSAGE_COUNT + " != 0 AND " + pinnedWhere; + + final String query; + + if (pinned) { + query = createQuery(where, PINNED + " ASC", offset, limit, false); + } else { + query = createQuery(where, offset, limit, false); + } + + Cursor cursor = db.rawQuery(query, new String[]{}); setNotifyConversationListListeners(cursor); @@ -660,7 +670,7 @@ public class ThreadDatabase extends Database { public int getPinnedConversationListCount() { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String[] columns = new String[] { "COUNT(*)" }; - String query = ARCHIVED + " = 0 AND " + PINNED + " = 1 AND " + MESSAGE_COUNT + " != 0"; + String query = ARCHIVED + " = 0 AND " + PINNED + " != 0 AND " + MESSAGE_COUNT + " != 0"; try (Cursor cursor = db.query(TABLE_NAME, columns, query, null, null, null, null)) { if (cursor != null && cursor.moveToFirst()) { @@ -686,14 +696,25 @@ public class ThreadDatabase extends Database { } public void pinConversations(@NonNull Set threadIds) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - ContentValues contentValues = new ContentValues(1); - String placeholders = StringUtil.join(Stream.of(threadIds).map(unused -> "?").toList(), ","); - String selection = ID + " IN (" + placeholders + ")"; + SQLiteDatabase db = databaseHelper.getWritableDatabase(); - contentValues.put(PINNED, 1); + try { + db.beginTransaction(); - db.update(TABLE_NAME, contentValues, selection, SqlUtil.buildArgs(Stream.of(threadIds).toArray())); + int pinnedCount = getPinnedConversationListCount(); + + for (long threadId : threadIds) { + ContentValues contentValues = new ContentValues(1); + contentValues.put(PINNED, ++pinnedCount); + + db.update(TABLE_NAME, contentValues, ID_WHERE, SqlUtil.buildArgs(threadId)); + } + + db.setTransactionSuccessful(); + } finally { + db.endTransaction(); + notifyConversationListListeners(); + } notifyConversationListListeners(); } @@ -1102,6 +1123,11 @@ public class ThreadDatabase extends Database { private @NonNull String createQuery(@NonNull String where, long offset, long limit, boolean preferPinned) { String orderBy = (preferPinned ? TABLE_NAME + "." + PINNED + " DESC, " : "") + TABLE_NAME + "." + DATE + " DESC"; + + return createQuery(where, orderBy, offset, limit, preferPinned); + } + + private @NonNull String createQuery(@NonNull String where, @NonNull String orderBy, long offset, long limit, boolean preferPinned) { String projection = Util.join(COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION, ","); String query =