Fix auto-update timestamps.
This commit is contained in:
parent
1c55ad21a3
commit
4bf8e2c488
6 changed files with 113 additions and 22 deletions
|
@ -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));
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
|
@ -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
|
||||
)
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue