pull/1313/head
Andrew 3 months ago
parent 2e50dc08bb
commit 398b5bf7b4

@ -43,18 +43,15 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) {
mappingAdapter.submitList(items.toAdapterItems()) mappingAdapter.submitList(items.toAdapterItems())
} }
private fun List<ActionItem>.toAdapterItems(): List<DisplayItem> { private fun List<ActionItem>.toAdapterItems(): List<DisplayItem> =
return this.mapIndexed { index, item -> mapIndexed { index, item ->
val displayType: DisplayType = when { when {
this.size == 1 -> DisplayType.ONLY size == 1 -> DisplayType.ONLY
index == 0 -> DisplayType.TOP index == 0 -> DisplayType.TOP
index == this.size - 1 -> DisplayType.BOTTOM index == size - 1 -> DisplayType.BOTTOM
else -> DisplayType.MIDDLE else -> DisplayType.MIDDLE
} }.let { DisplayItem(item, it) }
DisplayItem(item, displayType)
} }
}
private data class DisplayItem( private data class DisplayItem(
val item: ActionItem, val item: ActionItem,
@ -94,9 +91,7 @@ class ContextMenuList(recyclerView: RecyclerView, onItemClick: () -> Unit) {
color?.let(title::setTextColor) color?.let(title::setTextColor)
color?.let(subtitle::setTextColor) color?.let(subtitle::setTextColor)
subtitle.isGone = true subtitle.isGone = true
item.subtitle?.let { item.subtitle?.let { startSubtitleJob(subtitle, it) }
startSubtitleJob(subtitle, it)
}
itemView.setOnClickListener { itemView.setOnClickListener {
item.action.run() item.action.run()
onItemClick() onItemClick()

@ -5,9 +5,7 @@ import android.util.AttributeSet
import android.view.LayoutInflater import android.view.LayoutInflater
import android.view.ViewGroup import android.view.ViewGroup
import android.widget.LinearLayout import android.widget.LinearLayout
import androidx.annotation.DimenRes
import androidx.core.view.isVisible import androidx.core.view.isVisible
import androidx.core.view.marginEnd
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
import androidx.recyclerview.widget.ListAdapter import androidx.recyclerview.widget.ListAdapter
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
@ -21,6 +19,7 @@ import network.loki.messenger.libsession_util.util.ExpiryMode
import org.session.libsession.messaging.messages.ExpirationConfiguration import org.session.libsession.messaging.messages.ExpirationConfiguration
import org.session.libsession.messaging.open_groups.OpenGroup import org.session.libsession.messaging.open_groups.OpenGroup
import org.session.libsession.utilities.ExpirationUtil import org.session.libsession.utilities.ExpirationUtil
import org.session.libsession.utilities.modifyLayoutParams
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities import org.thoughtcrime.securesms.conversation.v2.utilities.MentionManagerUtilities
import org.thoughtcrime.securesms.database.GroupDatabase import org.thoughtcrime.securesms.database.GroupDatabase
@ -28,12 +27,14 @@ import org.thoughtcrime.securesms.database.LokiAPIDatabase
import org.thoughtcrime.securesms.util.DateUtils import org.thoughtcrime.securesms.util.DateUtils
import java.util.Locale import java.util.Locale
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.roundToInt
@AndroidEntryPoint @AndroidEntryPoint
class ConversationActionBarView : LinearLayout { class ConversationActionBarView @JvmOverloads constructor(
context: Context,
private lateinit var binding: ViewConversationActionBarBinding attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private val binding = ViewConversationActionBarBinding.inflate(LayoutInflater.from(context), this, true)
@Inject lateinit var lokiApiDb: LokiAPIDatabase @Inject lateinit var lokiApiDb: LokiAPIDatabase
@Inject lateinit var groupDb: GroupDatabase @Inject lateinit var groupDb: GroupDatabase
@ -46,12 +47,7 @@ class ConversationActionBarView : LinearLayout {
} }
} }
constructor(context: Context) : super(context) { initialize() } init {
constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { initialize() }
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) { initialize() }
private fun initialize() {
binding = ViewConversationActionBarBinding.inflate(LayoutInflater.from(context), this, true)
var previousState: Int var previousState: Int
var currentState = 0 var currentState = 0
binding.settingsPager.registerOnPageChangeCallback(object : OnPageChangeCallback() { binding.settingsPager.registerOnPageChangeCallback(object : OnPageChangeCallback() {
@ -68,8 +64,7 @@ class ConversationActionBarView : LinearLayout {
} }
}) })
binding.settingsPager.adapter = settingsAdapter binding.settingsPager.adapter = settingsAdapter
val mediator = TabLayoutMediator(binding.settingsTabLayout, binding.settingsPager) { _, _ -> } TabLayoutMediator(binding.settingsTabLayout, binding.settingsPager) { _, _ -> }.attach()
mediator.attach()
} }
fun bind( fun bind(
@ -80,42 +75,32 @@ class ConversationActionBarView : LinearLayout {
openGroup: OpenGroup? = null openGroup: OpenGroup? = null
) { ) {
this.delegate = delegate this.delegate = delegate
@DimenRes val sizeID: Int = if (recipient.isClosedGroupRecipient) { binding.profilePictureView.layoutParams = resources.getDimensionPixelSize(
R.dimen.medium_profile_picture_size if (recipient.isClosedGroupRecipient) R.dimen.medium_profile_picture_size else R.dimen.small_profile_picture_size
} else { ).let { LayoutParams(it, it) }
R.dimen.small_profile_picture_size
}
val size = resources.getDimension(sizeID).roundToInt()
binding.profilePictureView.layoutParams = LayoutParams(size, size)
MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadId, context) MentionManagerUtilities.populateUserPublicKeyCacheIfNeeded(threadId, context)
update(recipient, openGroup, config) update(recipient, openGroup, config)
} }
fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) { fun update(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
binding.profilePictureView.update(recipient) binding.profilePictureView.update(recipient)
binding.conversationTitleView.text = when { binding.conversationTitleView.text = recipient.takeUnless { it.isLocalNumber }?.toShortString() ?: context.getString(R.string.note_to_self)
recipient.isLocalNumber -> context.getString(R.string.note_to_self)
else -> recipient.toShortString()
}
updateSubtitle(recipient, openGroup, config) updateSubtitle(recipient, openGroup, config)
binding.conversationTitleContainer.apply { binding.conversationTitleContainer.modifyLayoutParams<MarginLayoutParams> {
layoutParams = (layoutParams as MarginLayoutParams).apply { marginEnd = if (recipient.showCallMenu()) 0 else binding.profilePictureView.width
marginEnd = if (recipient.showCallMenu()) 0 else binding.profilePictureView.width
}
} }
} }
fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) { fun updateSubtitle(recipient: Recipient, openGroup: OpenGroup? = null, config: ExpirationConfiguration? = null) {
val settings = mutableListOf<ConversationSetting>() val settings = mutableListOf<ConversationSetting>()
if (config?.isEnabled == true) { if (config?.isEnabled == true) {
val prefix = if (config.expiryMode is ExpiryMode.AfterRead) { val prefix = when (config.expiryMode) {
context.getString(R.string.expiration_type_disappear_after_read) is ExpiryMode.AfterRead -> R.string.expiration_type_disappear_after_read
} else { else -> R.string.expiration_type_disappear_after_send
context.getString(R.string.expiration_type_disappear_after_send) }.let(context::getString)
}
settings += ConversationSetting( settings += ConversationSetting(
"$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.expiryMode.expirySeconds)}" , "$prefix - ${ExpirationUtil.getExpirationAbbreviatedDisplayValue(context, config.expiryMode.expirySeconds)}",
ConversationSettingType.EXPIRATION, ConversationSettingType.EXPIRATION,
R.drawable.ic_timer, R.drawable.ic_timer,
resources.getString(R.string.AccessibilityId_disappearing_messages_type_and_time) resources.getString(R.string.AccessibilityId_disappearing_messages_type_and_time)
@ -123,11 +108,9 @@ class ConversationActionBarView : LinearLayout {
} }
if (recipient.isMuted) { if (recipient.isMuted) {
settings += ConversationSetting( settings += ConversationSetting(
if (recipient.mutedUntil != Long.MAX_VALUE) { recipient.mutedUntil.takeUnless { it == Long.MAX_VALUE }
context.getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(recipient.mutedUntil, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())) ?.let { context.getString(R.string.ConversationActivity_muted_until_date, DateUtils.getFormattedDateTime(it, "EEE, MMM d, yyyy HH:mm", Locale.getDefault())) }
} else { ?: context.getString(R.string.ConversationActivity_muted_forever),
context.getString(R.string.ConversationActivity_muted_forever)
},
ConversationSettingType.NOTIFICATION, ConversationSettingType.NOTIFICATION,
R.drawable.ic_outline_notifications_off_24 R.drawable.ic_outline_notifications_off_24
) )
@ -174,23 +157,15 @@ class ConversationActionBarView : LinearLayout {
binding.leftArrowImageView.isVisible = itemCount > 1 binding.leftArrowImageView.isVisible = itemCount > 1
binding.rightArrowImageView.isVisible = itemCount > 1 binding.rightArrowImageView.isVisible = itemCount > 1
} }
} }
class SettingsDiffer: DiffUtil.ItemCallback<ConversationSetting>() { class SettingsDiffer: DiffUtil.ItemCallback<ConversationSetting>() {
override fun areItemsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting): Boolean { override fun areItemsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting): Boolean = oldItem.settingType === newItem.settingType
return oldItem.settingType === newItem.settingType override fun areContentsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting): Boolean = oldItem == newItem
}
override fun areContentsTheSame(oldItem: ConversationSetting, newItem: ConversationSetting): Boolean {
return oldItem == newItem
}
} }
} }
} }
fun interface ConversationActionBarDelegate { fun interface ConversationActionBarDelegate {
fun onDisappearingMessagesClicked() fun onDisappearingMessagesClicked()
} }
@ -206,4 +181,4 @@ enum class ConversationSettingType {
EXPIRATION, EXPIRATION,
MEMBER_COUNT, MEMBER_COUNT,
NOTIFICATION NOTIFICATION
} }

@ -45,11 +45,10 @@ class DisappearingMessages @Inject constructor(
fun showFollowSettingDialog(context: Context, message: MessageRecord) = context.showSessionDialog { fun showFollowSettingDialog(context: Context, message: MessageRecord) = context.showSessionDialog {
title(R.string.dialog_disappearing_messages_follow_setting_title) title(R.string.dialog_disappearing_messages_follow_setting_title)
if (message.expiresIn == 0L) { text(if (message.expiresIn == 0L) {
text(R.string.dialog_disappearing_messages_follow_setting_off_body) context.getString(R.string.dialog_disappearing_messages_follow_setting_off_body)
} else { } else {
text( context.getString(
context.getString(
R.string.dialog_disappearing_messages_follow_setting_on_body, R.string.dialog_disappearing_messages_follow_setting_on_body,
ExpirationUtil.getExpirationDisplayValue( ExpirationUtil.getExpirationDisplayValue(
context, context,
@ -57,8 +56,7 @@ class DisappearingMessages @Inject constructor(
), ),
context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead) context.getExpirationTypeDisplayValue(message.isNotDisappearAfterRead)
) )
) })
}
destructiveButton( destructiveButton(
text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set, text = if (message.expiresIn == 0L) R.string.dialog_disappearing_messages_follow_setting_confirm else R.string.dialog_disappearing_messages_follow_setting_set,
contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button contentDescription = if (message.expiresIn == 0L) R.string.AccessibilityId_confirm else R.string.AccessibilityId_set_button

@ -60,8 +60,8 @@ class DisappearingMessagesActivity: PassphraseRequiredActionBarActivity() {
lifecycleScope.launch { lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) { repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.state.collect { state -> viewModel.state.collect {
supportActionBar?.subtitle = state.subtitle(this@DisappearingMessagesActivity) supportActionBar?.subtitle = it.subtitle(this@DisappearingMessagesActivity)
} }
} }
} }

@ -63,8 +63,8 @@ class DisappearingMessagesViewModel(
val groupRecord = recipient?.takeIf { it.isClosedGroupRecipient } val groupRecord = recipient?.takeIf { it.isClosedGroupRecipient }
?.run { groupDb.getGroup(address.toGroupString()).orNull() } ?.run { groupDb.getGroup(address.toGroupString()).orNull() }
_state.update { state -> _state.update {
state.copy( it.copy(
address = recipient?.address, address = recipient?.address,
isGroup = groupRecord != null, isGroup = groupRecord != null,
isNoteToSelf = recipient?.address?.serialize() == textSecurePreferences.getLocalNumber(), isNoteToSelf = recipient?.address?.serialize() == textSecurePreferences.getLocalNumber(),

@ -470,7 +470,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
storage.markConversationAsRead(viewModel.threadId, it) storage.markConversationAsRead(viewModel.threadId, it)
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.d(TAG, "bufferedLastSeenChannel collectLatest", e) Log.e(TAG, "bufferedLastSeenChannel collectLatest", e)
} }
} }
} }
@ -486,8 +486,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
true, true,
screenshotObserver screenshotObserver
) )
val recipient = viewModel.recipient ?: return viewModel.run {
binding?.toolbarContent?.update(recipient, viewModel.openGroup, viewModel.expirationConfiguration) binding?.toolbarContent?.update(recipient ?: return, openGroup, expirationConfiguration)
}
} }
override fun onPause() { override fun onPause() {
@ -504,8 +505,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
override fun dispatchIntent(body: (Context) -> Intent?) { override fun dispatchIntent(body: (Context) -> Intent?) {
val intent = body(this) ?: return body(this)?.let { push(it, false) }
push(intent, false)
} }
override fun showDialog(dialogFragment: DialogFragment, tag: String?) { override fun showDialog(dialogFragment: DialogFragment, tag: String?) {
@ -684,21 +684,18 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
private fun getLatestOpenGroupInfoIfNeeded() { private fun getLatestOpenGroupInfoIfNeeded() {
viewModel.openGroup?.let { openGroup -> val openGroup = viewModel.openGroup ?: return
OpenGroupApi.getMemberCount(openGroup.room, openGroup.server).successUi { OpenGroupApi.getMemberCount(openGroup.room, openGroup.server) successUi {
binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!, openGroup, viewModel.expirationConfiguration) binding?.toolbarContent?.updateSubtitle(viewModel.recipient!!, openGroup, viewModel.expirationConfiguration)
maybeUpdateToolbar(viewModel.recipient!!) maybeUpdateToolbar(viewModel.recipient!!)
}
} }
} }
// called from onCreate // called from onCreate
private fun setUpBlockedBanner() { private fun setUpBlockedBanner() {
val recipient = viewModel.recipient ?: return val recipient = viewModel.recipient?.takeUnless { it.isGroupRecipient } ?: return
if (recipient.isGroupRecipient) { return }
val sessionID = recipient.address.toString() val sessionID = recipient.address.toString()
val contact = sessionContactDb.getContactWithSessionID(sessionID) val name = sessionContactDb.getContactWithSessionID(sessionID)?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
val name = contact?.displayName(Contact.ContactContext.REGULAR) ?: sessionID
binding?.blockedBannerTextView?.text = resources.getString(R.string.activity_conversation_blocked_banner_text, name) binding?.blockedBannerTextView?.text = resources.getString(R.string.activity_conversation_blocked_banner_text, name)
binding?.blockedBanner?.isVisible = recipient.isBlocked binding?.blockedBanner?.isVisible = recipient.isBlocked
binding?.blockedBanner?.setOnClickListener { viewModel.unblock() } binding?.blockedBanner?.setOnClickListener { viewModel.unblock() }
@ -790,7 +787,7 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
this this
) )
} }
viewModel.recipient?.let { maybeUpdateToolbar(it) } maybeUpdateToolbar(recipient)
return true return true
} }
@ -829,14 +826,9 @@ class ConversationActivityV2 : PassphraseRequiredActionBarActivity(), InputBarDe
} }
private fun showOrHideInputIfNeeded() { private fun showOrHideInputIfNeeded() {
val recipient = viewModel.recipient binding?.inputBar?.showInput = viewModel.recipient?.takeIf { it.isClosedGroupRecipient }
if (recipient != null && recipient.isClosedGroupRecipient) { ?.run { address.toGroupString().let(groupDb::getGroup).orNull()?.isActive == true }
val group = groupDb.getGroup(recipient.address.toGroupString()).orNull() ?: true
val isActive = (group?.isActive == true)
binding?.inputBar?.showInput = isActive
} else {
binding?.inputBar?.showInput = true
}
} }
private fun setUpMessageRequestsBar() { private fun setUpMessageRequestsBar() {

@ -27,6 +27,7 @@ import org.session.libsession.messaging.sending_receiving.attachments.Attachment
import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment import org.session.libsession.messaging.sending_receiving.attachments.DatabaseAttachment
import org.session.libsession.utilities.ThemeUtil import org.session.libsession.utilities.ThemeUtil
import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.getColorFromAttr
import org.session.libsession.utilities.modifyLayoutParams
import org.session.libsession.utilities.recipients.Recipient import org.session.libsession.utilities.recipients.Recipient
import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2 import org.thoughtcrime.securesms.conversation.v2.ConversationActivityV2
import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet import org.thoughtcrime.securesms.conversation.v2.ModalUrlBottomSheet
@ -198,9 +199,9 @@ class VisibleMessageContentView : ConstraintLayout {
isStart = isStartOfMessageCluster, isStart = isStartOfMessageCluster,
isEnd = isEndOfMessageCluster isEnd = isEndOfMessageCluster
) )
val layoutParams = binding.albumThumbnailView.root.layoutParams as ConstraintLayout.LayoutParams binding.albumThumbnailView.root.modifyLayoutParams<ConstraintLayout.LayoutParams> {
layoutParams.horizontalBias = if (message.isOutgoing) 1f else 0f horizontalBias = if (message.isOutgoing) 1f else 0f
binding.albumThumbnailView.root.layoutParams = layoutParams }
onContentClick.add { event -> onContentClick.add { event ->
binding.albumThumbnailView.root.calculateHitObject(event, message, thread, onAttachmentNeedsDownload) binding.albumThumbnailView.root.calculateHitObject(event, message, thread, onAttachmentNeedsDownload)
} }
@ -233,9 +234,9 @@ class VisibleMessageContentView : ConstraintLayout {
} }
} }
} }
val layoutParams = binding.contentParent.layoutParams as ConstraintLayout.LayoutParams binding.contentParent.modifyLayoutParams<ConstraintLayout.LayoutParams> {
layoutParams.horizontalBias = if (message.isOutgoing) 1f else 0f horizontalBias = if (message.isOutgoing) 1f else 0f
binding.contentParent.layoutParams = layoutParams }
} }
private val onContentClick: MutableList<((event: MotionEvent) -> Unit)> = mutableListOf() private val onContentClick: MutableList<((event: MotionEvent) -> Unit)> = mutableListOf()
@ -306,17 +307,9 @@ class VisibleMessageContentView : ConstraintLayout {
} }
@ColorInt @ColorInt
fun getTextColor(context: Context, message: MessageRecord): Int { fun getTextColor(context: Context, message: MessageRecord): Int = context.getColorFromAttr(
val colorAttribute = if (message.isOutgoing) { if (message.isOutgoing) R.attr.message_sent_text_color else R.attr.message_received_text_color
// sent )
R.attr.message_sent_text_color
} else {
// received
R.attr.message_received_text_color
}
return context.getColorFromAttr(colorAttribute)
}
} }
// endregion // endregion
} }

@ -36,6 +36,7 @@ import org.session.libsession.snode.SnodeAPI
import org.session.libsession.utilities.Address import org.session.libsession.utilities.Address
import org.session.libsession.utilities.ViewUtil import org.session.libsession.utilities.ViewUtil
import org.session.libsession.utilities.getColorFromAttr import org.session.libsession.utilities.getColorFromAttr
import org.session.libsession.utilities.modifyLayoutParams
import org.session.libsignal.utilities.IdPrefix import org.session.libsignal.utilities.IdPrefix
import org.session.libsignal.utilities.ThreadUtils import org.session.libsignal.utilities.ThreadUtils
import org.thoughtcrime.securesms.ApplicationContext import org.thoughtcrime.securesms.ApplicationContext
@ -243,14 +244,12 @@ class VisibleMessageView : LinearLayout {
private fun showStatusMessage(message: MessageRecord) { private fun showStatusMessage(message: MessageRecord) {
val disappearing = message.expiresIn > 0 val disappearing = message.expiresIn > 0
binding.messageInnerLayout.apply { binding.messageInnerLayout.modifyLayoutParams<FrameLayout.LayoutParams> {
layoutParams = (layoutParams as FrameLayout.LayoutParams) gravity = if (message.isOutgoing) Gravity.END else Gravity.START
.apply { gravity = if (message.isOutgoing) Gravity.END else Gravity.START }
} }
binding.statusContainer.apply { binding.statusContainer.modifyLayoutParams<ConstraintLayout.LayoutParams> {
layoutParams = (layoutParams as ConstraintLayout.LayoutParams) horizontalBias = if (message.isOutgoing) 1f else 0f
.apply { horizontalBias = if (message.isOutgoing) 1f else 0f }
} }
binding.expirationTimerView.isGone = true binding.expirationTimerView.isGone = true
@ -356,7 +355,7 @@ class VisibleMessageView : LinearLayout {
swipeToReplyIconRect.right = right swipeToReplyIconRect.right = right
swipeToReplyIconRect.bottom = bottom swipeToReplyIconRect.bottom = bottom
if (translationX < 0 /*&& !binding.expirationTimerView.isVisible*/) { if (translationX < 0 && !binding.expirationTimerView.isVisible) {
val threshold = swipeToReplyThreshold val threshold = swipeToReplyThreshold
swipeToReplyIcon.bounds = swipeToReplyIconRect swipeToReplyIcon.bounds = swipeToReplyIconRect
swipeToReplyIcon.alpha = (255.0f * (min(abs(translationX), threshold) / threshold)).roundToInt() swipeToReplyIcon.alpha = (255.0f * (min(abs(translationX), threshold) / threshold)).roundToInt()

@ -425,12 +425,10 @@ class LokiAPIDatabase(context: Context, helper: SQLCipherOpenHelper) : Database(
database.endTransaction() database.endTransaction()
} }
override fun getLastLegacySenderAddress(threadRecipientAddress: String): String? { override fun getLastLegacySenderAddress(threadRecipientAddress: String): String? =
val database = databaseHelper.readableDatabase databaseHelper.readableDatabase.get(LAST_LEGACY_MESSAGE_TABLE, LEGACY_THREAD_RECIPIENT_QUERY, wrap(threadRecipientAddress)) { cursor ->
return database.get(LAST_LEGACY_MESSAGE_TABLE, LEGACY_THREAD_RECIPIENT_QUERY, wrap(threadRecipientAddress)) { cursor ->
cursor.getString(LAST_LEGACY_SENDER_RECIPIENT) cursor.getString(LAST_LEGACY_SENDER_RECIPIENT)
} }
}
override fun setLastLegacySenderAddress( override fun setLastLegacySenderAddress(
threadRecipientAddress: String, threadRecipientAddress: String,

@ -305,8 +305,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
override fun markExpireStarted(messageId: Long, startedTimestamp: Long) { override fun markExpireStarted(messageId: Long, startedTimestamp: Long) {
Log.d(TAG, "markExpireStarted() called with: messageId = $messageId, startedTimestamp = $startedTimestamp")
val contentValues = ContentValues() val contentValues = ContentValues()
contentValues.put(EXPIRE_STARTED, startedTimestamp) contentValues.put(EXPIRE_STARTED, startedTimestamp)
val db = databaseHelper.writableDatabase val db = databaseHelper.writableDatabase
@ -886,8 +884,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
override fun deleteMessage(messageId: Long): Boolean { override fun deleteMessage(messageId: Long): Boolean {
Log.d(TAG, "deleteMessage() called with: messageId = $messageId")
val threadId = getThreadIdForMessage(messageId) val threadId = getThreadIdForMessage(messageId)
val attachmentDatabase = get(context).attachmentDatabase() val attachmentDatabase = get(context).attachmentDatabase()
queue(Runnable { attachmentDatabase.deleteAttachmentsForMessage(messageId) }) queue(Runnable { attachmentDatabase.deleteAttachmentsForMessage(messageId) })
@ -1405,8 +1401,6 @@ class MmsDatabase(context: Context, databaseHelper: SQLCipherOpenHelper) : Messa
} }
companion object { companion object {
private val TAG = MmsDatabase::class.java.simpleName private val TAG = MmsDatabase::class.java.simpleName
const val TABLE_NAME: String = "mms" const val TABLE_NAME: String = "mms"
const val DATE_SENT: String = "date" const val DATE_SENT: String = "date"

@ -14,6 +14,7 @@ import network.loki.messenger.libsession_util.util.Conversation
import network.loki.messenger.libsession_util.util.ExpiryMode import network.loki.messenger.libsession_util.util.ExpiryMode
import network.loki.messenger.libsession_util.util.GroupInfo import network.loki.messenger.libsession_util.util.GroupInfo
import network.loki.messenger.libsession_util.util.UserPic import network.loki.messenger.libsession_util.util.UserPic
import network.loki.messenger.libsession_util.util.afterSend
import org.session.libsession.avatars.AvatarHelper import org.session.libsession.avatars.AvatarHelper
import org.session.libsession.database.StorageProtocol import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.BlindedIdMapping import org.session.libsession.messaging.BlindedIdMapping
@ -498,16 +499,11 @@ open class Storage(
} }
// Set or reset the shared library to use latest expiration config // Set or reset the shared library to use latest expiration config
getThreadId(recipient)?.let { ourThread -> getThreadId(recipient)?.let {
val currentExpiration = getExpirationConfiguration(ourThread) setExpirationConfiguration(
if (currentExpiration != null && currentExpiration.updatedTimestampMs > messageTimestamp) { getExpirationConfiguration(it)?.takeIf { it.updatedTimestampMs > messageTimestamp } ?: ExpirationConfiguration(it, userProfile.getNtsExpiry(), messageTimestamp)
setExpirationConfiguration(currentExpiration) )
} else {
val expiration = ExpirationConfiguration(ourThread, userProfile.getNtsExpiry(), messageTimestamp)
setExpirationConfiguration(expiration)
}
} }
} }
private fun updateContacts(contacts: Contacts, messageTimestamp: Long) { private fun updateContacts(contacts: Contacts, messageTimestamp: Long) {
@ -635,20 +631,11 @@ open class Storage(
// Start polling // Start polling
ClosedGroupPollerV2.shared.startPolling(group.sessionId) ClosedGroupPollerV2.shared.startPolling(group.sessionId)
} }
getThreadId(Address.fromSerialized(groupId))?.let { conversationThreadId -> getThreadId(Address.fromSerialized(groupId))?.let {
setExpirationConfiguration(
val currentExpiration = getExpirationConfiguration(conversationThreadId) getExpirationConfiguration(it)?.takeIf { it.updatedTimestampMs > messageTimestamp }
if (currentExpiration != null && currentExpiration.updatedTimestampMs > messageTimestamp) { ?: ExpirationConfiguration(it, afterSend(group.disappearingTimer), messageTimestamp)
setExpirationConfiguration(currentExpiration) )
} else {
val mode =
if (group.disappearingTimer == 0L) ExpiryMode.NONE
else ExpiryMode.AfterSend(group.disappearingTimer)
val newConfig = ExpirationConfiguration(
conversationThreadId, mode, messageTimestamp
)
setExpirationConfiguration(newConfig)
}
} }
} }
} }
@ -1213,18 +1200,11 @@ open class Storage(
setPinned(conversationThreadId, contact.priority == PRIORITY_PINNED) setPinned(conversationThreadId, contact.priority == PRIORITY_PINNED)
} }
} }
getThreadId(recipient)?.let { conversationThreadId -> getThreadId(recipient)?.let {
val currentExpiration = getExpirationConfiguration(conversationThreadId) setExpirationConfiguration(
if (currentExpiration != null && currentExpiration.updatedTimestampMs > timestamp) { getExpirationConfiguration(it)?.takeIf { it.updatedTimestampMs > timestamp }
setExpirationConfiguration(currentExpiration) ?: ExpirationConfiguration(it, contact.expiryMode, timestamp)
} else { )
val expiration = ExpirationConfiguration(
conversationThreadId,
contact.expiryMode,
timestamp
)
setExpirationConfiguration(expiration)
}
} }
setRecipientHash(recipient, contact.hashCode().toString()) setRecipientHash(recipient, contact.hashCode().toString())
} }
@ -1340,26 +1320,25 @@ open class Storage(
} }
override fun getLastLegacyRecipient(threadRecipient: String): String? = override fun getLastLegacyRecipient(threadRecipient: String): String? =
DatabaseComponent.get(context).lokiAPIDatabase().getLastLegacySenderAddress(threadRecipient) DatabaseComponent.get(context).lokiAPIDatabase().getLastLegacySenderAddress(threadRecipient)
override fun setLastLegacyRecipient(threadRecipient: String, senderRecipient: String?) { override fun setLastLegacyRecipient(threadRecipient: String, senderRecipient: String?) {
DatabaseComponent.get(context).lokiAPIDatabase().setLastLegacySenderAddress(threadRecipient, senderRecipient) DatabaseComponent.get(context).lokiAPIDatabase().setLastLegacySenderAddress(threadRecipient, senderRecipient)
} }
override fun deleteConversation(threadID: Long) { override fun deleteConversation(threadID: Long) {
val recipient = getRecipientForThread(threadID)
val threadDB = DatabaseComponent.get(context).threadDatabase() val threadDB = DatabaseComponent.get(context).threadDatabase()
val groupDB = DatabaseComponent.get(context).groupDatabase() val groupDB = DatabaseComponent.get(context).groupDatabase()
threadDB.deleteConversation(threadID) threadDB.deleteConversation(threadID)
if (recipient != null) { val recipient = getRecipientForThread(threadID) ?: return
if (recipient.isContactRecipient) { when {
recipient.isContactRecipient -> {
if (recipient.isLocalNumber) return if (recipient.isLocalNumber) return
val contacts = configFactory.contacts ?: return val contacts = configFactory.contacts ?: return
contacts.upsertContact(recipient.address.serialize()) { contacts.upsertContact(recipient.address.serialize()) { priority = PRIORITY_HIDDEN }
this.priority = PRIORITY_HIDDEN
}
ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context) ConfigurationMessageUtilities.forceSyncConfigurationNowIfNeeded(context)
} else if (recipient.isClosedGroupRecipient) { }
recipient.isClosedGroupRecipient -> {
// TODO: handle closed group // TODO: handle closed group
val volatile = configFactory.convoVolatile ?: return val volatile = configFactory.convoVolatile ?: return
val groups = configFactory.userGroups ?: return val groups = configFactory.userGroups ?: return

@ -28,21 +28,18 @@ class MarkReadReceiver : BroadcastReceiver() {
@SuppressLint("StaticFieldLeak") @SuppressLint("StaticFieldLeak")
override fun onReceive(context: Context, intent: Intent) { override fun onReceive(context: Context, intent: Intent) {
if (CLEAR_ACTION != intent.action) return if (CLEAR_ACTION != intent.action) return
val threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA) val threadIds = intent.getLongArrayExtra(THREAD_IDS_EXTRA) ?: return
if (threadIds != null) { NotificationManagerCompat.from(context).cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1))
NotificationManagerCompat.from(context) object : AsyncTask<Void?, Void?, Void?>() {
.cancel(intent.getIntExtra(NOTIFICATION_ID_EXTRA, -1)) override fun doInBackground(vararg params: Void?): Void? {
object : AsyncTask<Void?, Void?, Void?>() { val currentTime = nowWithOffset
override fun doInBackground(vararg params: Void?): Void? { threadIds.forEach {
val currentTime = nowWithOffset Log.i(TAG, "Marking as read: $it")
threadIds.forEach { shared.storage.markConversationAsRead(it, currentTime, true)
Log.i(TAG, "Marking as read: $it")
shared.storage.markConversationAsRead(it, currentTime, true)
}
return null
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR) return null
} }
}.executeOnExecutor(AsyncTask.THREAD_POOL_EXECUTOR)
} }
companion object { companion object {
@ -111,17 +108,17 @@ class MarkReadReceiver : BroadcastReceiver() {
context: Context, context: Context,
markedReadMessages: List<MarkedMessageInfo> markedReadMessages: List<MarkedMessageInfo>
) { ) {
if (isReadReceiptsEnabled(context)) { if (!isReadReceiptsEnabled(context)) return
markedReadMessages.map { it.syncMessageId }
.filter { shouldSendReadReceipt(Recipient.from(context, it.address, false)) } markedReadMessages.map { it.syncMessageId }
.groupBy { it.address } .filter { shouldSendReadReceipt(Recipient.from(context, it.address, false)) }
.forEach { (address, messages) -> .groupBy { it.address }
messages.map { it.timetamp } .forEach { (address, messages) ->
.let(::ReadReceipt) messages.map { it.timetamp }
.apply { sentTimestamp = nowWithOffset } .let(::ReadReceipt)
.let { send(it, address) } .apply { sentTimestamp = nowWithOffset }
} .let { send(it, address) }
} }
} }
private fun fetchUpdatedExpiriesAndScheduleDeletion( private fun fetchUpdatedExpiriesAndScheduleDeletion(

@ -154,7 +154,7 @@ class DefaultConversationRepository @Inject constructor(
openGroupInvitation.url = openGroup.joinURL openGroupInvitation.url = openGroup.joinURL
message.openGroupInvitation = openGroupInvitation message.openGroupInvitation = openGroupInvitation
val expirationConfig = storage.getExpirationConfiguration(threadId) val expirationConfig = storage.getExpirationConfiguration(threadId)
val expiresInMillis = (expirationConfig?.expiryMode?.expiryMillis ?: 0) val expiresInMillis = expirationConfig?.expiryMode?.expiryMillis ?: 0
val expireStartedAt = if (expirationConfig?.expiryMode is ExpiryMode.AfterSend) message.sentTimestamp!! else 0 val expireStartedAt = if (expirationConfig?.expiryMode is ExpiryMode.AfterSend) message.sentTimestamp!! else 0
val outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation( val outgoingTextMessage = OutgoingTextMessage.fromOpenGroupInvitation(
openGroupInvitation, openGroupInvitation,
@ -225,14 +225,10 @@ class DefaultConversationRepository @Inject constructor(
override fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? { override fun buildUnsendRequest(recipient: Recipient, message: MessageRecord): UnsendRequest? {
if (recipient.isOpenGroupRecipient) return null if (recipient.isOpenGroupRecipient) return null
messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?: return null messageDataProvider.getServerHashForMessage(message.id, message.isMms) ?: return null
return UnsendRequest().apply { return UnsendRequest(
author = if (message.isOutgoing) { author = message.takeUnless { it.isOutgoing }?.run { individualRecipient.address.contactIdentifier() } ?: textSecurePreferences.getLocalNumber(),
textSecurePreferences.getLocalNumber()
} else {
message.individualRecipient.address.contactIdentifier()
}
timestamp = message.timestamp timestamp = message.timestamp
} )
} }
override suspend fun deleteMessageWithoutUnsendRequest( override suspend fun deleteMessageWithoutUnsendRequest(

@ -48,8 +48,6 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
private fun getDatabase(mms: Boolean) = if (mms) mmsDatabase else smsDatabase private fun getDatabase(mms: Boolean) = if (mms) mmsDatabase else smsDatabase
fun scheduleDeletion(id: Long, mms: Boolean, startedAtTimestamp: Long, expiresInMillis: Long) { fun scheduleDeletion(id: Long, mms: Boolean, startedAtTimestamp: Long, expiresInMillis: Long) {
Log.d(TAG, "scheduleDeletion() called with: id = $id, mms = $mms, startedAtTimestamp = $startedAtTimestamp, expiresInMillis = $expiresInMillis")
if (startedAtTimestamp <= 0) return if (startedAtTimestamp <= 0) return
val expiresAtMillis = startedAtTimestamp + expiresInMillis val expiresAtMillis = startedAtTimestamp + expiresInMillis
@ -67,7 +65,6 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
message: ExpirationTimerUpdate, message: ExpirationTimerUpdate,
expireStartedAt: Long expireStartedAt: Long
) { ) {
Log.d(TAG, "insertIncomingExpirationTimerMessage() called with: message = $message, expireStartedAt = $expireStartedAt")
val senderPublicKey = message.sender val senderPublicKey = message.sender
val sentTimestamp = message.sentTimestamp val sentTimestamp = message.sentTimestamp
val groupId = message.groupPublicKey val groupId = message.groupPublicKey
@ -118,7 +115,6 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
message: ExpirationTimerUpdate, message: ExpirationTimerUpdate,
expireStartedAt: Long expireStartedAt: Long
) { ) {
Log.d(TAG, "insertOutgoingExpirationTimerMessage() called with: message = $message, expireStartedAt = $expireStartedAt")
val sentTimestamp = message.sentTimestamp val sentTimestamp = message.sentTimestamp
val groupId = message.groupPublicKey val groupId = message.groupPublicKey
val duration = message.expiryMode.expiryMillis val duration = message.expiryMode.expiryMillis
@ -152,7 +148,6 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
override fun insertExpirationTimerMessage(message: ExpirationTimerUpdate) { override fun insertExpirationTimerMessage(message: ExpirationTimerUpdate) {
val expiryMode: ExpiryMode = message.expiryMode val expiryMode: ExpiryMode = message.expiryMode
Log.d(TAG, "setExpirationTimer() called with: message = $message, expiryMode = $expiryMode")
val userPublicKey = getLocalNumber(context) val userPublicKey = getLocalNumber(context)
val senderPublicKey = message.sender val senderPublicKey = message.sender
@ -171,12 +166,10 @@ class ExpiringMessageManager(context: Context) : MessageExpirationManagerProtoco
} }
override fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) { override fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) {
Log.d(TAG, "startAnyExpiration() called with: timestamp = $timestamp, author = $author, expireStartedAt = $expireStartedAt")
mmsSmsDatabase.getMessageFor(timestamp, author)?.run { mmsSmsDatabase.getMessageFor(timestamp, author)?.run {
getDatabase(isMms()).markExpireStarted(getId(), expireStartedAt) getDatabase(isMms()).markExpireStarted(getId(), expireStartedAt)
scheduleDeletion(getId(), isMms(), expireStartedAt, expiresIn) scheduleDeletion(getId(), isMms(), expireStartedAt, expiresIn)
} ?: Log.e(TAG, "no message record!!!") } ?: Log.e(TAG, "no message record!")
} }
private inner class LoadTask : Runnable { private inner class LoadTask : Runnable {

@ -14,3 +14,5 @@ sealed class ExpiryMode(val expirySeconds: Long) {
fun coerceSendToRead(coerce: Boolean = true) = if (coerce && this is AfterSend) AfterRead(expirySeconds) else this fun coerceSendToRead(coerce: Boolean = true) = if (coerce && this is AfterSend) AfterRead(expirySeconds) else this
} }
fun afterSend(seconds: Long) = seconds.takeIf { it > 0 }?.let(ExpiryMode::AfterSend) ?: ExpiryMode.NONE

@ -144,7 +144,6 @@ class BatchMessageReceiveJob(
runBlocking(Dispatchers.IO) { runBlocking(Dispatchers.IO) {
fun processMessages(threadId: Long, messages: List<ParsedMessage>) = async { fun processMessages(threadId: Long, messages: List<ParsedMessage>) = async {
Log.d(TAG, "processMessages() threadId = $threadId, messages = $messages")
// The LinkedHashMap should preserve insertion order // The LinkedHashMap should preserve insertion order
val messageIds = linkedMapOf<Long, Pair<Boolean, Boolean>>() val messageIds = linkedMapOf<Long, Pair<Boolean, Boolean>>()
val myLastSeen = storage.getLastSeen(threadId) val myLastSeen = storage.getLastSeen(threadId)

@ -6,7 +6,6 @@ import org.session.libsession.database.StorageProtocol
import org.session.libsession.messaging.MessagingModuleConfiguration import org.session.libsession.messaging.MessagingModuleConfiguration
import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate import org.session.libsession.messaging.messages.control.ExpirationTimerUpdate
import org.session.libsession.messaging.messages.visible.VisibleMessage import org.session.libsession.messaging.messages.visible.VisibleMessage
import org.session.libsession.utilities.Address
import org.session.libsession.utilities.GroupUtil import org.session.libsession.utilities.GroupUtil
import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType import org.session.libsignal.protos.SignalServiceProtos.Content.ExpirationType
@ -43,13 +42,11 @@ abstract class Message {
} }
} }
open fun isValid(): Boolean { open fun isValid(): Boolean =
val sentTimestamp = sentTimestamp sentTimestamp?.let { it > 0 } == true
if (sentTimestamp != null && sentTimestamp <= 0) { return false } && receivedTimestamp?.let { it > 0 } == true
val receivedTimestamp = receivedTimestamp && sender != null
if (receivedTimestamp != null && receivedTimestamp <= 0) { return false } && recipient != null
return sender != null && recipient != null
}
abstract fun toProto(): SignalServiceProtos.Content? abstract fun toProto(): SignalServiceProtos.Content?
@ -60,18 +57,17 @@ abstract class Message {
}.build() }.build()
} }
fun SignalServiceProtos.Content.Builder.applyExpiryMode(): SignalServiceProtos.Content.Builder { fun SignalServiceProtos.Content.Builder.applyExpiryMode() = apply {
expirationTimer = expiryMode.expirySeconds.toInt() expirationTimer = expiryMode.expirySeconds.toInt()
expirationType = when (expiryMode) { expirationType = when (expiryMode) {
is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND is ExpiryMode.AfterSend -> ExpirationType.DELETE_AFTER_SEND
is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ is ExpiryMode.AfterRead -> ExpirationType.DELETE_AFTER_READ
else -> ExpirationType.UNKNOWN else -> ExpirationType.UNKNOWN
} }
return this
} }
} }
inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Content): M { inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Content): M = apply {
(proto.takeIf { it.hasExpirationTimer() }?.expirationTimer ?: proto.dataMessage?.expireTimer)?.let { duration -> (proto.takeIf { it.hasExpirationTimer() }?.expirationTimer ?: proto.dataMessage?.expireTimer)?.let { duration ->
expiryMode = when (proto.expirationType.takeIf { duration > 0 }) { expiryMode = when (proto.expirationType.takeIf { duration > 0 }) {
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong()) ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
@ -79,23 +75,21 @@ inline fun <reified M: Message> M.copyExpiration(proto: SignalServiceProtos.Cont
else -> ExpiryMode.NONE else -> ExpiryMode.NONE
} }
} }
return this
} }
fun SignalServiceProtos.Content.expiryMode(): ExpiryMode = fun SignalServiceProtos.Content.expiryMode(): ExpiryMode =
(takeIf { it.hasExpirationTimer() }?.expirationTimer ?: dataMessage?.expireTimer)?.let { duration -> (takeIf { it.hasExpirationTimer() }?.expirationTimer ?: dataMessage?.expireTimer)?.let { duration ->
when (expirationType.takeIf { duration > 0 }) { when (expirationType.takeIf { duration > 0 }) {
ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong()) ExpirationType.DELETE_AFTER_SEND -> ExpiryMode.AfterSend(duration.toLong())
ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong()) ExpirationType.DELETE_AFTER_READ -> ExpiryMode.AfterRead(duration.toLong())
else -> ExpiryMode.NONE else -> ExpiryMode.NONE
} }
} ?: ExpiryMode.NONE } ?: ExpiryMode.NONE
/** /**
* Apply ExpiryMode from the current setting. * Apply ExpiryMode from the current setting.
*/ */
inline fun <reified M: Message> M.applyExpiryMode(thread: Long): M { inline fun <reified M: Message> M.applyExpiryMode(thread: Long): M = apply {
val storage = MessagingModuleConfiguration.shared.storage val storage = MessagingModuleConfiguration.shared.storage
expiryMode = storage.getExpirationConfiguration(thread)?.expiryMode?.coerceSendToRead(coerceDisappearAfterSendToRead) ?: ExpiryMode.NONE expiryMode = storage.getExpirationConfiguration(thread)?.expiryMode?.coerceSendToRead(coerceDisappearAfterSendToRead) ?: ExpiryMode.NONE
return this
} }

@ -4,9 +4,7 @@ import org.session.libsession.messaging.messages.copyExpiration
import org.session.libsignal.protos.SignalServiceProtos import org.session.libsignal.protos.SignalServiceProtos
import org.session.libsignal.utilities.Log import org.session.libsignal.utilities.Log
class UnsendRequest(): ControlMessage() { class UnsendRequest(var timestamp: Long? = null, var author: String? = null): ControlMessage() {
var timestamp: Long? = null
var author: String? = null
override val isSelfSendValid: Boolean = true override val isSelfSendValid: Boolean = true
@ -20,18 +18,8 @@ class UnsendRequest(): ControlMessage() {
companion object { companion object {
const val TAG = "UnsendRequest" const val TAG = "UnsendRequest"
fun fromProto(proto: SignalServiceProtos.Content): UnsendRequest? { fun fromProto(proto: SignalServiceProtos.Content): UnsendRequest? =
val unsendRequestProto = if (proto.hasUnsendRequest()) proto.unsendRequest else return null proto.takeIf { it.hasUnsendRequest() }?.unsendRequest?.run { UnsendRequest(timestamp, author) }?.copyExpiration(proto)
val timestamp = unsendRequestProto.timestamp
val author = unsendRequestProto.author
return UnsendRequest(timestamp, author)
.copyExpiration(proto)
}
}
constructor(timestamp: Long, author: String) : this() {
this.timestamp = timestamp
this.author = author
} }
override fun toProto(): SignalServiceProtos.Content? { override fun toProto(): SignalServiceProtos.Content? {

@ -108,27 +108,23 @@ object GroupUtil {
@JvmStatic @JvmStatic
@Throws(IOException::class) @Throws(IOException::class)
fun doubleDecodeGroupId(groupID: String): String { fun doubleDecodeGroupId(groupID: String): String =
return Hex.toStringCondensed(getDecodedGroupIDAsData(getDecodedGroupID(groupID))) Hex.toStringCondensed(getDecodedGroupIDAsData(getDecodedGroupID(groupID)))
}
@JvmStatic @JvmStatic
fun addressToGroupSessionId(address: Address): String { fun addressToGroupSessionId(address: Address): String =
return doubleDecodeGroupId(address.toGroupString()) doubleDecodeGroupId(address.toGroupString())
}
fun createConfigMemberMap( fun createConfigMemberMap(
members: Collection<String>, members: Collection<String>,
admins: Collection<String> admins: Collection<String>
): Map<String, Boolean> { ): Map<String, Boolean> {
// Start with admins // Start with admins
val memberMap = admins.associate { val memberMap = admins.associateWith { true }.toMutableMap()
it to true
}.toMutableMap()
// Add the remaining members (there may be duplicates, so only add ones that aren't already in there from admins) // Add the remaining members (there may be duplicates, so only add ones that aren't already in there from admins)
for (member in members) { for (member in members) {
if (!memberMap.contains(member)) { if (member !in memberMap) {
memberMap[member] = false memberMap[member] = false
} }
} }

@ -46,8 +46,6 @@ class SSKEnvironment(
fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long) fun startAnyExpiration(timestamp: Long, author: String, expireStartedAt: Long)
fun maybeStartExpiration(message: Message, startDisappearAfterRead: Boolean = false) { fun maybeStartExpiration(message: Message, startDisappearAfterRead: Boolean = false) {
Log.d("MessageExpirationManagerProtocol", "maybeStartExpiration() called with: message = $message, startDisappearAfterRead = $startDisappearAfterRead")
if (message is ExpirationTimerUpdate && message.isGroup) return if (message is ExpirationTimerUpdate && message.isGroup) return
maybeStartExpiration( maybeStartExpiration(
@ -59,8 +57,6 @@ class SSKEnvironment(
} }
fun startDisappearAfterRead(timestamp: Long, sender: String) { fun startDisappearAfterRead(timestamp: Long, sender: String) {
Log.d("MessageExpirationManagerProtocol", "startDisappearAfterRead() called with: timestamp = $timestamp, sender = $sender")
startAnyExpiration( startAnyExpiration(
timestamp, timestamp,
sender, sender,
@ -69,8 +65,6 @@ class SSKEnvironment(
} }
fun maybeStartExpiration(timestamp: Long, sender: String, mode: ExpiryMode, startDisappearAfterRead: Boolean = false) { fun maybeStartExpiration(timestamp: Long, sender: String, mode: ExpiryMode, startDisappearAfterRead: Boolean = false) {
Log.d("MessageExpirationManagerProtocol", "maybeStartExpiration() called with: timestamp = $timestamp, sender = $sender, mode = $mode, startDisappearAfterRead = $startDisappearAfterRead")
val expireStartedAt = when (mode) { val expireStartedAt = when (mode) {
is ExpiryMode.AfterSend -> timestamp is ExpiryMode.AfterSend -> timestamp
is ExpiryMode.AfterRead -> if (startDisappearAfterRead) nowWithOffset.coerceAtLeast(timestamp + 1) else return is ExpiryMode.AfterRead -> if (startDisappearAfterRead) nowWithOffset.coerceAtLeast(timestamp + 1) else return

@ -2,6 +2,8 @@ package org.session.libsession.utilities
import android.content.Context import android.content.Context
import android.util.TypedValue import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import androidx.annotation.AttrRes import androidx.annotation.AttrRes
import androidx.annotation.ColorInt import androidx.annotation.ColorInt
@ -14,3 +16,7 @@ fun Context.getColorFromAttr(
theme.resolveAttribute(attrColor, typedValue, resolveRefs) theme.resolveAttribute(attrColor, typedValue, resolveRefs)
return typedValue.data return typedValue.data
} }
inline fun <reified LP: ViewGroup.LayoutParams> View.modifyLayoutParams(function: LP.() -> Unit) {
layoutParams = (layoutParams as LP).apply { function() }
}

Loading…
Cancel
Save