diff --git a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt index 44c30741ef..a99b40f36f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/SessionDialogBuilder.kt @@ -105,7 +105,7 @@ class SessionDialogBuilder(val context: Context) { fun destructiveButton( @StringRes text: Int, - @StringRes contentDescription: Int, + @StringRes contentDescription: Int = text, listener: () -> Unit = {} ) = button( text, diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt index a9f2263a46..5d49f1a78d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/v2/messages/ControlMessageView.kt @@ -10,7 +10,13 @@ import androidx.core.view.isVisible import androidx.recyclerview.widget.RecyclerView import network.loki.messenger.R import network.loki.messenger.databinding.ViewControlMessageBinding +import org.session.libsession.messaging.messages.ExpirationConfiguration +import org.session.libsession.utilities.ExpirationUtil.getExpirationDisplayValue +import org.session.libsession.utilities.getExpirationTypeDisplayValue import org.thoughtcrime.securesms.database.model.MessageRecord +import org.thoughtcrime.securesms.showSessionDialog +import kotlin.time.Duration.Companion.milliseconds +import kotlin.time.Duration.Companion.seconds class ControlMessageView : LinearLayout { @@ -31,15 +37,33 @@ class ControlMessageView : LinearLayout { binding.expirationTimerView.isGone = true binding.followSetting.isGone = true var messageBody: CharSequence = message.getDisplayBody(context) - binding.root.contentDescription= null + binding.root.contentDescription = null when { message.isExpirationTimerUpdate -> { - binding.expirationTimerView.apply { - isVisible = true - setExpirationTime(message.expireStarted, message.expiresIn) + binding.apply { + expirationTimerView.isVisible = true + expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn) + followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled && !message.isOutgoing + followSetting.setOnClickListener { + context.showSessionDialog { + val isOff = message.expiresIn == 0L + title(R.string.dialog_disappearing_messages_follow_setting_title) + if (isOff) { + text(R.string.dialog_disappearing_messages_follow_setting_off_body) + } else { + text( + context.getString( + R.string.dialog_disappearing_messages_follow_setting_on_body, + getExpirationDisplayValue(context, message.expiresIn.milliseconds), + context.getExpirationTypeDisplayValue(message.expireStarted == message.timestamp) + ) + ) + } + destructiveButton(if (isOff) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set) { } + cancelButton() + } + } } - - binding.followSetting.isGone = message.isOutgoing } message.isMediaSavedNotification -> { binding.iconImageView.apply { 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 af682bd1ed..dac078bc9e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/database/MmsDatabase.kt @@ -25,6 +25,7 @@ import com.google.android.mms.pdu_alt.PduHeaders import org.json.JSONArray import org.json.JSONException import org.json.JSONObject +import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.signal.IncomingMediaMessage import org.session.libsession.messaging.messages.signal.OutgoingGroupMediaMessage import org.session.libsession.messaging.messages.signal.OutgoingMediaMessage @@ -1170,7 +1171,7 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa * @param outgoing if true only delete outgoing messages, if false only delete incoming messages, if null delete both. */ private fun deleteExpirationTimerMessages(threadId: Long, outgoing: Boolean? = null) { - val outgoingClause = outgoing?.let { + val outgoingClause = outgoing?.takeIf { ExpirationConfiguration.isNewConfigEnabled }?.let { val comparison = if (it) "IN" else "NOT IN" " AND $MESSAGE_BOX & ${MmsSmsColumns.Types.BASE_TYPE_MASK} $comparison (${MmsSmsColumns.Types.OUTGOING_MESSAGE_TYPES.joinToString()})" } ?: "" diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java index 80501c9a2c..fbc36337bb 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/MessageRecord.java @@ -116,7 +116,7 @@ public abstract class MessageRecord extends DisplayRecord { return new SpannableString(UpdateMessageBuilder.INSTANCE.buildGroupUpdateMessage(context, updateMessageData, getIndividualRecipient().getAddress().serialize(), isOutgoing())); } else if (isExpirationTimerUpdate()) { int seconds = (int) (getExpiresIn() / 1000); - return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), getThreadId(), isOutgoing())); + return new SpannableString(UpdateMessageBuilder.INSTANCE.buildExpirationTimerMessage(context, seconds, getIndividualRecipient().getAddress().serialize(), getThreadId(), isOutgoing(), getTimestamp(), expireStarted)); } else if (isDataExtractionNotification()) { if (isScreenshotNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.SCREENSHOT, getIndividualRecipient().getAddress().serialize()))); else if (isMediaSavedNotification()) return new SpannableString((UpdateMessageBuilder.INSTANCE.buildDataExtractionMessage(context, DataExtractionNotificationInfoMessage.Kind.MEDIA_SAVED, getIndividualRecipient().getAddress().serialize()))); diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 0395f76d3a..ab8d322b8f 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -906,6 +906,11 @@ Are you sure you want to join the %s open group? Open URL? Are you sure you want to open %s? + Follow Setting + Messages you send will no longer disappear. Are you sure you want to turn off disappearing messages? + Set your messages to disappear %1$s after they have been %2$s? + Set + Confirm Open Copy URL Enable Link Previews? diff --git a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt index 159283238b..2584583734 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/messages/control/ExpirationTimerUpdate.kt @@ -56,8 +56,6 @@ data class ExpirationTimerUpdate(var expiryMode: ExpiryMode, var syncTarget: Str } return try { SignalServiceProtos.Content.newBuilder().apply { - expirationType - expirationTimer dataMessage = dataMessageProto.build() setExpirationConfigurationIfNeeded(threadID) }.build() diff --git a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt index 9fb55b2494..1571bb22c9 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/sending_receiving/MessageReceiver.kt @@ -151,11 +151,10 @@ object MessageReceiver { val isUserBlindedSender = sender == openGroupPublicKey?.let { SodiumUtilities.blindedKeyPair(it, MessagingModuleConfiguration.shared.getUserED25519KeyPair()!!) }?.let { SessionId(IdPrefix.BLINDED, it.publicKey.asBytes).hexString } val isUserSender = sender == userPublicKey - // Ignore self send if needed - if (!message.isSelfSendValid && (isUserSender || isUserBlindedSender)) { - throw Error.SelfSend - } + if (isUserSender || isUserBlindedSender) { + // Ignore self send if needed + if (!message.isSelfSendValid) throw Error.SelfSend message.isSenderSelf = true } // Guard against control messages in open groups diff --git a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt index 503f1271f9..bc76fff841 100644 --- a/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt +++ b/libsession/src/main/java/org/session/libsession/messaging/utilities/UpdateMessageBuilder.kt @@ -12,14 +12,14 @@ import org.session.libsession.messaging.calls.CallMessageType.CALL_OUTGOING import org.session.libsession.messaging.contacts.Contact import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.sending_receiving.data_extraction.DataExtractionNotificationInfoMessage -import org.session.libsession.utilities.Address import org.session.libsession.utilities.ExpirationUtil +import org.session.libsession.utilities.getExpirationTypeDisplayValue import org.session.libsession.utilities.truncateIdForDisplay object UpdateMessageBuilder { val storage = MessagingModuleConfiguration.shared.storage - fun getSenderName(senderId: String) = storage.getContactWithSessionID(senderId) + private fun getSenderName(senderId: String) = storage.getContactWithSessionID(senderId) ?.displayName(Contact.ContactContext.REGULAR) ?: truncateIdForDisplay(senderId) @@ -76,11 +76,17 @@ object UpdateMessageBuilder { } } - fun buildExpirationTimerMessage(context: Context, duration: Long, senderId: String? = null, threadId: Long, isOutgoing: Boolean = false): String { + fun buildExpirationTimerMessage( + context: Context, + duration: Long, + senderId: String? = null, + threadId: Long, + isOutgoing: Boolean = false, + timestamp: Long, + expireStarted: Long + ): String { if (!isOutgoing && senderId == null) return "" - val senderName: String = if (!isOutgoing) { - getSenderName(senderId!!) - } else { context.getString(R.string.MessageRecord_you) } + val senderName = if (isOutgoing) context.getString(R.string.MessageRecord_you) else getSenderName(senderId!!) return if (duration <= 0) { if (isOutgoing) { if (ExpirationConfiguration.isNewConfigEnabled) { @@ -98,11 +104,7 @@ object UpdateMessageBuilder { } else { val time = ExpirationUtil.getExpirationDisplayValue(context, duration.toInt()) val config = threadId.let { storage.getExpirationConfiguration(it) } - val state = when (config?.expiryMode) { - is ExpiryMode.AfterSend -> context.getString(R.string.MessageRecord_state_sent) - is ExpiryMode.AfterRead -> context.getString(R.string.MessageRecord_state_read) - else -> "" - } + val state = context.getExpirationTypeDisplayValue(timestamp == expireStarted) if (isOutgoing) { if (ExpirationConfiguration.isNewConfigEnabled) { context.getString(R.string.MessageRecord_you_set_messages_to_disappear_s_after_s, time, state) diff --git a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.kt b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.kt index 9834ee36fb..f3647fa4f8 100644 --- a/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.kt +++ b/libsession/src/main/java/org/session/libsession/utilities/ExpirationUtil.kt @@ -1,10 +1,13 @@ package org.session.libsession.utilities import android.content.Context +import network.loki.messenger.libsession_util.util.ExpiryMode import org.session.libsession.R import java.util.concurrent.TimeUnit import kotlin.time.Duration +fun Context.getExpirationTypeDisplayValue(sent: Boolean) = if (sent) getString(R.string.MessageRecord_state_sent) else getString(R.string.MessageRecord_state_read) + object ExpirationUtil { @JvmStatic fun getExpirationDisplayValue(context: Context, duration: Duration): String = getExpirationDisplayValue(context, duration.inWholeSeconds.toInt())