Hide follow settings button when not applicable

This commit is contained in:
Andrew 2023-12-15 11:48:54 +10:30
parent cf0dbffafd
commit 988040b577
9 changed files with 92 additions and 17 deletions

View File

@ -0,0 +1,38 @@
package org.thoughtcrime.securesms.conversation.disappearingmessages
import android.content.Context
import dagger.hilt.android.qualifiers.ApplicationContext
import network.loki.messenger.libsession_util.util.ExpiryMode
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
import org.session.libsession.messaging.sending_receiving.MessageSender
import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.SSKEnvironment
import org.session.libsession.utilities.SSKEnvironment.MessageExpirationManagerProtocol
import org.session.libsession.utilities.TextSecurePreferences
import org.thoughtcrime.securesms.util.ConfigurationMessageUtilities
import javax.inject.Inject
class DisappearingMessages @Inject constructor(
@ApplicationContext private val context: Context,
private val textSecurePreferences: TextSecurePreferences,
private val messageExpirationManager: MessageExpirationManagerProtocol,
) {
fun set(threadId: Long, address: Address, mode: ExpiryMode) {
val expiryChangeTimestampMs = SnodeAPI.nowWithOffset
MessagingModuleConfiguration.shared.storage.setExpirationConfiguration(ExpirationConfiguration(threadId, mode, expiryChangeTimestampMs))
val message = ExpirationTimerUpdate(mode).apply {
sender = textSecurePreferences.getLocalNumber()
isSenderSelf = true
recipient = address.serialize()
sentTimestamp = expiryChangeTimestampMs
}
messageExpirationManager.setExpirationTimer(message)
MessageSender.send(message, address)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
}
}

View File

@ -99,7 +99,7 @@ class DisappearingMessagesViewModel(
recipient = address.serialize() recipient = address.serialize()
sentTimestamp = expiryChangeTimestampMs sentTimestamp = expiryChangeTimestampMs
} }
messageExpirationManager.setExpirationTimer(message, mode) messageExpirationManager.setExpirationTimer(message)
MessageSender.send(message, address) MessageSender.send(message, address)
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(application) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(application)

View File

@ -8,16 +8,26 @@ import androidx.core.content.res.ResourcesCompat
import androidx.core.view.isGone import androidx.core.view.isGone
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import dagger.hilt.android.AndroidEntryPoint
import network.loki.messenger.R import network.loki.messenger.R
import network.loki.messenger.databinding.ViewControlMessageBinding import network.loki.messenger.databinding.ViewControlMessageBinding
import network.loki.messenger.libsession_util.util.ExpiryMode
import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.utilities.ExpirationUtil.getExpirationDisplayValue import org.session.libsession.utilities.ExpirationUtil.getExpirationDisplayValue
import org.session.libsession.utilities.getExpirationTypeDisplayValue import org.session.libsession.utilities.getExpirationTypeDisplayValue
import org.thoughtcrime.securesms.conversation.disappearingmessages.DisappearingMessages
import org.thoughtcrime.securesms.database.model.MessageRecord import org.thoughtcrime.securesms.database.model.MessageRecord
import org.thoughtcrime.securesms.showSessionDialog import org.thoughtcrime.securesms.showSessionDialog
import javax.inject.Inject
import kotlin.time.Duration.Companion.milliseconds import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.Duration.Companion.seconds import kotlin.time.Duration.Companion.seconds
private val MessageRecord.expiryMode get() = if (expiresIn <= 0) ExpiryMode.NONE
else if (expireStarted == timestamp) ExpiryMode.AfterSend(expiresIn / 1000)
else ExpiryMode.AfterRead(expiresIn / 1000)
@AndroidEntryPoint
class ControlMessageView : LinearLayout { class ControlMessageView : LinearLayout {
private lateinit var binding: ViewControlMessageBinding private lateinit var binding: ViewControlMessageBinding
@ -26,6 +36,8 @@ class ControlMessageView : LinearLayout {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() } constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() } constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
@Inject lateinit var disappearingMessages: DisappearingMessages
private fun initialize() { private fun initialize() {
binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true) binding = ViewControlMessageBinding.inflate(LayoutInflater.from(context), this, true)
layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT) layoutParams = RecyclerView.LayoutParams(RecyclerView.LayoutParams.MATCH_PARENT, RecyclerView.LayoutParams.WRAP_CONTENT)
@ -42,24 +54,31 @@ class ControlMessageView : LinearLayout {
message.isExpirationTimerUpdate -> { message.isExpirationTimerUpdate -> {
binding.apply { binding.apply {
expirationTimerView.isVisible = true expirationTimerView.isVisible = true
expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn) expirationTimerView.setExpirationTime(message.expireStarted, message.expiresIn)
followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled && !message.isOutgoing
followSetting.isVisible = ExpirationConfiguration.isNewConfigEnabled
&& !message.isOutgoing
&& message.expiryMode != (MessagingModuleConfiguration.shared.storage.getExpirationConfiguration(message.threadId)?.expiryMode ?: ExpiryMode.NONE)
followSetting.setOnClickListener { followSetting.setOnClickListener {
context.showSessionDialog { context.showSessionDialog {
val isOff = message.expiresIn == 0L
title(R.string.dialog_disappearing_messages_follow_setting_title) title(R.string.dialog_disappearing_messages_follow_setting_title)
if (isOff) { if (message.expiresIn == 0L) {
text(R.string.dialog_disappearing_messages_follow_setting_off_body) text(R.string.dialog_disappearing_messages_follow_setting_off_body)
} else { } else {
text( text(
context.getString( context.getString(
R.string.dialog_disappearing_messages_follow_setting_on_body, R.string.dialog_disappearing_messages_follow_setting_on_body,
getExpirationDisplayValue(context, message.expiresIn.milliseconds), getExpirationDisplayValue(context, message.expiresIn.milliseconds),
context.getExpirationTypeDisplayValue(message.expireStarted == message.timestamp) context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead)
) )
) )
} }
destructiveButton(if (isOff) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set) { } destructiveButton(if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set) {
disappearingMessages.set(message.threadId, message.recipient.address, message.expiryMode)
}
cancelButton() cancelButton()
} }
} }

View File

@ -8,6 +8,7 @@ import android.graphics.drawable.ColorDrawable
import android.os.Handler import android.os.Handler
import android.os.Looper import android.os.Looper
import android.util.AttributeSet import android.util.AttributeSet
import android.util.Log
import android.view.Gravity import android.view.Gravity
import android.view.HapticFeedbackConstants import android.view.HapticFeedbackConstants
import android.view.MotionEvent import android.view.MotionEvent
@ -61,6 +62,8 @@ import kotlin.math.min
import kotlin.math.roundToInt import kotlin.math.roundToInt
import kotlin.math.sqrt import kotlin.math.sqrt
private const val TAG = "VisibleMessageView"
@AndroidEntryPoint @AndroidEntryPoint
class VisibleMessageView : LinearLayout { class VisibleMessageView : LinearLayout {
@ -349,6 +352,8 @@ class VisibleMessageView : LinearLayout {
} }
private fun updateExpirationTimer(message: MessageRecord) { private fun updateExpirationTimer(message: MessageRecord) {
Log.d(TAG, "updateExpirationTimer() called with: message = $message")
if (!message.isOutgoing) binding.messageStatusTextView.bringToFront() if (!message.isOutgoing) binding.messageStatusTextView.bringToFront()
val expireStarted = message.expireStarted.takeIf { it > 0 } ?: SnodeAPI.nowWithOffset val expireStarted = message.expireStarted.takeIf { it > 0 } ?: SnodeAPI.nowWithOffset

View File

@ -54,6 +54,10 @@ public abstract class MessageRecord extends DisplayRecord {
private final List<ReactionRecord> reactions; private final List<ReactionRecord> reactions;
private final boolean hasMention; private final boolean hasMention;
public final boolean isNotDisappearAfterRead() {
return expireStarted == getTimestamp();
}
public abstract boolean isMms(); public abstract boolean isMms();
public abstract boolean isMmsNotification(); public abstract boolean isMmsNotification();

View File

@ -58,6 +58,8 @@ class MarkReadReceiver : BroadcastReceiver() {
) { ) {
if (markedReadMessages.isEmpty()) return if (markedReadMessages.isEmpty()) return
Log.d(TAG, "process() called with: context = $context, markedReadMessages = $markedReadMessages")
sendReadReceipts(context, markedReadMessages) sendReadReceipts(context, markedReadMessages)
markedReadMessages.forEach { scheduleDeletion(context, it.expirationInfo) } markedReadMessages.forEach { scheduleDeletion(context, it.expirationInfo) }
@ -119,6 +121,8 @@ class MarkReadReceiver : BroadcastReceiver() {
context: Context, context: Context,
hashToMessage: Map<String, MarkedMessageInfo> hashToMessage: Map<String, MarkedMessageInfo>
) { ) {
Log.d(TAG, "fetchUpdatedExpiriesAndScheduleDeletion() called with: context = $context, hashToMessage = $hashToMessage")
@Suppress("UNCHECKED_CAST") @Suppress("UNCHECKED_CAST")
val expiries = SnodeAPI.getExpiries(hashToMessage.keys.toList(), TextSecurePreferences.getLocalNumber(context)!!).get()["expiries"] as Map<String, Long> val expiries = SnodeAPI.getExpiries(hashToMessage.keys.toList(), TextSecurePreferences.getLocalNumber(context)!!).get()["expiries"] as Map<String, Long>
hashToMessage.forEach { (hash, info) -> expiries[hash]?.let { scheduleDeletion(context, info.expirationInfo, it - info.expirationInfo.expireStarted) } } hashToMessage.forEach { (hash, info) -> expiries[hash]?.let { scheduleDeletion(context, info.expirationInfo, it - info.expirationInfo.expireStarted) } }
@ -129,6 +133,8 @@ class MarkReadReceiver : BroadcastReceiver() {
expirationInfo: ExpirationInfo, expirationInfo: ExpirationInfo,
expiresIn: Long = expirationInfo.expiresIn expiresIn: Long = expirationInfo.expiresIn
) { ) {
Log.d(TAG, "scheduleDeletion() called with: context = $context, expirationInfo = $expirationInfo, expiresIn = $expiresIn")
if (expiresIn <= 0 || expirationInfo.expireStarted > 0) return if (expiresIn <= 0 || expirationInfo.expireStarted > 0) return
val now = SnodeAPI.nowWithOffset val now = SnodeAPI.nowWithOffset

View File

@ -62,7 +62,8 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
synchronized(expiringMessageReferences) { (expiringMessageReferences as Object).notifyAll() } synchronized(expiringMessageReferences) { (expiringMessageReferences as Object).notifyAll() }
} }
override fun setExpirationTimer(message: ExpirationTimerUpdate, expiryMode: ExpiryMode?) { override fun setExpirationTimer(message: ExpirationTimerUpdate) {
val expiryMode: ExpiryMode = message.expiryMode
Log.d(TAG, "setExpirationTimer() called with: message = $message, expiryMode = $expiryMode") Log.d(TAG, "setExpirationTimer() called with: message = $message, expiryMode = $expiryMode")
val userPublicKey = getLocalNumber(context) val userPublicKey = getLocalNumber(context)

View File

@ -155,6 +155,7 @@ fun MessageReceiver.cancelTypingIndicatorsIfNeeded(senderPublicKey: String) {
private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimerUpdate) { private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimerUpdate) {
if (ExpirationConfiguration.isNewConfigEnabled) return if (ExpirationConfiguration.isNewConfigEnabled) return
val module = MessagingModuleConfiguration.shared val module = MessagingModuleConfiguration.shared
try { try {
val threadId = fromSerialized(message.groupPublicKey?.let(::doubleEncodeGroupID) ?: message.sender!!) val threadId = fromSerialized(message.groupPublicKey?.let(::doubleEncodeGroupID) ?: message.sender!!)
@ -170,7 +171,7 @@ private fun MessageReceiver.handleExpirationTimerUpdate(message: ExpirationTimer
} catch (e: Exception) { } catch (e: Exception) {
Log.e("Loki", "Failed to update expiration configuration.") Log.e("Loki", "Failed to update expiration configuration.")
} }
SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, message.expiryMode) SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message)
} }
private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) { private fun MessageReceiver.handleDataExtractionNotification(message: DataExtractionNotification) {
@ -298,24 +299,25 @@ fun MessageReceiver.updateExpiryIfNeeded(
val lastDisappearingMessageChangeTimestamp = proto.lastDisappearingMessageChangeTimestamp val lastDisappearingMessageChangeTimestamp = proto.lastDisappearingMessageChangeTimestamp
// don't update any values for open groups
if (recipient.isOpenGroupRecipient && type != null) throw MessageReceiver.Error.InvalidMessage
val incoming1on1 = recipient.isContactRecipient && !message.isSenderSelf
val remoteConfig = ExpirationConfiguration( val remoteConfig = ExpirationConfiguration(
threadID, threadID,
expiryMode, expiryMode,
lastDisappearingMessageChangeTimestamp lastDisappearingMessageChangeTimestamp
) )
// don't update any values for open groups remoteConfig.takeUnless { incoming1on1 }?.takeIf {
if (recipient.isOpenGroupRecipient && type != null) throw MessageReceiver.Error.InvalidMessage
remoteConfig.takeIf {
localConfig == null localConfig == null
|| it.updatedTimestampMs > localConfig.updatedTimestampMs || it.updatedTimestampMs > localConfig.updatedTimestampMs
|| !isNewConfigEnabled && !proto.hasLastDisappearingMessageChangeTimestamp() } || !isNewConfigEnabled && !proto.hasLastDisappearingMessageChangeTimestamp()
?.let(storage::setExpirationConfiguration) }?.let(storage::setExpirationConfiguration)
if (message is ExpirationTimerUpdate) { if (message is ExpirationTimerUpdate) {
SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message, expiryMode) SSKEnvironment.shared.messageExpirationManager.setExpirationTimer(message)
} }
} }

View File

@ -38,7 +38,7 @@ class SSKEnvironment(
} }
interface MessageExpirationManagerProtocol { interface MessageExpirationManagerProtocol {
fun setExpirationTimer(message: ExpirationTimerUpdate, expiryType: ExpiryMode?) fun setExpirationTimer(message: ExpirationTimerUpdate)
fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long)
} }