From ca200c4f2e56bb802ef1a82bf9adfeddf32b098a Mon Sep 17 00:00:00 2001
From: 0x330a <92654767+0x330a@users.noreply.github.com>
Date: Mon, 14 Nov 2022 17:22:00 +1100
Subject: [PATCH] feat: add clear messages and media dialogs and change db to
not use message count == 0 in conversations
---
.../securesms/MediaOverviewActivity.java | 42 +++++++-
.../settings/ClearAllMediaDialog.kt | 33 +++++++
.../settings/ClearAllMessagesDialog.kt | 53 ++++++++++
.../settings/ConversationSettingsActivity.kt | 7 ++
.../settings/ConversationSettingsViewModel.kt | 17 +++-
.../securesms/database/Storage.kt | 10 ++
.../securesms/database/ThreadDatabase.java | 9 +-
.../res/layout/dialog_clear_all_media.xml | 65 +++++++++++++
.../res/layout/dialog_clear_all_messages.xml | 97 +++++++++++++++++++
.../res/layout/media_overview_activity.xml | 12 ++-
app/src/main/res/values/strings.xml | 10 ++
app/src/main/res/values/themes.xml | 75 ++++++++++++++
.../v2/ConversationSettingsViewModelTest.kt | 95 ++++++++++++++++++
.../libsession/database/StorageProtocol.kt | 1 +
14 files changed, 516 insertions(+), 10 deletions(-)
create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMediaDialog.kt
create mode 100644 app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMessagesDialog.kt
create mode 100644 app/src/main/res/layout/dialog_clear_all_media.xml
create mode 100644 app/src/main/res/layout/dialog_clear_all_messages.xml
create mode 100644 app/src/test/java/org/thoughtcrime/securesms/conversation/v2/ConversationSettingsViewModelTest.kt
diff --git a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java
index 53a909c5a..561b03672 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/MediaOverviewActivity.java
@@ -52,9 +52,18 @@ import androidx.viewpager.widget.ViewPager;
import com.codewaves.stickyheadergrid.StickyHeaderGridLayoutManager;
import com.google.android.material.tabs.TabLayout;
+import org.session.libsession.messaging.MessagingModuleConfiguration;
import org.session.libsession.messaging.messages.control.DataExtractionNotification;
import org.session.libsession.messaging.sending_receiving.MessageSender;
import org.session.libsession.utilities.Address;
+import org.session.libsession.utilities.GroupRecord;
+import org.session.libsession.utilities.TextSecurePreferences;
+import org.session.libsession.utilities.Util;
+import org.session.libsession.utilities.ViewUtil;
+import org.session.libsession.utilities.recipients.Recipient;
+import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
+import org.session.libsignal.utilities.Log;
+import org.thoughtcrime.securesms.conversation.settings.ClearAllMediaDialog;
import org.thoughtcrime.securesms.database.CursorRecyclerViewAdapter;
import org.thoughtcrime.securesms.database.MediaDatabase;
import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader;
@@ -62,25 +71,22 @@ import org.thoughtcrime.securesms.database.loaders.BucketedThreadMediaLoader.Buc
import org.thoughtcrime.securesms.database.loaders.ThreadMediaLoader;
import org.thoughtcrime.securesms.mms.GlideApp;
import org.thoughtcrime.securesms.permissions.Permissions;
-import org.session.libsession.utilities.recipients.Recipient;
import org.thoughtcrime.securesms.util.AttachmentUtil;
import org.thoughtcrime.securesms.util.SaveAttachmentTask;
import org.thoughtcrime.securesms.util.StickyHeaderDecoration;
-import org.session.libsession.utilities.Util;
-import org.session.libsession.utilities.ViewUtil;
-import org.session.libsession.utilities.task.ProgressDialogAsyncTask;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import java.util.Locale;
+import kotlin.Unit;
import network.loki.messenger.R;
/**
* Activity for displaying media attachments in-app
*/
-public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
+public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity implements View.OnClickListener {
@SuppressWarnings("unused")
private final static String TAG = MediaOverviewActivity.class.getSimpleName();
@@ -132,6 +138,20 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
this.recipient.addListener(recipient -> {
Util.runOnMain(() -> actionBar.setTitle(recipient.toShortString()));
});
+ View clearButton = toolbar.findViewById(R.id.clearMedia);
+ if (!this.recipient.isClosedGroupRecipient()) {
+ clearButton.setVisibility(View.GONE);
+ } else {
+ String userPublicKey = TextSecurePreferences.getLocalNumber(this);
+ GroupRecord groupRecord = MessagingModuleConfiguration.getShared().getStorage().getGroup(this.recipient.getAddress().toGroupString());
+ if (userPublicKey == null || groupRecord == null) {
+ clearButton.setVisibility(View.GONE);
+ } else {
+ boolean isUserAdmin = groupRecord.getAdmins().contains(Address.fromSerialized(userPublicKey));
+ clearButton.setVisibility(isUserAdmin ? View.VISIBLE : View.GONE);
+ clearButton.setOnClickListener(this);
+ }
+ }
}
public void onEnterMultiSelect() {
@@ -139,6 +159,18 @@ public class MediaOverviewActivity extends PassphraseRequiredActionBarActivity {
viewPager.setEnabled(false);
}
+ @Override
+ public void onClick(View v) {
+ if (v.getId() == R.id.clearMedia) {
+ FragmentManager fm = getSupportFragmentManager();
+ ClearAllMediaDialog dialog = new ClearAllMediaDialog(() -> {
+ Log.d("Loki", "Clear all the media");
+ return Unit.INSTANCE;
+ });
+ dialog.show(fm, "ClearAllMedia");
+ }
+ }
+
public void onExitMultiSelect() {
tabLayout.setEnabled(true);
viewPager.setEnabled(true);
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMediaDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMediaDialog.kt
new file mode 100644
index 000000000..9c5048ce1
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMediaDialog.kt
@@ -0,0 +1,33 @@
+package org.thoughtcrime.securesms.conversation.settings
+
+import android.view.LayoutInflater
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import network.loki.messenger.databinding.DialogClearAllMediaBinding
+import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
+
+class ClearAllMediaDialog(private val callback: ()->Unit): BaseDialog(), View.OnClickListener {
+
+ private lateinit var binding: DialogClearAllMediaBinding
+
+ override fun setContentView(builder: AlertDialog.Builder) {
+ super.setContentView(builder)
+ binding = DialogClearAllMediaBinding.inflate(LayoutInflater.from(requireContext()))
+ with (binding) {
+ clear.setOnClickListener(this@ClearAllMediaDialog)
+ cancel.setOnClickListener(this@ClearAllMediaDialog)
+ }
+ builder.setView(binding.root)
+ }
+
+ override fun onClick(v: View) {
+ when {
+ v === binding.cancel -> dismiss()
+ v === binding.clear -> {
+ callback()
+ dismiss()
+ }
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMessagesDialog.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMessagesDialog.kt
new file mode 100644
index 000000000..1824001bc
--- /dev/null
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ClearAllMessagesDialog.kt
@@ -0,0 +1,53 @@
+package org.thoughtcrime.securesms.conversation.settings
+
+import android.view.LayoutInflater
+import android.view.View
+import androidx.appcompat.app.AlertDialog
+import androidx.core.view.isVisible
+import network.loki.messenger.databinding.DialogClearAllMessagesBinding
+import org.thoughtcrime.securesms.conversation.v2.utilities.BaseDialog
+
+class ClearAllMessagesDialog(private val isUserAdmin: Boolean, private val callback: (Option) -> Unit): BaseDialog(), View.OnClickListener {
+
+ enum class Option {
+ FOR_ME,
+ FOR_EVERYONE
+ }
+
+ private lateinit var binding: DialogClearAllMessagesBinding
+
+ override fun setContentView(builder: AlertDialog.Builder) {
+ super.setContentView(builder)
+ binding = DialogClearAllMessagesBinding.inflate(LayoutInflater.from(requireContext()))
+ with (binding) {
+ forEveryone.isVisible = isUserAdmin
+ forEveryone.setOnClickListener(this@ClearAllMessagesDialog)
+ forMe.isVisible = isUserAdmin
+ forMe.setOnClickListener(this@ClearAllMessagesDialog)
+ close.isVisible = isUserAdmin
+ close.setOnClickListener(this@ClearAllMessagesDialog)
+
+ cancel.isVisible = !isUserAdmin
+ cancel.setOnClickListener(this@ClearAllMessagesDialog)
+ clear.isVisible = !isUserAdmin
+ clear.setOnClickListener(this@ClearAllMessagesDialog)
+ }
+ builder.setView(binding.root)
+ }
+
+ override fun onClick(v: View) {
+ when {
+ v === binding.cancel ||
+ v === binding.close -> dismiss()
+ v === binding.forMe ||
+ v === binding.clear -> {
+ callback(Option.FOR_ME)
+ dismiss()
+ }
+ v === binding.forEveryone -> {
+ callback(Option.FOR_EVERYONE)
+ dismiss()
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt
index 0fe8b021f..a548a81ca 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsActivity.kt
@@ -11,6 +11,7 @@ import network.loki.messenger.databinding.ActivityConversationSettingsBinding
import org.session.libsignal.utilities.Log
import org.thoughtcrime.securesms.MediaOverviewActivity
import org.thoughtcrime.securesms.PassphraseRequiredActionBarActivity
+import org.thoughtcrime.securesms.conversation.settings.ClearAllMessagesDialog.Option
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.database.LokiThreadDatabase
import org.thoughtcrime.securesms.database.ThreadDatabase
@@ -61,6 +62,7 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
binding.profilePictureView.root.glide = GlideApp.with(this)
updateRecipientDisplay()
binding.searchConversation.setOnClickListener(this)
+ binding.clearMessages.setOnClickListener(this)
binding.allMedia.setOnClickListener(this)
binding.pinConversation.setOnClickListener(this)
binding.notificationSettings.setOnClickListener(this)
@@ -144,6 +146,11 @@ class ConversationSettingsActivity: PassphraseRequiredActionBarActivity(), View.
notificationActivityCallback.launch(viewModel.threadId)
}
v === binding.back -> onBackPressed()
+ v === binding.clearMessages -> {
+ ClearAllMessagesDialog(viewModel.isUserGroupAdmin()) { option ->
+ viewModel.clearMessages(option == Option.FOR_EVERYONE)
+ }.show(supportFragmentManager, "Clear messages dialog")
+ }
}
}
}
\ No newline at end of file
diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt
index 083161c5e..c1bc18174 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/settings/ConversationSettingsViewModel.kt
@@ -32,12 +32,27 @@ class ConversationSettingsViewModel(
}
fun isUserGroupAdmin(): Boolean = recipient?.let { recipient ->
- if (!recipient.isGroupRecipient) return@let false
+ if (!recipient.isClosedGroupRecipient) return@let false
val localUserAddress = prefs.getLocalNumber() ?: return@let false
val group = storage.getGroup(recipient.address.toGroupString())
group?.admins?.contains(Address.fromSerialized(localUserAddress)) ?: false // this will have to be replaced for new closed groups
} ?: false
+ fun clearMessages(forAll: Boolean) {
+ if (forAll && !isUserGroupAdmin()) return
+
+ if (!forAll) {
+ viewModelScope.launch {
+ storage.clearMessages(threadId)
+ }
+ } else {
+ // do a send message here and on success do a clear messages
+ viewModelScope.launch {
+ storage.clearMessages(threadId)
+ }
+ }
+ }
+
// DI-related
@dagger.assisted.AssistedFactory
interface AssistedFactory {
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 f2fb35aab..1bb866128 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/Storage.kt
@@ -731,6 +731,16 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
return threadDb.getPinned(threadID)
}
+ override fun clearMessages(threadID: Long): Boolean {
+ val smsDb = DatabaseComponent.get(context).smsDatabase()
+ val mmsDb = DatabaseComponent.get(context).mmsDatabase()
+ val threadDb = DatabaseComponent.get(context).threadDatabase()
+ smsDb.deleteThread(threadID)
+ mmsDb.deleteThread(threadID) // threadDB update called from within
+ threadDb.update(threadID, false)
+ return true
+ }
+
override fun getAttachmentDataUri(attachmentId: AttachmentId): Uri {
return PartAuthority.getAttachmentDataUri(attachmentId)
}
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 6b892bdef..58a92fdf0 100644
--- a/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java
+++ b/app/src/main/java/org/thoughtcrime/securesms/database/ThreadDatabase.java
@@ -466,7 +466,7 @@ public class ThreadDatabase extends Database {
}
public Cursor getApprovedConversationList() {
- String where = "((" + MESSAGE_COUNT + " != 0 AND (" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%')) OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
+ String where = "((" + HAS_SENT + " = 1 OR " + RecipientDatabase.APPROVED + " = 1 OR "+ GroupDatabase.TABLE_NAME +"."+GROUP_ID+" LIKE '"+CLOSED_GROUP_PREFIX+"%') OR " + GroupDatabase.TABLE_NAME + "." + GROUP_ID + " LIKE '" + OPEN_GROUP_PREFIX + "%') " +
"AND " + ARCHIVED + " = 0 ";
return getConversationList(where);
}
@@ -692,6 +692,8 @@ public class ThreadDatabase extends Database {
deleteThread(threadId);
notifyConversationListListeners();
return true;
+ } else {
+ updateThread(threadId, 0, "", null, System.currentTimeMillis(), 0, 0, 0, false, 0, 0);
}
return false;
}
@@ -731,8 +733,9 @@ public class ThreadDatabase extends Database {
}
private boolean deleteThreadOnEmpty(long threadId) {
- Recipient threadRecipient = getRecipientForThreadId(threadId);
- return threadRecipient != null && !threadRecipient.isOpenGroupRecipient();
+ return false; // TODO: test the deletion / clearing logic here to make sure this is the desired functionality
+// Recipient threadRecipient = getRecipientForThreadId(threadId);
+// return threadRecipient != null && !threadRecipient.isOpenGroupRecipient();
}
private @NonNull String getFormattedBodyFor(@NonNull MessageRecord messageRecord) {
diff --git a/app/src/main/res/layout/dialog_clear_all_media.xml b/app/src/main/res/layout/dialog_clear_all_media.xml
new file mode 100644
index 000000000..b81c8cadd
--- /dev/null
+++ b/app/src/main/res/layout/dialog_clear_all_media.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_clear_all_messages.xml b/app/src/main/res/layout/dialog_clear_all_messages.xml
new file mode 100644
index 000000000..b04995227
--- /dev/null
+++ b/app/src/main/res/layout/dialog_clear_all_messages.xml
@@ -0,0 +1,97 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/media_overview_activity.xml b/app/src/main/res/layout/media_overview_activity.xml
index eaa36d219..79170e665 100644
--- a/app/src/main/res/layout/media_overview_activity.xml
+++ b/app/src/main/res/layout/media_overview_activity.xml
@@ -17,7 +17,17 @@
android:id="@+id/toolbar"
android:layout_width="match_parent"
android:layout_height="?android:attr/actionBarSize"
- app:layout_scrollFlags="scroll|enterAlways"/>
+ app:layout_scrollFlags="scroll|enterAlways">
+
+
Delete Group
image
video
+ Clear All Messages
+ Clear All Media
+ Are you sure you want to clear all group messages?
+ Are you sure you want to clear all media and files? This will also delete all messages with attachments.
+ Are you sure you want to clear %s messages from your device?
+ For Me
+ For Everyone
+ Clear
+ Cancel
+ Clear All
diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml
index 4c7dfd1db..9dab12461 100644
--- a/app/src/main/res/values/themes.xml
+++ b/app/src/main/res/values/themes.xml
@@ -1,6 +1,81 @@