Fix auto-update timestamps.

This commit is contained in:
Alex Hart 2023-09-18 08:47:09 -04:00
parent 1c55ad21a3
commit 4bf8e2c488
6 changed files with 113 additions and 22 deletions

View file

@ -12,6 +12,7 @@ import org.signal.core.util.logging.Log;
import org.thoughtcrime.securesms.components.mention.MentionAnnotation;
import org.thoughtcrime.securesms.conversation.mutiselect.Multiselect;
import org.thoughtcrime.securesms.conversation.mutiselect.MultiselectCollection;
import org.thoughtcrime.securesms.conversation.v2.computed.FormattedDate;
import org.thoughtcrime.securesms.database.BodyRangeUtil;
import org.thoughtcrime.securesms.database.MentionUtil;
import org.thoughtcrime.securesms.database.NoSuchMessageException;
@ -45,7 +46,7 @@ public class ConversationMessage {
@NonNull private final Recipient threadRecipient;
private final boolean hasBeenQuoted;
@Nullable private final MessageRecord originalMessage;
@NonNull private final String formattedDate;
@NonNull private final ComputedProperties computedProperties;
private ConversationMessage(@NonNull MessageRecord messageRecord,
@Nullable CharSequence body,
@ -54,15 +55,15 @@ public class ConversationMessage {
@Nullable MessageStyler.Result styleResult,
@NonNull Recipient threadRecipient,
@Nullable MessageRecord originalMessage,
@NonNull String formattedDate)
@NonNull ComputedProperties computedProperties)
{
this.messageRecord = messageRecord;
this.hasBeenQuoted = hasBeenQuoted;
this.mentions = mentions != null ? mentions : Collections.emptyList();
this.styleResult = styleResult != null ? styleResult : MessageStyler.Result.none();
this.threadRecipient = threadRecipient;
this.originalMessage = originalMessage;
this.formattedDate = formattedDate;
this.messageRecord = messageRecord;
this.hasBeenQuoted = hasBeenQuoted;
this.mentions = mentions != null ? mentions : Collections.emptyList();
this.styleResult = styleResult != null ? styleResult : MessageStyler.Result.none();
this.threadRecipient = threadRecipient;
this.originalMessage = originalMessage;
this.computedProperties = computedProperties;
if (body != null) {
this.body = SpannableString.valueOf(body);
@ -95,9 +96,8 @@ public class ConversationMessage {
return hasBeenQuoted;
}
@NonNull
public String getFormattedDate() {
return formattedDate;
public @NonNull ComputedProperties getComputedProperties() {
return computedProperties;
}
@Override
@ -160,6 +160,27 @@ public class ConversationMessage {
return threadRecipient;
}
public static @NonNull FormattedDate getFormattedDate(@NonNull Context context, @NonNull MessageRecord messageRecord) {
return MessageRecordUtil.isScheduled(messageRecord) ? new FormattedDate(false, DateUtils.getOnlyTimeString(context, Locale.getDefault(), ((MediaMmsMessageRecord) messageRecord).getScheduledDate()))
: DateUtils.getDatelessRelativeTimeSpanFormattedDate(context, Locale.getDefault(), messageRecord.getTimestamp());
}
public static class ComputedProperties {
private @NonNull FormattedDate formattedDate;
ComputedProperties(@NonNull FormattedDate formattedDate) {
this.formattedDate = formattedDate;
}
public synchronized FormattedDate getFormattedDate() {
return formattedDate;
}
public synchronized void setFormattedDate(@NonNull FormattedDate formattedDate) {
this.formattedDate = formattedDate;
}
}
/**
* Factory providing multiple ways of creating {@link ConversationMessage}s.
*/
@ -204,8 +225,8 @@ public class ConversationMessage {
}
}
String formattedDate = MessageRecordUtil.isScheduled(messageRecord) ? DateUtils.getOnlyTimeString(context, Locale.getDefault(), ((MediaMmsMessageRecord) messageRecord).getScheduledDate())
: DateUtils.getDatelessRelativeTimeSpanString(context, Locale.getDefault(), messageRecord.getTimestamp());
FormattedDate formattedDate = getFormattedDate(context, messageRecord);
return new ConversationMessage(messageRecord,
styledAndMentionBody != null ? styledAndMentionBody : mentionsUpdate != null ? mentionsUpdate.getBody() : body,
mentionsUpdate != null ? mentionsUpdate.getMentions() : null,
@ -213,7 +234,7 @@ public class ConversationMessage {
styleResult,
threadRecipient,
originalMessage,
formattedDate);
new ComputedProperties(formattedDate));
}
/**

View file

@ -183,6 +183,8 @@ import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryChanged
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryReplacement
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryResultsControllerV2
import org.thoughtcrime.securesms.conversation.ui.inlinequery.InlineQueryViewModelV2
import org.thoughtcrime.securesms.conversation.v2.computed.ConversationMessageComputeWorkers
import org.thoughtcrime.securesms.conversation.v2.data.ConversationMessageElement
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupCallViewModel
import org.thoughtcrime.securesms.conversation.v2.groups.ConversationGroupViewModel
import org.thoughtcrime.securesms.conversation.v2.items.ChatColorsDrawable
@ -1040,7 +1042,13 @@ class ConversationFragment :
getVoiceNoteMediaController().voiceNotePlaybackState.observe(viewLifecycleOwner, inputPanel.playbackStateObserver)
val conversationUpdateTick = ConversationUpdateTick { adapter.updateTimestamps() }
val conversationUpdateTick = ConversationUpdateTick {
disposables += ConversationMessageComputeWorkers.recomputeFormattedDate(
requireContext(),
adapter.currentList.filterIsInstance<ConversationMessageElement>()
).observeOn(AndroidSchedulers.mainThread()).subscribeBy { adapter.updateTimestamps() }
}
viewLifecycleOwner.lifecycle.addObserver(conversationUpdateTick)
if (args.conversationScreenType.isInPopup) {

View file

@ -0,0 +1,40 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.conversation.v2.computed
import android.content.Context
import io.reactivex.rxjava3.core.Single
import io.reactivex.rxjava3.schedulers.Schedulers
import org.signal.core.util.ThreadUtil
import org.signal.core.util.concurrent.SignalExecutors
import org.thoughtcrime.securesms.conversation.ConversationMessage
import org.thoughtcrime.securesms.conversation.v2.data.ConversationMessageElement
/**
* Collection of workers to recompute computed ConversationMessage fields.
*/
object ConversationMessageComputeWorkers {
private val executor = SignalExecutors.newCachedSingleThreadExecutor("conversation-message-compute", ThreadUtil.PRIORITY_IMPORTANT_BACKGROUND_THREAD)
fun recomputeFormattedDate(
context: Context,
items: List<ConversationMessageElement>
): Single<Boolean> {
return Single.fromCallable {
var hasUpdatedProperties = false
for (item in items) {
val oldDate = item.conversationMessage.computedProperties.formattedDate
if (oldDate.isRelative) {
val newDate = ConversationMessage.getFormattedDate(context, item.conversationMessage.messageRecord)
item.conversationMessage.computedProperties.formattedDate = newDate
hasUpdatedProperties = true
}
}
hasUpdatedProperties
}.subscribeOn(Schedulers.from(executor))
}
}

View file

@ -0,0 +1,11 @@
/*
* Copyright 2023 Signal Messenger, LLC
* SPDX-License-Identifier: AGPL-3.0-only
*/
package org.thoughtcrime.securesms.conversation.v2.computed
data class FormattedDate(
val isRelative: Boolean,
val value: String
)

View file

@ -98,6 +98,7 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
override val badgeImageView: View? = binding.senderBadge
private var reactionMeasureListener: ReactionMeasureListener = ReactionMeasureListener()
private var dateString: String = ""
private val bodyBubbleDrawable = ChatColorsDrawable()
private val footerDrawable = ChatColorsDrawable()
@ -196,7 +197,9 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
)
if (ConversationAdapterBridge.PAYLOAD_TIMESTAMP in payload) {
presentDate()
if (conversationMessage.computedProperties.formattedDate.value != dateString) {
presentDate()
}
hasProcessedSupportedPayload = true
}
@ -605,6 +608,8 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
return
}
dateString = conversationMessage.computedProperties.formattedDate.value
binding.footerDate.setOnClickListener(null)
binding.footerDate.visible = true
binding.footerDate.setTextColor(themeDelegate.getFooterTextColor(conversationMessage))
@ -623,9 +628,9 @@ open class V2ConversationItemTextOnlyViewHolder<Model : MappingModel<Model>>(
} else if (record.isRateLimited) {
binding.footerDate.setText(R.string.ConversationItem_send_paused)
} else if (record.isScheduled()) {
binding.footerDate.text = conversationMessage.formattedDate
binding.footerDate.text = conversationMessage.computedProperties.formattedDate.value
} else {
var date = conversationMessage.formattedDate
var date = dateString
if (conversationContext.displayMode != ConversationItemDisplayMode.Detailed && record is MediaMmsMessageRecord && record.isEditMessage()) {
date = getContext().getString(R.string.ConversationItem_edited_timestamp_footer, date)

View file

@ -22,6 +22,7 @@ import android.os.Build
import android.text.format.DateFormat
import org.signal.core.util.logging.Log
import org.thoughtcrime.securesms.R
import org.thoughtcrime.securesms.conversation.v2.computed.FormattedDate
import java.text.DateFormatSymbols
import java.text.ParseException
import java.text.SimpleDateFormat
@ -124,16 +125,21 @@ object DateUtils : android.text.format.DateUtils() {
*/
@JvmStatic
fun getDatelessRelativeTimeSpanString(context: Context, locale: Locale, timestamp: Long): String {
return getDatelessRelativeTimeSpanFormattedDate(context, locale, timestamp).value
}
@JvmStatic
fun getDatelessRelativeTimeSpanFormattedDate(context: Context, locale: Locale, timestamp: Long): FormattedDate {
return when {
timestamp.isWithin(1.minutes) -> {
context.getString(R.string.DateUtils_just_now)
FormattedDate(true, context.getString(R.string.DateUtils_just_now))
}
timestamp.isWithin(1.hours) -> {
val minutes = timestamp.convertDeltaTo(DurationUnit.MINUTES)
context.resources.getString(R.string.DateUtils_minutes_ago, minutes)
FormattedDate(true, context.resources.getString(R.string.DateUtils_minutes_ago, minutes))
}
else -> {
getOnlyTimeString(context, locale, timestamp)
FormattedDate(false, getOnlyTimeString(context, locale, timestamp))
}
}
}