diff --git a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt index d74174fecb..06e344a239 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/attachments/DatabaseAttachmentProvider.kt @@ -145,6 +145,12 @@ class DatabaseAttachmentProvider(context: Context, helper: SQLCipherOpenHelper) return smsDatabase.isOutgoingMessage(timestamp) || mmsDatabase.isOutgoingMessage(timestamp) } + override fun isDeletedMessage(timestamp: Long): Boolean { + val smsDatabase = DatabaseComponent.get(context).smsDatabase() + val mmsDatabase = DatabaseComponent.get(context).mmsDatabase() + return smsDatabase.isDeletedMessage(timestamp) || mmsDatabase.isDeletedMessage(timestamp) + } + override fun handleSuccessfulAttachmentUpload(attachmentId: Long, attachmentStream: SignalServiceAttachmentStream, attachmentKey: ByteArray, uploadResult: UploadResult) { val database = DatabaseComponent.get(context).attachmentDatabase() val databaseAttachment = getDatabaseAttachment(attachmentId) ?: return 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 9cdb76cf25..485fcaca5f 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 @@ -851,8 +851,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe binding.messageRequestBar.visibility = View.GONE } if (!uiState.conversationExists && !isFinishing) { - // Conversation should be deleted now, go to homepage with a cleared stack - baseContext.startHomeActivity(isFromOnboarding = false, isNewAccount = false) + // Conversation should be deleted now + finish() } // show or hide the text input diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt index abadc06335..ea265bdbbf 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/menus/ConversationActionModeCallback.kt @@ -42,18 +42,6 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p val blindedPublicKey = openGroup?.publicKey?.let { SodiumUtilities.blindedKeyPair(it, edKeyPair)?.publicKey?.asBytes } ?.let { AccountId(IdPrefix.BLINDED, it) }?.hexString - // Embedded function - fun userCanDeleteSelectedItems(): Boolean { - // admin can delete all combinations - if(adapter.isAdmin) return true - - val allSentByCurrentUser = selectedItems.all { it.isOutgoing } - val allReceivedByCurrentUser = selectedItems.all { !it.isOutgoing } - if (openGroup == null) { return allSentByCurrentUser || allReceivedByCurrentUser } - if (allSentByCurrentUser) { return true } - return OpenGroupManager.isUserModerator(context, openGroup.groupId, userPublicKey, blindedPublicKey) - } - // Embedded function fun userCanBanSelectedUsers(): Boolean { if (openGroup == null) { return false } @@ -67,7 +55,7 @@ class ConversationActionModeCallback(private val adapter: ConversationAdapter, p // Delete message - menu.findItem(R.id.menu_context_delete_message).isVisible = userCanDeleteSelectedItems() + menu.findItem(R.id.menu_context_delete_message).isVisible = true // can always delete since delete logic will be handled by the VM // Ban user menu.findItem(R.id.menu_context_ban_user).isVisible = userCanBanSelectedUsers() // Ban and delete all 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 63460744f6..60c4de6883 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -106,6 +106,23 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa .any { MmsSmsColumns.Types.isOutgoingMessageType(it) } } + fun isDeletedMessage(timestamp: Long): Boolean = + databaseHelper.writableDatabase.query( + TABLE_NAME, + arrayOf(ID, THREAD_ID, MESSAGE_BOX, ADDRESS), + DATE_SENT + " = ?", + arrayOf(timestamp.toString()), + null, + null, + null, + null + ).use { cursor -> + cursor.asSequence() + .map { cursor.getColumnIndexOrThrow(MESSAGE_BOX) } + .map(cursor::getLong) + .any { MmsSmsColumns.Types.isDeletedMessage(it) } + } + fun incrementReceiptCount( messageId: SyncMessageId, timestamp: Long, diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java index b737be855e..510272cfb8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsSmsDatabase.java @@ -17,6 +17,9 @@ package org.thoughtcrime.securesms.database; import static org.thoughtcrime.securesms.database.MmsDatabase.MESSAGE_BOX; +import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_DELETED_INCOMING_TYPE; +import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_DELETED_OUTGOING_TYPE; +import static org.thoughtcrime.securesms.database.MmsSmsColumns.Types.BASE_TYPE_MASK; import android.content.Context; import android.database.Cursor; @@ -96,6 +99,14 @@ public class MmsSmsDatabase extends Database { } } + public @Nullable MessageRecord getNonDeletedMessageForTimestamp(long timestamp) { + String selection = MmsSmsColumns.NORMALIZED_DATE_SENT + " = " + timestamp; + try (Cursor cursor = queryTables(PROJECTION, selection, null, null)) { + MmsSmsDatabase.Reader reader = readerFor(cursor); + return reader.getNext(); + } + } + public @Nullable MessageRecord getMessageFor(long timestamp, String serializedAuthor) { return getMessageFor(timestamp, serializedAuthor, true); } @@ -323,7 +334,9 @@ public class MmsSmsDatabase extends Database { public long getLastMessageTimestamp(long threadId) { String order = MmsSmsColumns.NORMALIZED_DATE_SENT + " DESC"; - String selection = MmsSmsColumns.THREAD_ID + " = " + threadId; + // make sure the last message isn't marked as deleted + String selection = MmsSmsColumns.THREAD_ID + " = " + threadId + " AND " + + "(ifnull("+SmsDatabase.TYPE+", "+MmsDatabase.MESSAGE_BOX+") & "+BASE_TYPE_MASK+") NOT IN ("+BASE_DELETED_OUTGOING_TYPE+", "+BASE_DELETED_INCOMING_TYPE+")"; // this ugly line checks whether the type is deleted (incoming or outgoing) for either the sms table or the mms table try (Cursor cursor = queryTables(PROJECTION, selection, order, "1")) { if (cursor.moveToFirst()) { 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 5088b76d29..ed7945e8a3 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/SmsDatabase.java @@ -243,6 +243,7 @@ public class SmsDatabase extends MessagingDatabase { contentValues.put(READ, 1); contentValues.put(BODY, displayedMessage); contentValues.put(HAS_MENTION, 0); + contentValues.put(STATUS, Status.STATUS_NONE); database.update(TABLE_NAME, contentValues, ID_WHERE, new String[] {String.valueOf(messageId)}); updateTypeBitmask(messageId, Types.BASE_TYPE_MASK, @@ -299,6 +300,28 @@ public class SmsDatabase extends MessagingDatabase { return isOutgoing; } + public boolean isDeletedMessage(long timestamp) { + SQLiteDatabase database = databaseHelper.getWritableDatabase(); + Cursor cursor = null; + boolean isDeleted = false; + + try { + cursor = database.query(TABLE_NAME, new String[] { ID, THREAD_ID, ADDRESS, TYPE }, + DATE_SENT + " = ?", new String[] { String.valueOf(timestamp) }, + null, null, null, null); + + while (cursor.moveToNext()) { + if (Types.isDeletedMessage(cursor.getLong(cursor.getColumnIndexOrThrow(TYPE)))) { + isDeleted = true; + } + } + } finally { + if (cursor != null) cursor.close(); + } + + return isDeleted; + } + public void incrementReceiptCount(SyncMessageId messageId, boolean deliveryReceipt, boolean readReceipt) { SQLiteDatabase database = databaseHelper.getWritableDatabase(); Cursor cursor = null; diff --git a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt index 5a19263cac..cfbd03bb57 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/home/HomeActivity.kt @@ -372,6 +372,11 @@ class HomeActivity : PassphraseRequiredActionBarActivity(), binding.seedReminderView.isVisible = false } + // refresh search on resume, in case we a conversation was deleted + if (binding.globalSearchRecycler.isVisible){ + globalSearchViewModel.refresh() + } + updateLegacyConfigView() // Sync config changes if there are any diff --git a/app/src/main/res/drawable-hdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-hdpi/ic_trash_filled_32.png deleted file mode 100644 index 586af53d08..0000000000 Binary files a/app/src/main/res/drawable-hdpi/ic_trash_filled_32.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-mdpi/ic_trash_filled_32.png deleted file mode 100644 index 0b1351e1c9..0000000000 Binary files a/app/src/main/res/drawable-mdpi/ic_trash_filled_32.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xhdpi/ic_trash_filled_32.png deleted file mode 100644 index 3d9e02a1f1..0000000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_trash_filled_32.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png deleted file mode 100644 index 6a4ccab1b6..0000000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_trash_filled_32.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png b/app/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png deleted file mode 100644 index 7ddfecdadb..0000000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_trash_filled_32.png and /dev/null differ diff --git a/app/src/main/res/layout/image_editor_hud.xml b/app/src/main/res/layout/image_editor_hud.xml index d78afa6686..6a9538d603 100644 --- a/app/src/main/res/layout/image_editor_hud.xml +++ b/app/src/main/res/layout/image_editor_hud.xml @@ -31,7 +31,8 @@ android:layout_height="wrap_content" android:background="?attr/selectableItemBackgroundBorderless" android:padding="8dp" - android:src="@drawable/ic_trash_filled_32" /> + app:tint="@color/white" + android:src="@drawable/ic_delete" /> ? diff --git a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt index 52d56184cc..2010a58e61 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/jobs/MessageSendJob.kt @@ -37,6 +37,13 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job { val message = message as? VisibleMessage val storage = MessagingModuleConfiguration.shared.storage + // do not attempt to send if the message is marked as deleted + message?.sentTimestamp?.let{ + if(messageDataProvider.isDeletedMessage(it)){ + return@execute + } + } + val sentTimestamp = this.message.sentTimestamp val sender = storage.getUserPublicKey() if (sentTimestamp != null && sender != null) { @@ -107,7 +114,10 @@ class MessageSendJob(val message: Message, val destination: Destination) : Job { Log.w(TAG, "Failed to send $message::class.simpleName.") val message = message as? VisibleMessage if (message != null) { - if (!MessagingModuleConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!)) { + if ( + MessagingModuleConfiguration.shared.messageDataProvider.isDeletedMessage(message.sentTimestamp!!) || + !MessagingModuleConfiguration.shared.messageDataProvider.isOutgoingMessage(message.sentTimestamp!!) + ) { return // The message has been deleted } } diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt index 301648f97b..73073532b0 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageSender.kt @@ -469,9 +469,15 @@ object MessageSender { fun handleFailedMessageSend(message: Message, error: Exception, isSyncMessage: Boolean = false) { val storage = MessagingModuleConfiguration.shared.storage + val timestamp = message.sentTimestamp!! + + // no need to handle if message is marked as deleted + if(MessagingModuleConfiguration.shared.messageDataProvider.isDeletedMessage(message.sentTimestamp!!)){ + return + } + val userPublicKey = storage.getUserPublicKey()!! - val timestamp = message.sentTimestamp!! val author = message.sender ?: userPublicKey if (isSyncMessage) storage.markAsSyncFailed(timestamp, author, error)