Group settings refactor

pull/1014/head
charles 2 years ago
parent 2494483afb
commit d23b849c91

@ -16,6 +16,7 @@ import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R
import network.loki.messenger.databinding.ViewConversationActionBarBinding
import network.loki.messenger.databinding.ViewConversationSettingBinding
import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.recipients.Recipient
@ -74,6 +75,7 @@ class ConversationActionBarView : LinearLayout {
delegate: ConversationActionBarDelegate,
threadId: Long,
recipient: Recipient,
config: ExpirationConfiguration? = null,
openGroup: OpenGroup? = null,
glide: GlideRequests
) {
@ -87,24 +89,24 @@ class ConversationActionBarView : LinearLayout {
binding.profilePictureView.root.layoutParams = LayoutParams(size, size)
binding.profilePictureView.root.glide = glide
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadId, context)
update(recipient, openGroup)
update(recipient, openGroup, config)
}
fun update(recipient: Recipient, openGroup: OpenGroup? = null) {
fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
binding.profilePictureView.root.update(recipient)
binding.conversationTitleView.text = when {
recipient.isLocalNumber -> context.getString(R.string.note_to_self)
else -> recipient.toShortString()
}
updateSubtitle(recipient, openGroup)
updateSubtitle(recipient, openGroup, config)
}
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null) {
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
val settings = mutableListOf<ConversationSetting>()
if (recipient.expireMessages > 0) {
if (config?.isEnabled == true) {
settings.add(
ConversationSetting(
"${context.getString(R.string.expiration_type_disappear_after_read)} - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, recipient.expireMessages)}" ,
"${context.getString(R.string.expiration_type_disappear_after_read)} - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config?.durationSeconds ?: 0)}" ,
ConversationSettingType.EXPIRATION,
R.drawable.ic_timer
)

@ -455,7 +455,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
actionBar.title = ""
actionBar.setDisplayHomeAsUpEnabled(true)
actionBar.setHomeButtonEnabled(true)
binding!!.toolbarContent.bind(this, viewModel.threadId, recipient, viewModel.openGroup, glide)
binding!!.toolbarContent.bind(this, viewModel.threadId, recipient, viewModel.expirationConfiguration, viewModel.openGroup, glide)
maybeUpdateToolbar(recipient)
}
@ -548,7 +548,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun getLatestOpenGroupInfoIfNeeded() {
viewModel.openGroup?.let { openGroup ->
OpenGroupApi.getMemberCount(openGroup.room, openGroup.server).successUi {
binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!, openGroup)
binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!, openGroup, viewModel.expirationConfiguration)
maybeUpdateToolbar(viewModel.recipient!!)
}
}
@ -568,7 +568,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
private fun setUpOutdatedClientBanner() {
val recipient = viewModel.recipient ?: return
if (recipient.expireMessages == 0 || !ExpirationConfiguration.isNewConfigEnabled) { return }
if (viewModel.expirationConfiguration?.isEnabled != true || !ExpirationConfiguration.isNewConfigEnabled) { return }
binding?.outdatedBannerTextView?.text =
resources.getString(R.string.activity_conversation_outdated_client_banner_text, recipient.name)
binding?.outdatedBanner?.isVisible = true
@ -622,7 +622,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
menu,
menuInflater,
recipient,
this
this,
viewModel.expirationConfiguration
)
}
viewModel.recipient?.let { maybeUpdateToolbar(it) }
@ -653,7 +654,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
}
private fun maybeUpdateToolbar(recipient: Recipient) {
binding?.toolbarContent?.update(recipient, viewModel.openGroup)
binding?.toolbarContent?.update(recipient, viewModel.openGroup, viewModel.expirationConfiguration)
}
private fun showOrHideInputIfNeeded() {
@ -1347,7 +1348,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
val message = VisibleMessage()
message.sentTimestamp = System.currentTimeMillis()
message.text = text
val outgoingTextMessage = OutgoingTextMessage.from(message, recipient)
val expiresInMillis = (viewModel.expirationConfiguration?.durationSeconds ?: 0) * 1000L
val outgoingTextMessage = OutgoingTextMessage.from(message, recipient, expiresInMillis)
// Clear the input bar
binding?.inputBar?.text = ""
binding?.inputBar?.cancelQuoteDraft()
@ -1384,7 +1386,8 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
else it.individualRecipient.address
quote?.copy(author = sender)
}
val outgoingTextMessage = OutgoingMediaMessage.from(message, recipient, attachments, localQuote, linkPreview)
val expiresInMillis = (viewModel.expirationConfiguration?.durationSeconds ?: 0) * 1000L
val outgoingTextMessage = OutgoingMediaMessage.from(message, recipient, attachments, localQuote, linkPreview, expiresInMillis)
// Clear the input bar
binding?.inputBar?.text = ""
binding?.inputBar?.cancelQuoteDraft()

@ -10,6 +10,7 @@ import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch
import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.messaging.open_groups.OpenGroupApi
import org.session.libsession.messaging.utilities.SessionId
@ -32,6 +33,9 @@ class ConversationViewModel(
private val _uiState = MutableStateFlow(ConversationUiState())
val uiState: StateFlow<ConversationUiState> = _uiState
val expirationConfiguration: ExpirationConfiguration?
get() = storage.getExpirationConfiguration(threadId)
val recipient: Recipient?
get() = repository.maybeGetRecipientForThreadId(threadId)

@ -18,6 +18,7 @@ import androidx.core.content.pm.ShortcutInfoCompat
import androidx.core.content.pm.ShortcutManagerCompat
import androidx.core.graphics.drawable.IconCompat
import network.loki.messenger.R
import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.messaging.sending_receiving.leave
import org.session.libsession.utilities.GroupUtil.doubleDecodeGroupID
@ -46,7 +47,8 @@ object ConversationMenuHelper {
menu: Menu,
inflater: MenuInflater,
thread: Recipient,
context: Context
context: Context,
config: ExpirationConfiguration?
) {
// Prepare
menu.clear()
@ -54,7 +56,7 @@ object ConversationMenuHelper {
// Base menu (options that should always be present)
inflater.inflate(R.menu.menu_conversation, menu)
// Expiring messages
if (thread.expireMessages == 0 && !isOpenGroup &&
if (config?.isEnabled != true && !isOpenGroup &&
(thread.hasApprovedMe() || thread.isClosedGroupRecipient || thread.isLocalNumber)
) {
inflater.inflate(R.menu.menu_conversation_expiration_off, menu)

@ -60,15 +60,15 @@ class ExpirationConfigurationDatabase(context: Context, helper: SQLCipherOpenHel
val query = "$THREAD_ID = ?"
val args = arrayOf("$threadId")
val mappings: MutableList<ExpirationConfiguration> = mutableListOf()
val configurations: MutableList<ExpirationConfiguration> = mutableListOf()
readableDatabase.query(TABLE_NAME, null, query, args, null, null, null).use { cursor ->
while (cursor.moveToNext()) {
mappings += readExpirationConfiguration(cursor)
configurations += readExpirationConfiguration(cursor)
}
}
return mappings.firstOrNull()
return configurations.firstOrNull()
}
fun setExpirationConfiguration(configuration: ExpirationConfiguration) {

@ -168,19 +168,27 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
recipientDb.setApprovedMe(targetRecipient, true)
}
}
val expiresIn = getExpirationConfiguration(message.threadID ?: -1)?.durationSeconds ?: 0
if (message.isMediaMessage() || attachments.isNotEmpty()) {
val quote: Optional<QuoteModel> = if (quotes != null) Optional.of(quotes) else Optional.absent()
val linkPreviews: Optional<List<LinkPreview>> = if (linkPreview.isEmpty()) Optional.absent() else Optional.of(linkPreview.mapNotNull { it!! })
val mmsDatabase = DatabaseComponent.get(context).mmsDatabase()
val insertResult = if (isUserSender || isUserBlindedSender) {
val mediaMessage = OutgoingMediaMessage.from(message, targetRecipient, pointers, quote.orNull(), linkPreviews.orNull()?.firstOrNull())
val mediaMessage = OutgoingMediaMessage.from(
message,
targetRecipient,
pointers,
quote.orNull(),
linkPreviews.orNull()?.firstOrNull(),
expiresIn * 1000L
)
mmsDatabase.insertSecureDecryptedMessageOutbox(mediaMessage, message.threadID ?: -1, message.sentTimestamp!!, runThreadUpdate)
} else {
// It seems like we have replaced SignalServiceAttachment with SessionServiceAttachment
val signalServiceAttachments = attachments.mapNotNull {
it.toSignalPointer()
}
val mediaMessage = IncomingMediaMessage.from(message, senderAddress, targetRecipient.expireMessages * 1000L, group, signalServiceAttachments, quote, linkPreviews)
val mediaMessage = IncomingMediaMessage.from(message, senderAddress, expiresIn * 1000L, group, signalServiceAttachments, quote, linkPreviews)
mmsDatabase.insertSecureDecryptedMessageInbox(mediaMessage, message.threadID ?: -1, message.receivedTimestamp ?: 0, runIncrement, runThreadUpdate)
}
if (insertResult.isPresent) {
@ -192,11 +200,11 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
val insertResult = if (isUserSender || isUserBlindedSender) {
val textMessage = if (isOpenGroupInvitation) OutgoingTextMessage.fromOpenGroupInvitation(message.openGroupInvitation, targetRecipient, message.sentTimestamp)
else OutgoingTextMessage.from(message, targetRecipient)
else OutgoingTextMessage.from(message, targetRecipient, expiresIn * 1000L)
smsDatabase.insertMessageOutbox(message.threadID ?: -1, textMessage, message.sentTimestamp!!, runThreadUpdate)
} else {
val textMessage = if (isOpenGroupInvitation) IncomingTextMessage.fromOpenGroupInvitation(message.openGroupInvitation, senderAddress, message.sentTimestamp)
else IncomingTextMessage.from(message, senderAddress, group, targetRecipient.expireMessages * 1000L)
else IncomingTextMessage.from(message, senderAddress, group, expiresIn * 1000L)
val encrypted = IncomingEncryptedMessage(textMessage, textMessage.messageBody)
smsDatabase.insertMessageInbox(encrypted, message.receivedTimestamp ?: 0, runIncrement, runThreadUpdate)
}
@ -542,7 +550,7 @@ class Storage(context: Context, helper: SQLCipherOpenHelper) : Database(context,
override fun setExpirationTimer(groupID: String, duration: Int) {
val recipient = Recipient.from(context, fromSerialized(groupID), false)
val threadId = DatabaseComponent.get(context).threadDatabase().getThreadIdIfExistsFor(recipient)
val threadId = DatabaseComponent.get(context).threadDatabase().getOrCreateThreadIdFor(recipient)
DatabaseComponent.get(context).expirationConfigurationDatabase().setExpirationConfiguration(
ExpirationConfiguration(threadId, duration, ExpirationType.DELETE_AFTER_SEND.number, System.currentTimeMillis())
)

@ -26,6 +26,7 @@ import android.os.Bundle;
import androidx.core.app.RemoteInput;
import org.session.libsession.messaging.messages.ExpirationConfiguration;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
@ -84,10 +85,12 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
message.setText(responseText.toString());
message.setSentTimestamp(System.currentTimeMillis());
MessageSender.send(message, recipient.getAddress());
ExpirationConfiguration config = DatabaseComponent.get(context).expirationConfigurationDatabase().getExpirationConfiguration(threadId);
long expiresInMillis = config == null ? 0 : config.getDurationSeconds() * 1000L;
if (recipient.isGroupRecipient()) {
Log.w("AndroidAutoReplyReceiver", "GroupRecipient, Sending media message");
OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null);
OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null, expiresInMillis);
try {
DatabaseComponent.get(context).mmsDatabase().insertMessageOutbox(reply, replyThreadId, false, null, true);
} catch (MmsException e) {
@ -95,7 +98,7 @@ public class AndroidAutoReplyReceiver extends BroadcastReceiver {
}
} else {
Log.w("AndroidAutoReplyReceiver", "Sending regular message ");
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient);
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient, expiresInMillis);
DatabaseComponent.get(context).smsDatabase().insertMessageOutbox(replyThreadId, reply, false, System.currentTimeMillis(), null, true);
}

@ -26,6 +26,7 @@ import android.os.Bundle;
import androidx.core.app.RemoteInput;
import org.session.libsession.messaging.messages.ExpirationConfiguration;
import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage;
import org.session.libsession.messaging.messages.signal.OutgoingTextMessage;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
@ -78,10 +79,11 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
VisibleMessage message = new VisibleMessage();
message.setSentTimestamp(System.currentTimeMillis());
message.setText(responseText.toString());
ExpirationConfiguration config = DatabaseComponent.get(context).expirationConfigurationDatabase().getExpirationConfiguration(threadId);
long expiresInMillis = config == null ? 0 : config.getDurationSeconds() * 1000L;
switch (replyMethod) {
case GroupMessage: {
OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null);
OutgoingMediaMessage reply = OutgoingMediaMessage.from(message, recipient, Collections.emptyList(), null, null, expiresInMillis);
try {
DatabaseComponent.get(context).mmsDatabase().insertMessageOutbox(reply, threadId, false, null, true);
MessageSender.send(message, address);
@ -91,7 +93,7 @@ public class RemoteReplyReceiver extends BroadcastReceiver {
break;
}
case SecureMessage: {
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient);
OutgoingTextMessage reply = OutgoingTextMessage.from(message, recipient, expiresInMillis);
DatabaseComponent.get(context).smsDatabase().insertMessageOutbox(threadId, reply, false, System.currentTimeMillis(), null, true);
MessageSender.send(message, address);
break;

@ -162,14 +162,14 @@ public class ExpiringMessageManager implements SSKEnvironment.MessageExpirationM
MessageRecord messageRecord = DatabaseComponent.get(context).mmsSmsDatabase().getMessageFor(timestamp, author);
if (messageRecord != null) {
boolean mms = messageRecord.isMms();
Recipient recipient = messageRecord.getRecipient();
if (recipient.getExpireMessages() <= 0) return;
ExpirationConfiguration config = DatabaseComponent.get(context).expirationConfigurationDatabase().getExpirationConfiguration(messageRecord.getThreadId());
if (config == null || !config.isEnabled()) return;
if (mms) {
mmsDatabase.markExpireStarted(messageRecord.getId());
} else {
smsDatabase.markExpireStarted(messageRecord.getId());
}
scheduleDeletion(messageRecord.getId(), mms, recipient.getExpireMessages() * 1000);
scheduleDeletion(messageRecord.getId(), mms, config.getDurationSeconds() * 1000L);
}
}

@ -12,6 +12,6 @@ class ExpirationConfiguration(
val expirationType: ExpirationType? = ExpirationType.valueOf(expirationTypeValue)
companion object {
val isNewConfigEnabled = System.currentTimeMillis() > 1_674_000_000_000 // 18/01/2023
val isNewConfigEnabled = true//System.currentTimeMillis() > 1_674_000_000_000 // 18/01/2023
}
}

@ -128,8 +128,9 @@ class ConfigurationMessage(var closedGroups: List<ClosedGroup>, var openGroups:
if (!group.members.contains(Address.fromSerialized(storage.getUserPublicKey()!!))) continue
val groupPublicKey = GroupUtil.doubleDecodeGroupID(group.encodedId).toHexString()
val encryptionKeyPair = storage.getLatestClosedGroupEncryptionKeyPair(groupPublicKey) ?: continue
val recipient = Recipient.from(context, Address.fromSerialized(group.encodedId), false)
val closedGroup = ClosedGroup(groupPublicKey, group.title, encryptionKeyPair, group.members.map { it.serialize() }, group.admins.map { it.serialize() }, recipient.expireMessages)
val threadID = storage.getOrCreateThreadIdFor(Address.fromSerialized(group.encodedId))
val expiryConfig = storage.getExpirationConfiguration(threadID)
val closedGroup = ClosedGroup(groupPublicKey, group.title, encryptionKeyPair, group.members.map { it.serialize() }, group.admins.map { it.serialize() }, expiryConfig?.durationSeconds ?: 0)
closedGroups.add(closedGroup)
}
if (group.isOpenGroup) {

@ -4,13 +4,13 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.utilities.DistributionTypes;
import org.session.libsession.utilities.IdentityKeyMismatch;
import org.session.libsession.utilities.NetworkFailure;
import org.session.libsession.messaging.sending_receiving.attachments.Attachment;
import org.session.libsession.utilities.Contact;
import org.session.libsession.messaging.sending_receiving.link_preview.LinkPreview;
import org.session.libsession.messaging.sending_receiving.quotes.QuoteModel;
import org.session.libsession.utilities.Contact;
import org.session.libsession.utilities.DistributionTypes;
import org.session.libsession.utilities.IdentityKeyMismatch;
import org.session.libsession.utilities.NetworkFailure;
import org.session.libsession.utilities.recipients.Recipient;
import java.util.Collections;
@ -78,14 +78,15 @@ public class OutgoingMediaMessage {
Recipient recipient,
List<Attachment> attachments,
@Nullable QuoteModel outgoingQuote,
@Nullable LinkPreview linkPreview)
@Nullable LinkPreview linkPreview,
long expiresInMillis)
{
List<LinkPreview> previews = Collections.emptyList();
if (linkPreview != null) {
previews = Collections.singletonList(linkPreview);
}
return new OutgoingMediaMessage(recipient, message.getText(), attachments, message.getSentTimestamp(), -1,
recipient.getExpireMessages() * 1000, DistributionTypes.DEFAULT, outgoingQuote, Collections.emptyList(),
expiresInMillis, DistributionTypes.DEFAULT, outgoingQuote, Collections.emptyList(),
previews, Collections.emptyList(), Collections.emptyList());
}

@ -1,5 +1,6 @@
package org.session.libsession.messaging.messages.signal;
import org.session.libsession.messaging.messages.ExpirationConfiguration;
import org.session.libsession.messaging.messages.visible.OpenGroupInvitation;
import org.session.libsession.messaging.messages.visible.VisibleMessage;
import org.session.libsession.utilities.recipients.Recipient;
@ -21,8 +22,8 @@ public class OutgoingTextMessage {
this.sentTimestampMillis = sentTimestampMillis;
}
public static OutgoingTextMessage from(VisibleMessage message, Recipient recipient) {
return new OutgoingTextMessage(recipient, message.getText(), recipient.getExpireMessages() * 1000, -1, message.getSentTimestamp());
public static OutgoingTextMessage from(VisibleMessage message, Recipient recipient, long expiresInMillis) {
return new OutgoingTextMessage(recipient, message.getText(), expiresInMillis, -1, message.getSentTimestamp());
}
public static OutgoingTextMessage fromOpenGroupInvitation(OpenGroupInvitation openGroupInvitation, Recipient recipient, Long sentTimestamp) {

@ -133,8 +133,8 @@ fun MessageSender.addMembers(groupPublicKey: String, membersToAdd: List<String>)
Log.d("Loki", "Can't add members to nonexistent closed group.")
throw Error.NoThread
}
val recipient = Recipient.from(context, fromSerialized(groupID), false)
val expireTimer = recipient.expireMessages
val threadId = storage.getOrCreateThreadIdFor(fromSerialized(groupID))
val expireTimer = storage.getExpirationConfiguration(threadId)?.durationSeconds ?: 0
if (membersToAdd.isEmpty()) {
Log.d("Loki", "Invalid closed group update.")
throw Error.InvalidClosedGroupUpdate

Loading…
Cancel
Save