feat: add clear messages and media dialogs and change db to not use message count == 0 in conversations

This commit is contained in:
0x330a 2022-11-14 17:22:00 +11:00
parent e70b0ee606
commit ca200c4f2e
14 changed files with 516 additions and 10 deletions

View file

@ -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);

View file

@ -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()
}
}
}
}

View file

@ -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()
}
}
}
}

View file

@ -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")
}
}
}
}

View file

@ -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 {

View file

@ -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)
}

View file

@ -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) {

View file

@ -0,0 +1,65 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<TextView
android:layout_gravity="center"
android:layout_margin="@dimen/small_spacing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_clear_all_media_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/medium_font_size" />
<TextView
android:id="@+id/dialogDescriptionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_clear_all_media_message"
android:textAlignment="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.DestructiveText"
android:id="@+id/clear"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginEnd="@dimen/medium_spacing"
android:text="@string/dialog_clear_all_data_clear" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/dialog_clear_all_messages_cancel" />
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Small.ThreeBounce"
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
app:SpinKit_Color="?colorAccent"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View file

@ -0,0 +1,97 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center_horizontal"
android:orientation="vertical"
android:elevation="4dp"
android:padding="@dimen/medium_spacing">
<FrameLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">
<TextView
android:layout_gravity="center"
android:layout_margin="@dimen/small_spacing"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/dialog_clear_all_messages_title"
android:textColor="?android:textColorPrimary"
android:textStyle="bold"
android:textSize="@dimen/medium_font_size" />
<ImageView
android:id="@+id/close"
android:src="@drawable/ic_baseline_close_24"
android:layout_gravity="end|top"
android:layout_marginHorizontal="@dimen/small_spacing"
android:layout_width="24dp"
android:layout_height="24dp"
app:tint="?android:textColorPrimary" />
</FrameLayout>
<TextView
android:id="@+id/dialogDescriptionText"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:text="@string/dialog_clear_all_messages_message"
android:textAlignment="center"
android:textColor="?android:textColorPrimary"
android:textSize="@dimen/small_font_size" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="@dimen/large_spacing"
android:orientation="horizontal">
<Button
style="@style/Widget.Session.Button.Dialog.DestructiveText"
android:id="@+id/clear"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginEnd="@dimen/medium_spacing"
android:text="@string/dialog_clear_all_data_clear" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/cancel"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/dialog_clear_all_messages_cancel" />
<Button
style="@style/Widget.Session.Button.Dialog.DestructiveText"
android:id="@+id/forEveryone"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:layout_marginEnd="@dimen/medium_spacing"
android:text="@string/dialog_clear_all_messages_for_everyone" />
<Button
style="@style/Widget.Session.Button.Dialog.UnimportantText"
android:id="@+id/forMe"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
android:text="@string/dialog_clear_all_messages_for_me" />
<com.github.ybq.android.spinkit.SpinKitView
style="@style/SpinKitView.Small.ThreeBounce"
android:id="@+id/progressBar"
android:layout_width="0dp"
android:layout_height="@dimen/small_button_height"
android:layout_weight="1"
app:SpinKit_Color="?colorAccent"
android:visibility="gone" />
</LinearLayout>
</LinearLayout>

View file

@ -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">
<TextView
android:visibility="gone"
android:layout_marginHorizontal="@dimen/medium_spacing"
android:textColor="@color/destructive"
android:layout_gravity="center_vertical|end"
android:id="@+id/clearMedia"
android:text="@string/media_overview_activity__clear_media"
android:layout_width="wrap_content"
android:layout_height="wrap_content"/>
</androidx.appcompat.widget.Toolbar>
<org.thoughtcrime.securesms.components.ControllableTabLayout
android:id="@+id/tab_layout"

View file

@ -882,4 +882,14 @@
<string name="conversation_settings_delete_group">Delete Group</string>
<string name="image">image</string>
<string name="video">video</string>
<string name="dialog_clear_all_messages_title">Clear All Messages</string>
<string name="dialog_clear_all_media_title">Clear All Media</string>
<string name="dialog_clear_all_messages_message">Are you sure you want to clear all group messages?</string>
<string name="dialog_clear_all_media_message">Are you sure you want to clear all media and files? This will also delete all messages with attachments.</string>
<string name="dialog_clear_all_messages_local_message">Are you sure you want to clear %s messages from your device?</string>
<string name="dialog_clear_all_messages_for_me">For Me</string>
<string name="dialog_clear_all_messages_for_everyone">For Everyone</string>
<string name="dialog_clear_all_messages_clear">Clear</string>
<string name="dialog_clear_all_messages_cancel">Cancel</string>
<string name="media_overview_activity__clear_media">Clear All</string>
</resources>

View file

@ -1,6 +1,81 @@
<?xml version="1.0" encoding="utf-8"?>
<resources xmlns:tools="http://schemas.android.com/tools">
<style name="Base.Theme.Session" parent="@style/Theme.AppCompat.DayNight">
<!-- Main styles -->
<item name="sessionLogoTint">@color/classic_dark_6</item>
<item name="colorPrimary">@color/classic_dark_0</item>
<item name="colorPrimaryDark">@color/classic_dark_0</item>
<item name="colorControlNormal">?android:textColorPrimary</item>
<item name="colorControlActivated">?colorAccent</item>
<item name="android:colorControlHighlight">?colorAccent</item>
<item name="android:textColorPrimary">@color/classic_dark_6</item>
<item name="android:textColorSecondary">?android:textColorPrimary</item>
<item name="android:textColorTertiary">@color/classic_dark_5</item>
<item name="android:textColor">?android:textColorPrimary</item>
<item name="android:textColorHint">@color/gray27</item>
<item name="android:windowBackground">?colorPrimary</item>
<item name="android:navigationBarColor">@color/compose_view_background</item>
<item name="dialog_background_color">@color/classic_dark_1</item>
<item name="bottomSheetDialogTheme">@style/Classic.Dark.BottomSheet</item>
<item name="actionMenuTextColor">?android:textColorPrimary</item>
<item name="popupTheme">?actionBarPopupTheme</item>
<item name="colorCellBackground">@color/classic_dark_1</item>
<item name="colorSettingsBackground">@color/classic_dark_1</item>
<item name="colorDividerBackground">@color/classic_dark_3</item>
<item name="colorCellRipple">@color/classic_dark_3</item>
<item name="actionBarPopupTheme">@style/Dark.Popup</item>
<item name="actionBarWidgetTheme">@null</item>
<item name="actionBarTheme">@style/ThemeOverlay.AppCompat.Dark.ActionBar</item>
<item name="actionBarStyle">@style/Widget.Session.ActionBar</item>
<item name="prominentButtonColor">?colorAccent</item>
<item name="elementBorderColor">@color/classic_dark_3</item>
<!-- Home screen -->
<item name="searchBackgroundColor">#1B1B1B</item>
<item name="searchIconColor">#E5E5E8</item>
<item name="searchHintColor">@color/classic_dark_5</item>
<item name="searchTextColor">?android:textColorPrimary</item>
<item name="searchHighlightTint">?colorAccent</item>
<item name="home_gradient_start">#00000000</item>
<item name="home_gradient_end">@color/classic_dark_1</item>
<item name="conversation_pinned_background_color">?colorCellBackground</item>
<item name="conversation_unread_background_color">@color/classic_dark_2</item>
<item name="conversation_pinned_icon_color">@color/classic_dark_4</item>
<item name="unreadIndicatorBackgroundColor">@color/classic_dark_3</item>
<item name="unreadIndicatorTextColor">@color/classic_dark_0</item>
<!-- New conversation button -->
<item name="conversation_color_non_main">@color/classic_dark_2</item>
<item name="conversation_shadow_non_main">@color/transparent_black_30</item>
<item name="conversation_shadow_main">?colorAccent</item>
<item name="conversation_menu_background_color">@color/classic_dark_1</item>
<item name="conversation_menu_cell_color">?conversation_menu_background_color</item>
<item name="conversation_menu_border_color">@color/classic_dark_3</item>
<item name="conversationMenuSearchBackgroundColor">@color/classic_dark_0</item>
<!-- Conversation -->
<item name="message_received_background_color">@color/classic_dark_2</item>
<item name="message_received_text_color">@color/classic_dark_6</item>
<item name="message_sent_background_color">?colorAccent</item>
<item name="message_sent_text_color">@color/classic_dark_0</item>
<item name="input_bar_background">@color/classic_dark_1</item>
<item name="input_bar_text_hint">@color/classic_dark_5</item>
<item name="input_bar_text_user">@color/classic_dark_6</item>
<item name="input_bar_border">@color/classic_dark_3</item>
<item name="input_bar_button_background">@color/classic_dark_2</item>
<item name="input_bar_button_text_color">@color/classic_dark_6</item>
<item name="input_bar_button_background_opaque">@color/classic_dark_2</item>
<item name="input_bar_button_background_opaque_border">@color/classic_dark_3</item>
<item name="input_bar_lock_view_background">@color/classic_dark_2</item>
<item name="input_bar_lock_view_border">@color/classic_dark_3</item>
<item name="mention_candidates_view_background">@color/classic_dark_2</item>
<item name="mention_candidates_view_background_ripple">@color/classic_dark_3</item>
<item name="scroll_to_bottom_button_background">@color/classic_dark_1</item>
<item name="scroll_to_bottom_button_border">@color/classic_dark_3</item>
<item name="conversation_unread_count_indicator_background">@color/classic_dark_4</item>
<item name="message_selected">@color/classic_dark_1</item>
<item name="actionModeBackground">?colorPrimary</item>
<item name="android:colorBackground">?colorPrimary</item>
<item name="dialog_background_color">?colorPrimary</item>

View file

@ -0,0 +1,95 @@
package org.thoughtcrime.securesms.conversation.v2
import org.junit.Assert.assertEquals
import org.junit.Assert.assertFalse
import org.junit.Assert.assertTrue
import org.junit.Before
import org.junit.Test
import org.mockito.Mockito.mock
import org.mockito.kotlin.anyOrNull
import org.mockito.kotlin.never
import org.mockito.kotlin.verify
import org.mockito.kotlin.whenever
import org.session.libsession.database.StorageProtocol
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupRecord
import org.session.libsession.utilities.TextSecurePreferences
import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.BaseViewModelTest
import org.thoughtcrime.securesms.conversation.settings.ConversationSettingsViewModel
class ConversationSettingsViewModelTest: BaseViewModelTest() {
companion object {
const val TEST_THREAD_ID = 1L
const val TEST_LOCAL_ID = "1234"
}
private val mockedStorage = mock(StorageProtocol::class.java)
private val mockedPrefs = mock(TextSecurePreferences::class.java)
private val mockedRecipient = mock(Recipient::class.java)
private val viewModel = ConversationSettingsViewModel(TEST_THREAD_ID, mockedStorage, mockedPrefs)
@Before
fun setup() {
whenever(mockedStorage.getRecipientForThread(TEST_THREAD_ID)).thenReturn(mockedRecipient)
}
@Test
fun `it should get a mocked recipient`() {
assertEquals(mockedRecipient, viewModel.recipient)
}
@Test
fun `it should report correct pin status`() {
whenever(mockedStorage.isThreadPinned(TEST_THREAD_ID)).thenReturn(true)
val pinStatus = viewModel.isPinned()
verify(mockedStorage).isThreadPinned(TEST_THREAD_ID)
assertTrue(pinStatus)
}
@Test
fun `it should auto download attachments`() {
whenever(mockedStorage.shouldAutoDownloadAttachments(mockedRecipient)).thenReturn(true)
val shouldDownload = viewModel.autoDownloadAttachments()
verify(mockedStorage).shouldAutoDownloadAttachments(mockedRecipient)
assertTrue(shouldDownload)
}
@Test
fun `it should not auto download if recipient null`() {
whenever(mockedStorage.getRecipientForThread(TEST_THREAD_ID)).thenReturn(null)
val shouldDownload = viewModel.autoDownloadAttachments()
verify(mockedStorage, never()).shouldAutoDownloadAttachments(anyOrNull())
assertFalse(shouldDownload)
}
@Test
fun `it should call storage for if user is an admin`() {
val groupAddress = Address.fromSerialized("__textsecure_group__!1234")
whenever(mockedRecipient.isClosedGroupRecipient).thenReturn(true)
whenever(mockedRecipient.address).thenReturn(groupAddress)
whenever(mockedPrefs.getLocalNumber()).thenReturn(TEST_LOCAL_ID)
val mockedGroup = mock(GroupRecord::class.java).apply {
whenever(this.admins).thenReturn(listOf(Address.fromSerialized(TEST_LOCAL_ID)))
}
whenever(mockedStorage.getGroup(groupAddress.serialize())).thenReturn(mockedGroup)
val isUserAdmin = viewModel.isUserGroupAdmin()
assertTrue(isUserAdmin)
}
@Test
fun `it should not call storage for group admin when we aren't in a group`() {
whenever(mockedRecipient.isClosedGroupRecipient).thenReturn(false)
val isUserAdmin = viewModel.isUserGroupAdmin()
assertFalse(isUserAdmin)
}
@Test
fun `it should `() {
}
}

View file

@ -155,6 +155,7 @@ interface StorageProtocol {
fun getMessageCount(threadID: Long): Long
fun setThreadPinned(threadID: Long, isPinned: Boolean)
fun isThreadPinned(threadID: Long): Boolean
fun clearMessages(threadID: Long): Boolean
// Contacts
fun getContactWithSessionID(sessionID: String): Contact?