From ad51746e845cbc31c16ac599cab1188bc0674b84 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Mon, 8 Apr 2024 13:53:18 +1000 Subject: [PATCH 1/8] Initial fix implemented --- .../conversation/v2/ConversationActivityV2.kt | 11 ++++++----- .../thoughtcrime/securesms/database/MmsDatabase.kt | 3 +-- .../thoughtcrime/securesms/database/SmsDatabase.java | 1 + .../securesms/database/ThreadDatabase.java | 6 ++++-- 4 files changed, 12 insertions(+), 9 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index 00e72562d5..a9aab2d779 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1112,6 +1112,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val blindedRecipient = viewModel.blindedRecipient val binding = binding ?: return val openGroup = viewModel.openGroup + val (textResource, insertParam) = when { recipient.isLocalNumber -> R.string.activity_conversation_empty_state_note_to_self to null openGroup != null && !openGroup.canWrite -> R.string.activity_conversation_empty_state_read_only to recipient.toShortString() @@ -1883,11 +1884,13 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe return } + + val allSentByCurrentUser = messages.all { it.isOutgoing } val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id, it.isMms) != null } - // If the recipient is a community then we delete the message for everyone - if (recipient.isCommunityRecipient) { + // If the recipient is a community OR a Note-to-Self then we delete the message for everyone + if (recipient.isCommunityRecipient || recipient.isLocalNumber) { val messageCount = 1 // Only used for plurals string showSessionDialog { title(resources.getQuantityString(R.plurals.ConversationFragment_delete_selected_messages, messageCount, messageCount)) @@ -1917,8 +1920,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe } bottomSheet.show(supportFragmentManager, bottomSheet.tag) } - else // Finally, if this is a closed group and you are deleting someone else's message(s) - // then we can only delete locally. + else // Finally, if this is a closed group and you are deleting someone else's message(s) then we can only delete locally. { val messageCount = 1 showSessionDialog { @@ -2027,7 +2029,6 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe val message = messages.first() as MmsMessageRecord // Do not allow the user to download a file attachment before it has finished downloading - // TODO: Localise the msg in this toast! if (message.isMediaPending) { Toast.makeText(this, resources.getString(R.string.conversation_activity__wait_until_attachment_has_finished_downloading), Toast.LENGTH_LONG).show() return diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt index 0f39e3a496..f2fcefd0aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -1095,8 +1095,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa } val whereString = where.substring(0, where.length - 4) try { - cursor = - db!!.query(TABLE_NAME, arrayOf(ID), whereString, null, null, null, null) + cursor = db!!.query(TABLE_NAME, arrayOf(ID), whereString, null, null, null, null) val toDeleteStringMessageIds = mutableListOf() while (cursor.moveToNext()) { toDeleteStringMessageIds += cursor.getLong(0).toString() diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 61f17a2d86..450e7c2d23 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -633,6 +633,7 @@ public class SmsDatabase extends MessagingDatabase { SQLiteDatabase db = databaseHelper.getWritableDatabase(); long threadId = getThreadIdForMessage(messageId); db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); + notifyConversationListeners(threadId); boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); return threadDeleted; } 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 e2f9687451..5f71ce9330 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -515,11 +515,13 @@ public class ThreadDatabase extends Database { return getConversationList(where); } + /* public Cursor getArchivedConversationList() { String where = "(" + MESSAGE_COUNT + " != 0 OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + COMMUNITY_PREFIX + "%') " + "AND " + ARCHIVED + " = 1 "; return getConversationList(where); } + */ private Cursor getConversationList(String where) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); @@ -771,11 +773,11 @@ public class ThreadDatabase extends Database { deleteThread(threadId); return true; } + // todo: add empty snippet that clears existing data return false; } } finally { - if (reader != null) - reader.close(); + if (reader != null) { reader.close(); } notifyConversationListListeners(); notifyConversationListeners(threadId); } From dce0fd841303940943b8c675011d958ef46185e7 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Mon, 8 Apr 2024 14:08:40 +1000 Subject: [PATCH 2/8] Fixes #1448 --- .../securesms/conversation/v2/ConversationActivityV2.kt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt index a9aab2d779..7b9c3629f9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/ConversationActivityV2.kt @@ -1883,9 +1883,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe Log.w("ConversationActivityV2", "Asked to delete messages but could not obtain viewModel recipient - aborting.") return } - - - + val allSentByCurrentUser = messages.all { it.isOutgoing } val allHasHash = messages.all { lokiMessageDb.getMessageServerHash(it.id, it.isMms) != null } From 0ab0ecbb553cf3e31b725a4c9d68b751cef0dbd7 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Mon, 8 Apr 2024 15:23:00 +1000 Subject: [PATCH 3/8] Addressed PR feedback --- .../securesms/database/ThreadDatabase.java | 14 +------------- 1 file changed, 1 insertion(+), 13 deletions(-) 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 5f71ce9330..507088a0ad 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -515,14 +515,6 @@ public class ThreadDatabase extends Database { return getConversationList(where); } - /* - public Cursor getArchivedConversationList() { - String where = "(" + MESSAGE_COUNT + " != 0 OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + COMMUNITY_PREFIX + "%') " + - "AND " + ARCHIVED + " = 1 "; - return getConversationList(where); - } - */ - private Cursor getConversationList(String where) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); String query = createQuery(where, 0); @@ -752,10 +744,7 @@ public class ThreadDatabase extends Database { return true; } - MmsSmsDatabase.Reader reader = null; - - try { - reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId)); + try (MmsSmsDatabase.Reader reader = mmsSmsDatabase.readerFor(mmsSmsDatabase.getConversationSnippet(threadId))) { MessageRecord record = null; if (reader != null) { record = reader.getNext(); @@ -777,7 +766,6 @@ public class ThreadDatabase extends Database { return false; } } finally { - if (reader != null) { reader.close(); } notifyConversationListListeners(); notifyConversationListeners(threadId); } From 26fb268c76f9fb3d1b1fbaadac181c54a3d1d278 Mon Sep 17 00:00:00 2001 From: alansley Date: Thu, 18 Apr 2024 07:03:27 +1000 Subject: [PATCH 4/8] WIP --- .../org/thoughtcrime/securesms/database/SmsDatabase.java | 2 +- .../java/org/thoughtcrime/securesms/database/Storage.kt | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 450e7c2d23..7f92f366a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -634,7 +634,7 @@ public class SmsDatabase extends MessagingDatabase { long threadId = getThreadIdForMessage(messageId); db.delete(TABLE_NAME, ID_WHERE, new String[] {messageId+""}); notifyConversationListeners(threadId); - boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, true); + boolean threadDeleted = DatabaseComponent.get(context).threadDatabase().update(threadId, false, false); return threadDeleted; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 73dadda2cb..78e1814d51 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1371,7 +1371,12 @@ open class Storage( val threadDB = DatabaseComponent.get(context).threadDatabase() val groupDB = DatabaseComponent.get(context).groupDatabase() threadDB.deleteConversation(threadID) - val recipient = getRecipientForThread(threadID) ?: return + + val recipient = getRecipientForThread(threadID) + if (recipient == null) { Log.w(TAG, "Got null recipient when deleting conversation - aborting."); return } + + Log.w("[ACL]", "When deleting conversation, recipient is: ${recipient.name}") + when { recipient.isContactRecipient -> { if (recipient.isLocalNumber) return From 13902607b2d4393f672731a888847fa6ee734975 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Thu, 18 Apr 2024 08:51:04 +1000 Subject: [PATCH 5/8] Commit before converting SmsDatabase from Java to Kotlin --- .../securesms/database/SmsDatabase.java | 22 +++--- .../securesms/database/ThreadDatabase.java | 76 ++++++++++--------- .../database/helpers/SQLCipherOpenHelper.java | 2 +- 3 files changed, 53 insertions(+), 47 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java index 7f92f366a8..5166b4fdf3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -701,12 +701,7 @@ public class SmsDatabase extends MessagingDatabase { } } - /*package */void deleteThread(long threadId) { - SQLiteDatabase db = databaseHelper.getWritableDatabase(); - db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""}); - } - - /*package*/void deleteMessagesInThreadBeforeDate(long threadId, long date) { + void deleteMessagesInThreadBeforeDate(long threadId, long date) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); String where = THREAD_ID + " = ? AND (CASE " + TYPE; @@ -719,7 +714,12 @@ public class SmsDatabase extends MessagingDatabase { db.delete(TABLE_NAME, where, new String[] {threadId + ""}); } - /*package*/ void deleteThreads(Set threadIds) { + void deleteThread(long threadId) { + SQLiteDatabase db = databaseHelper.getWritableDatabase(); + db.delete(TABLE_NAME, THREAD_ID + " = ?", new String[] {threadId+""}); + } + + void deleteThreads(Set threadIds) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); String where = ""; @@ -727,23 +727,23 @@ public class SmsDatabase extends MessagingDatabase { where += THREAD_ID + " = '" + threadId + "' OR "; } - where = where.substring(0, where.length() - 4); + where = where.substring(0, where.length() - 4); // Remove the final: "' OR " db.delete(TABLE_NAME, where, null); } - /*package */ void deleteAllThreads() { + void deleteAllThreads() { SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.delete(TABLE_NAME, null, null); } - /*package*/ SQLiteDatabase beginTransaction() { + SQLiteDatabase beginTransaction() { SQLiteDatabase database = databaseHelper.getWritableDatabase(); database.beginTransaction(); return database; } - /*package*/ void endTransaction(SQLiteDatabase database) { + void endTransaction(SQLiteDatabase database) { database.setTransactionSuccessful(); database.endTransaction(); } 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 507088a0ad..c8605b1a59 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -26,14 +26,10 @@ import android.content.Context; import android.database.Cursor; import android.database.MergeCursor; import android.net.Uri; - import androidx.annotation.NonNull; import androidx.annotation.Nullable; - import com.annimon.stream.Stream; - import net.zetetic.database.sqlcipher.SQLiteDatabase; - import org.jetbrains.annotations.NotNull; import org.session.libsession.snode.SnodeAPI; import org.session.libsession.utilities.Address; @@ -61,14 +57,12 @@ import org.thoughtcrime.securesms.mms.Slide; import org.thoughtcrime.securesms.mms.SlideDeck; import org.thoughtcrime.securesms.notifications.MarkReadReceiver; import org.thoughtcrime.securesms.util.SessionMetaProtocol; - import java.io.Closeable; import java.util.Collections; import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; -import java.util.Set; public class ThreadDatabase extends Database { @@ -83,7 +77,7 @@ public class ThreadDatabase extends Database { public static final String TABLE_NAME = "thread"; public static final String ID = "_id"; - public static final String DATE = "date"; + public static final String THREAD_CREATION_DATE = "date"; public static final String MESSAGE_COUNT = "message_count"; public static final String ADDRESS = "recipient_ids"; public static final String SNIPPET = "snippet"; @@ -91,7 +85,7 @@ public class ThreadDatabase extends Database { public static final String READ = "read"; public static final String UNREAD_COUNT = "unread_count"; public static final String UNREAD_MENTION_COUNT = "unread_mention_count"; - public static final String TYPE = "type"; + public static final String DISTRIBUTION_TYPE = "type"; // See: DistributionTypes.kt private static final String ERROR = "error"; public static final String SNIPPET_TYPE = "snippet_type"; public static final String SNIPPET_URI = "snippet_uri"; @@ -101,27 +95,27 @@ public class ThreadDatabase extends Database { public static final String READ_RECEIPT_COUNT = "read_receipt_count"; public static final String EXPIRES_IN = "expires_in"; public static final String LAST_SEEN = "last_seen"; - public static final String HAS_SENT = "has_sent"; + public static final String HAS_SENT = "has_sent"; public static final String IS_PINNED = "is_pinned"; public static final String CREATE_TABLE = "CREATE TABLE " + TABLE_NAME + " (" + - ID + " INTEGER PRIMARY KEY, " + DATE + " INTEGER DEFAULT 0, " + + ID + " INTEGER PRIMARY KEY, " + THREAD_CREATION_DATE + " INTEGER DEFAULT 0, " + MESSAGE_COUNT + " INTEGER DEFAULT 0, " + ADDRESS + " TEXT, " + SNIPPET + " TEXT, " + SNIPPET_CHARSET + " INTEGER DEFAULT 0, " + READ + " INTEGER DEFAULT 1, " + - TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + + DISTRIBUTION_TYPE + " INTEGER DEFAULT 0, " + ERROR + " INTEGER DEFAULT 0, " + SNIPPET_TYPE + " INTEGER DEFAULT 0, " + SNIPPET_URI + " TEXT DEFAULT NULL, " + ARCHIVED + " INTEGER DEFAULT 0, " + STATUS + " INTEGER DEFAULT 0, " + DELIVERY_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + EXPIRES_IN + " INTEGER DEFAULT 0, " + LAST_SEEN + " INTEGER DEFAULT 0, " + HAS_SENT + " INTEGER DEFAULT 0, " + READ_RECEIPT_COUNT + " INTEGER DEFAULT 0, " + UNREAD_COUNT + " INTEGER DEFAULT 0);"; - public static final String[] CREATE_INDEXS = { + public static final String[] CREATE_INDEXES = { "CREATE INDEX IF NOT EXISTS thread_recipient_ids_index ON " + TABLE_NAME + " (" + ADDRESS + ");", "CREATE INDEX IF NOT EXISTS archived_count_index ON " + TABLE_NAME + " (" + ARCHIVED + ", " + MESSAGE_COUNT + ");", }; private static final String[] THREAD_PROJECTION = { - ID, DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, UNREAD_MENTION_COUNT, TYPE, ERROR, SNIPPET_TYPE, + ID, THREAD_CREATION_DATE, MESSAGE_COUNT, ADDRESS, SNIPPET, SNIPPET_CHARSET, READ, UNREAD_COUNT, UNREAD_MENTION_COUNT, DISTRIBUTION_TYPE, ERROR, SNIPPET_TYPE, SNIPPET_URI, ARCHIVED, STATUS, DELIVERY_RECEIPT_COUNT, EXPIRES_IN, LAST_SEEN, READ_RECEIPT_COUNT, IS_PINNED }; @@ -131,8 +125,8 @@ public class ThreadDatabase extends Database { private static final List COMBINED_THREAD_RECIPIENT_GROUP_PROJECTION = Stream.concat(Stream.concat(Stream.of(TYPED_THREAD_PROJECTION), Stream.of(RecipientDatabase.TYPED_RECIPIENT_PROJECTION)), - Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) - .toList(); + Stream.of(GroupDatabase.TYPED_GROUP_PROJECTION)) + .toList(); public static String getCreatePinnedCommand() { return "ALTER TABLE "+ TABLE_NAME + " " + @@ -158,11 +152,10 @@ public class ThreadDatabase extends Database { ContentValues contentValues = new ContentValues(4); long date = SnodeAPI.getNowWithOffset(); - contentValues.put(DATE, date - date % 1000); + contentValues.put(THREAD_CREATION_DATE, date - date % 1000); contentValues.put(ADDRESS, address.serialize()); - if (group) - contentValues.put(TYPE, distributionType); + if (group) contentValues.put(DISTRIBUTION_TYPE, distributionType); contentValues.put(MESSAGE_COUNT, 0); @@ -175,7 +168,7 @@ public class ThreadDatabase extends Database { long expiresIn, int readReceiptCount) { ContentValues contentValues = new ContentValues(7); - contentValues.put(DATE, date - date % 1000); + contentValues.put(THREAD_CREATION_DATE, date - date % 1000); contentValues.put(MESSAGE_COUNT, count); if (!body.isEmpty()) { contentValues.put(SNIPPET, body); @@ -187,19 +180,19 @@ public class ThreadDatabase extends Database { contentValues.put(READ_RECEIPT_COUNT, readReceiptCount); contentValues.put(EXPIRES_IN, expiresIn); - if (unarchive) { - contentValues.put(ARCHIVED, 0); - } + if (unarchive) { contentValues.put(ARCHIVED, 0); } SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); notifyConversationListListeners(); } + // ACL remove unused method + /* public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { ContentValues contentValues = new ContentValues(4); - contentValues.put(DATE, date - date % 1000); + contentValues.put(THREAD_CREATION_DATE, date - date % 1000); if (!snippet.isEmpty()) { contentValues.put(SNIPPET, snippet); } @@ -214,6 +207,7 @@ public class ThreadDatabase extends Database { db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); notifyConversationListListeners(); } + */ private void deleteThread(long threadId) { Recipient recipient = getRecipientForThreadId(threadId); @@ -226,6 +220,8 @@ public class ThreadDatabase extends Database { } } + // ACL remove unused methods + /* private void deleteThreads(Set threadIds) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); String where = ""; @@ -249,6 +245,7 @@ public class ThreadDatabase extends Database { addressCache.clear(); notifyConversationListListeners(); } + */ public void trimAllThreads(int length, ProgressListener listener) { Cursor cursor = null; @@ -356,18 +353,21 @@ public class ThreadDatabase extends Database { }}; } + // ACL remove unused method + /* public void setDistributionType(long threadId, int distributionType) { ContentValues contentValues = new ContentValues(1); - contentValues.put(TYPE, distributionType); + contentValues.put(CONVERSATION_TYPE, distributionType); SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); notifyConversationListListeners(); } + */ public void setDate(long threadId, long date) { ContentValues contentValues = new ContentValues(1); - contentValues.put(DATE, date); + contentValues.put(THREAD_CREATION_DATE, date); SQLiteDatabase db = databaseHelper.getWritableDatabase(); int updated = db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId+""}); if (updated > 0) notifyConversationListListeners(); @@ -375,11 +375,11 @@ public class ThreadDatabase extends Database { public int getDistributionType(long threadId) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + Cursor cursor = db.query(TABLE_NAME, new String[]{DISTRIBUTION_TYPE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); try { if (cursor != null && cursor.moveToNext()) { - return cursor.getInt(cursor.getColumnIndexOrThrow(TYPE)); + return cursor.getInt(cursor.getColumnIndexOrThrow(DISTRIBUTION_TYPE)); } return DistributionTypes.DEFAULT; @@ -469,7 +469,7 @@ public class ThreadDatabase extends Database { Cursor cursor = null; try { - String where = "SELECT " + DATE + " FROM " + TABLE_NAME + + String where = "SELECT " + THREAD_CREATION_DATE + " FROM " + TABLE_NAME + " LEFT OUTER JOIN " + RecipientDatabase.TABLE_NAME + " ON " + TABLE_NAME + "." + ADDRESS + " = " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.ADDRESS + " LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME + @@ -477,7 +477,7 @@ public class ThreadDatabase extends Database { " WHERE " + MESSAGE_COUNT + " != 0 AND " + ARCHIVED + " = 0 AND " + HAS_SENT + " = 0 AND " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.BLOCK + " = 0 AND " + RecipientDatabase.TABLE_NAME + "." + RecipientDatabase.APPROVED + " = 0 AND " + - GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL ORDER BY " + DATE + " DESC LIMIT 1"; + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " IS NULL ORDER BY " + THREAD_CREATION_DATE + " DESC LIMIT 1"; cursor = db.rawQuery(where, null); if (cursor != null && cursor.moveToFirst()) @@ -595,7 +595,7 @@ public class ThreadDatabase extends Database { public Long getLastUpdated(long threadId) { SQLiteDatabase db = databaseHelper.getReadableDatabase(); - Cursor cursor = db.query(TABLE_NAME, new String[]{DATE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); + Cursor cursor = db.query(TABLE_NAME, new String[]{THREAD_CREATION_DATE}, ID_WHERE, new String[]{String.valueOf(threadId)}, null, null, null); try { if (cursor != null && cursor.moveToFirst()) { @@ -622,6 +622,9 @@ public class ThreadDatabase extends Database { } public void deleteConversation(long threadId) { + + Log.w("[ACL]", "Hit ThreadDatabase.deleteConversation"); + DatabaseComponent.get(context).smsDatabase().deleteThread(threadId); DatabaseComponent.get(context).mmsDatabase().deleteThread(threadId); DatabaseComponent.get(context).draftDatabase().clearDrafts(threadId); @@ -733,10 +736,13 @@ public class ThreadDatabase extends Database { } public boolean update(long threadId, boolean unarchive, boolean shouldDeleteOnEmpty) { + + Log.w("[ACL]", "Hit ThreadDatabase.update!"); + MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); long count = mmsSmsDatabase.getConversationCount(threadId); - boolean shouldDeleteEmptyThread = shouldDeleteOnEmpty && deleteThreadOnEmpty(threadId); + boolean shouldDeleteEmptyThread = shouldDeleteOnEmpty && possibleToDeleteThreadOnEmpty(threadId); if (count == 0 && shouldDeleteEmptyThread) { deleteThread(threadId); @@ -810,7 +816,7 @@ public class ThreadDatabase extends Database { return setLastSeen(threadId, lastSeenTime); } - private boolean deleteThreadOnEmpty(long threadId) { + private boolean possibleToDeleteThreadOnEmpty(long threadId) { Recipient threadRecipient = getRecipientForThreadId(threadId); return threadRecipient != null && !threadRecipient.isCommunityRecipient(); } @@ -855,7 +861,7 @@ public class ThreadDatabase extends Database { " LEFT OUTER JOIN " + GroupDatabase.TABLE_NAME + " ON " + TABLE_NAME + "." + ADDRESS + " = " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " WHERE " + where + - " ORDER BY " + TABLE_NAME + "." + IS_PINNED + " DESC, " + TABLE_NAME + "." + DATE + " DESC"; + " ORDER BY " + TABLE_NAME + "." + IS_PINNED + " DESC, " + TABLE_NAME + "." + THREAD_CREATION_DATE + " DESC"; if (limit > 0) { query += " LIMIT " + limit; @@ -900,7 +906,7 @@ public class ThreadDatabase extends Database { public ThreadRecord getCurrent() { long threadId = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.ID)); - int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.TYPE)); + int distributionType = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.DISTRIBUTION_TYPE)); Address address = Address.fromSerialized(cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.ADDRESS))); Optional settings; @@ -916,7 +922,7 @@ public class ThreadDatabase extends Database { Recipient recipient = Recipient.from(context, address, settings, groupRecord, true); String body = cursor.getString(cursor.getColumnIndexOrThrow(ThreadDatabase.SNIPPET)); - long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.DATE)); + long date = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.THREAD_CREATION_DATE)); long count = cursor.getLong(cursor.getColumnIndexOrThrow(ThreadDatabase.MESSAGE_COUNT)); int unreadCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_COUNT)); int unreadMentionCount = cursor.getInt(cursor.getColumnIndexOrThrow(ThreadDatabase.UNREAD_MENTION_COUNT)); diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java index 7713043c2c..cd1988e83f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/helpers/SQLCipherOpenHelper.java @@ -357,7 +357,7 @@ public class SQLCipherOpenHelper extends SQLiteOpenHelper { executeStatements(db, SmsDatabase.CREATE_INDEXS); executeStatements(db, MmsDatabase.CREATE_INDEXS); executeStatements(db, AttachmentDatabase.CREATE_INDEXS); - executeStatements(db, ThreadDatabase.CREATE_INDEXS); + executeStatements(db, ThreadDatabase.CREATE_INDEXES); executeStatements(db, DraftDatabase.CREATE_INDEXS); executeStatements(db, GroupDatabase.CREATE_INDEXS); executeStatements(db, GroupReceiptDatabase.CREATE_INDEXES); From 2b464419776ac43f12654282d3c96cee3cf1263b Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Thu, 18 Apr 2024 14:29:55 +1000 Subject: [PATCH 6/8] Fixed conversation view closing + hopefully wrong status text displayed + deletion of contact on removal of last message in 1-on-1 convo --- .../conversation/start/ContactListAdapter.kt | 44 ++++++++++++------- .../securesms/database/Storage.kt | 11 ++--- .../repository/ConversationRepository.kt | 18 ++------ 3 files changed, 36 insertions(+), 37 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt index 68e2f975c9..29a94cdcd4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt @@ -4,10 +4,15 @@ import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView +import network.loki.messenger.R import network.loki.messenger.databinding.ContactSectionHeaderBinding import network.loki.messenger.databinding.ViewContactBinding import org.session.libsession.utilities.recipients.Recipient +import org.session.libsignal.utilities.Log +import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.mms.GlideRequests +import org.thoughtcrime.securesms.showSessionDialog +import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities sealed class ContactListItem { class Header(val name: String) : ContactListItem() @@ -35,11 +40,28 @@ class ContactListAdapter( binding.profilePictureView.update(contact.recipient) binding.nameTextView.text = contact.displayName binding.root.setOnClickListener { listener(contact.recipient) } + + // TODO: When we implement deleting contacts then probably do it here w/ something like: + /* + binding.root.setOnLongClickListener { + Log.w("[ACL]", "Long clicked on contact ${contact.recipient.name}") + binding.contentView.context.showSessionDialog { + title("Delete Contact") + text("Are you sure you want to delete this contact?") + button(R.string.delete) { + val contacts = configFactory.contacts ?: return + contacts.upsertContact(contact.recipient.address.serialize()) { priority = PRIORITY_HIDDEN } + ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) + endActionMode() + } + cancelButton(::endActionMode) + } + true + } + */ } - fun unbind() { - binding.profilePictureView.recycle() - } + fun unbind() { binding.profilePictureView.recycle() } } class HeaderViewHolder( @@ -52,15 +74,11 @@ class ContactListAdapter( } } - override fun getItemCount(): Int { - return items.size - } + override fun getItemCount(): Int { return items.size } override fun onViewRecycled(holder: RecyclerView.ViewHolder) { super.onViewRecycled(holder) - if (holder is ContactViewHolder) { - holder.unbind() - } + if (holder is ContactViewHolder) { holder.unbind() } } override fun getItemViewType(position: Int): Int { @@ -72,13 +90,9 @@ class ContactListAdapter( override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder { return if (viewType == ViewType.Contact) { - ContactViewHolder( - ViewContactBinding.inflate(LayoutInflater.from(context), parent, false) - ) + ContactViewHolder(ViewContactBinding.inflate(LayoutInflater.from(context), parent, false)) } else { - HeaderViewHolder( - ContactSectionHeaderBinding.inflate(LayoutInflater.from(context), parent, false) - ) + HeaderViewHolder(ContactSectionHeaderBinding.inflate(LayoutInflater.from(context), parent, false)) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 78e1814d51..380b816f24 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -99,7 +99,7 @@ private const val TAG = "Storage" open class Storage( context: Context, helper: SQLCipherOpenHelper, - private val configFactory: ConfigFactory + val configFactory: ConfigFactory ) : Database(context, helper), StorageProtocol, ThreadDatabase.ConversationThreadUpdateListener { override fun threadCreated(address: Address, threadId: Long) { @@ -1378,12 +1378,9 @@ open class Storage( Log.w("[ACL]", "When deleting conversation, recipient is: ${recipient.name}") when { - recipient.isContactRecipient -> { - if (recipient.isLocalNumber) return - val contacts = configFactory.contacts ?: return - contacts.upsertContact(recipient.address.serialize()) { priority = PRIORITY_HIDDEN } - ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) - } + // Note: We don't do anything if the thread is a 1-on-1 and the recipient is a contact + // of ours (i.e., when recipient.isContactRecipient) + recipient.isClosedGroupRecipient -> { // TODO: handle closed group val volatile = configFactory.convoVolatile ?: return diff --git a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt index 384f47d4d6..8a7a2dfd0f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/repository/ConversationRepository.kt @@ -4,19 +4,14 @@ import network.loki.messenger.libsession_util.util.ExpiryMode import android.content.ContentResolver import android.content.Context - import app.cash.copper.Query import app.cash.copper.flow.observeQuery - import dagger.hilt.android.qualifiers.ApplicationContext - import kotlin.coroutines.resume import kotlin.coroutines.resumeWithException import kotlin.coroutines.suspendCoroutine - import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.map - import org.session.libsession.database.MessageDataProvider import org.session.libsession.messaging.messages.Destination import org.session.libsession.messaging.messages.control.MessageRequestResponse @@ -32,9 +27,7 @@ import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.TextSecurePreferences import org.session.libsession.utilities.recipients.Recipient import org.session.libsignal.utilities.Log - import org.session.libsignal.utilities.toHexString - import org.thoughtcrime.securesms.database.DatabaseContentProviders import org.thoughtcrime.securesms.database.DraftDatabase import org.thoughtcrime.securesms.database.ExpirationConfigurationDatabase @@ -51,7 +44,6 @@ import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.ThreadRecord import org.thoughtcrime.securesms.dependencies.ConfigFactory import org.thoughtcrime.securesms.dependencies.DatabaseComponent - import javax.inject.Inject interface ConversationRepository { @@ -239,7 +231,7 @@ class DefaultConversationRepository @Inject constructor( .success { continuation.resume(ResultOf.Success(Unit)) }.fail { error -> - Log.w("[onversationRepository", "Call to SnodeAPI.deleteMessage failed - attempting to resume..") + Log.w("ConversationRepository", "Call to SnodeAPI.deleteMessage failed - attempting to resume..") continuation.resumeWithException(error) } } @@ -330,9 +322,7 @@ class DefaultConversationRepository @Inject constructor( while (reader.next != null) { deleteMessageRequest(reader.current) val recipient = reader.current.recipient - if (block) { - setBlocked(recipient, true) - } + if (block) { setBlocked(recipient, true) } } } return ResultOf.Success(Unit) @@ -359,9 +349,7 @@ class DefaultConversationRepository @Inject constructor( val cursor = mmsSmsDb.getConversation(threadId, true) mmsSmsDb.readerFor(cursor).use { reader -> while (reader.next != null) { - if (!reader.current.isOutgoing) { - return true - } + if (!reader.current.isOutgoing) { return true } } } return false From 3b3935f9be0b05250be4ee298224463932827e28 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Thu, 18 Apr 2024 14:53:37 +1000 Subject: [PATCH 7/8] Cleanup for PR review --- .../conversation/start/ContactListAdapter.kt | 7 +----- .../securesms/database/Storage.kt | 7 +++--- .../securesms/database/ThreadDatabase.java | 22 +++---------------- 3 files changed, 8 insertions(+), 28 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt index 29a94cdcd4..245b14da56 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/start/ContactListAdapter.kt @@ -4,15 +4,10 @@ import android.content.Context import android.view.LayoutInflater import android.view.ViewGroup import androidx.recyclerview.widget.RecyclerView -import network.loki.messenger.R import network.loki.messenger.databinding.ContactSectionHeaderBinding import network.loki.messenger.databinding.ViewContactBinding import org.session.libsession.utilities.recipients.Recipient -import org.session.libsignal.utilities.Log -import org.thoughtcrime.securesms.database.Storage import org.thoughtcrime.securesms.mms.GlideRequests -import org.thoughtcrime.securesms.showSessionDialog -import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities sealed class ContactListItem { class Header(val name: String) : ContactListItem() @@ -41,7 +36,7 @@ class ContactListAdapter( binding.nameTextView.text = contact.displayName binding.root.setOnClickListener { listener(contact.recipient) } - // TODO: When we implement deleting contacts then probably do it here w/ something like: + // TODO: When we implement deleting contacts (hide might be safest) then probably set a long-click listener here w/ something like: /* binding.root.setOnLongClickListener { Log.w("[ACL]", "Long clicked on contact ${contact.recipient.name}") diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 380b816f24..9bc1195f74 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1373,9 +1373,10 @@ open class Storage( threadDB.deleteConversation(threadID) val recipient = getRecipientForThread(threadID) - if (recipient == null) { Log.w(TAG, "Got null recipient when deleting conversation - aborting."); return } - - Log.w("[ACL]", "When deleting conversation, recipient is: ${recipient.name}") + if (recipient == null) { + Log.w(TAG, "Got null recipient when deleting conversation - aborting."); + return + } when { // Note: We don't do anything if the thread is a 1-on-1 and the recipient is a contact 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 c8605b1a59..f799a295e5 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java @@ -63,6 +63,7 @@ import java.util.HashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; public class ThreadDatabase extends Database { @@ -187,8 +188,6 @@ public class ThreadDatabase extends Database { notifyConversationListListeners(); } - // ACL remove unused method - /* public void updateSnippet(long threadId, String snippet, @Nullable Uri attachment, long date, long type, boolean unarchive) { ContentValues contentValues = new ContentValues(4); @@ -207,7 +206,6 @@ public class ThreadDatabase extends Database { db.update(TABLE_NAME, contentValues, ID + " = ?", new String[] {threadId + ""}); notifyConversationListListeners(); } - */ private void deleteThread(long threadId) { Recipient recipient = getRecipientForThreadId(threadId); @@ -220,15 +218,11 @@ public class ThreadDatabase extends Database { } } - // ACL remove unused methods - /* private void deleteThreads(Set threadIds) { SQLiteDatabase db = databaseHelper.getWritableDatabase(); String where = ""; - for (long threadId : threadIds) { - where += ID + " = '" + threadId + "' OR "; - } + for (long threadId : threadIds) { where += ID + " = '" + threadId + "' OR "; } where = where.substring(0, where.length() - 4); @@ -245,7 +239,6 @@ public class ThreadDatabase extends Database { addressCache.clear(); notifyConversationListListeners(); } - */ public void trimAllThreads(int length, ProgressListener listener) { Cursor cursor = null; @@ -353,17 +346,14 @@ public class ThreadDatabase extends Database { }}; } - // ACL remove unused method - /* public void setDistributionType(long threadId, int distributionType) { ContentValues contentValues = new ContentValues(1); - contentValues.put(CONVERSATION_TYPE, distributionType); + contentValues.put(DISTRIBUTION_TYPE, distributionType); SQLiteDatabase db = databaseHelper.getWritableDatabase(); db.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {threadId + ""}); notifyConversationListListeners(); } - */ public void setDate(long threadId, long date) { ContentValues contentValues = new ContentValues(1); @@ -622,9 +612,6 @@ public class ThreadDatabase extends Database { } public void deleteConversation(long threadId) { - - Log.w("[ACL]", "Hit ThreadDatabase.deleteConversation"); - DatabaseComponent.get(context).smsDatabase().deleteThread(threadId); DatabaseComponent.get(context).mmsDatabase().deleteThread(threadId); DatabaseComponent.get(context).draftDatabase().clearDrafts(threadId); @@ -736,9 +723,6 @@ public class ThreadDatabase extends Database { } public boolean update(long threadId, boolean unarchive, boolean shouldDeleteOnEmpty) { - - Log.w("[ACL]", "Hit ThreadDatabase.update!"); - MmsSmsDatabase mmsSmsDatabase = DatabaseComponent.get(context).mmsSmsDatabase(); long count = mmsSmsDatabase.getConversationCount(threadId); From a7e255091bc4d236b532418be3ab802f2b78fd46 Mon Sep 17 00:00:00 2001 From: Al Lansley Date: Fri, 19 Apr 2024 07:43:15 +1000 Subject: [PATCH 8/8] Implemented PR feedback --- .../securesms/database/Storage.kt | 33 +++++++++---------- 1 file changed, 15 insertions(+), 18 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt index 9bc1195f74..354ec05c46 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt @@ -1378,25 +1378,22 @@ open class Storage( return } - when { - // Note: We don't do anything if the thread is a 1-on-1 and the recipient is a contact - // of ours (i.e., when recipient.isContactRecipient) + // There is nothing further we need to do if this is a 1-on-1 conversation, and it's not + // possible to delete communities in this manner so bail. + if (recipient.isContactRecipient || recipient.isCommunityRecipient) return - recipient.isClosedGroupRecipient -> { - // TODO: handle closed group - val volatile = configFactory.convoVolatile ?: return - val groups = configFactory.userGroups ?: return - val groupID = recipient.address.toGroupString() - val closedGroup = getGroup(groupID) - val groupPublicKey = GroupUtil.doubleDecodeGroupId(recipient.address.serialize()) - if (closedGroup != null) { - groupDB.delete(groupID) // TODO: Should we delete the group? (seems odd to leave it) - volatile.eraseLegacyClosedGroup(groupPublicKey) - groups.eraseLegacyGroup(groupPublicKey) - } else { - Log.w("Loki-DBG", "Failed to find a closed group for ${groupPublicKey.take(4)}") - } - } + // If we get here then this is a closed group conversation (i.e., recipient.isClosedGroupRecipient) + val volatile = configFactory.convoVolatile ?: return + val groups = configFactory.userGroups ?: return + val groupID = recipient.address.toGroupString() + val closedGroup = getGroup(groupID) + val groupPublicKey = GroupUtil.doubleDecodeGroupId(recipient.address.serialize()) + if (closedGroup != null) { + groupDB.delete(groupID) + volatile.eraseLegacyClosedGroup(groupPublicKey) + groups.eraseLegacyGroup(groupPublicKey) + } else { + Log.w("Loki-DBG", "Failed to find a closed group for ${groupPublicKey.take(4)}") } }