diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java index 8ac4697793..1dc9879ac6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationDataSource.java @@ -22,17 +22,23 @@ import org.thoughtcrime.securesms.database.model.MessageId; import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.ReactionRecord; import org.thoughtcrime.securesms.database.model.SmsMessageRecord; +import org.thoughtcrime.securesms.database.model.UpdateDescription; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; +import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Stopwatch; import org.thoughtcrime.securesms.util.Util; +import org.whispersystems.signalservice.api.push.ServiceId; import java.util.ArrayList; import java.util.Collection; import java.util.Collections; import java.util.HashMap; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.stream.Collectors; /** @@ -90,6 +96,7 @@ class ConversationDataSource implements PagedDataSource referencedIds = new HashSet<>(); try (MmsSmsDatabase.Reader reader = MmsSmsDatabase.readerFor(db.getConversation(threadId, start, length))) { MessageRecord record; @@ -98,6 +105,11 @@ class ConversationDataSource implements PagedDataSource messages = Stream.of(records) .map(m -> ConversationMessageFactory.createWithUnresolvedData(context, m, mentionHelper.getMentions(m.getId()))) .toList(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java index 6059bb6d0e..e50e14d0aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationUpdateItem.java @@ -73,13 +73,12 @@ public final class ConversationUpdateItem extends FrameLayout private Stub donateButtonStub; private View background; private ConversationMessage conversationMessage; - private Recipient conversationRecipient; - private Optional nextMessageRecord; - private MessageRecord messageRecord; + private Recipient conversationRecipient; + private Optional nextMessageRecord; + private MessageRecord messageRecord; private boolean isMessageRequestAccepted; private LiveData displayBody; private EventListener eventListener; - private boolean hasWallpaper; private final UpdateObserver updateObserver = new UpdateObserver(); @@ -146,7 +145,6 @@ public final class ConversationUpdateItem extends FrameLayout boolean hasWallpaper, boolean isMessageRequestAccepted) { - this.hasWallpaper = hasWallpaper; this.conversationMessage = conversationMessage; this.messageRecord = conversationMessage.getMessageRecord(); this.nextMessageRecord = nextMessageRecord; diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java index cc832f0c33..fbebda87b2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListDataSource.java @@ -14,15 +14,22 @@ import org.signal.paging.PagedDataSource; import org.thoughtcrime.securesms.conversationlist.model.Conversation; import org.thoughtcrime.securesms.conversationlist.model.ConversationReader; import org.thoughtcrime.securesms.database.SignalDatabase; +import org.thoughtcrime.securesms.database.SmsDatabase; import org.thoughtcrime.securesms.database.ThreadDatabase; +import org.thoughtcrime.securesms.database.model.MessageRecord; import org.thoughtcrime.securesms.database.model.ThreadRecord; +import org.thoughtcrime.securesms.database.model.UpdateDescription; import org.thoughtcrime.securesms.dependencies.ApplicationDependencies; import org.thoughtcrime.securesms.recipients.Recipient; +import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.Stopwatch; import java.util.ArrayList; +import java.util.HashSet; import java.util.LinkedList; import java.util.List; +import java.util.Set; +import java.util.stream.Collectors; abstract class ConversationListDataSource implements PagedDataSource { @@ -53,22 +60,32 @@ abstract class ConversationListDataSource implements PagedDataSource conversations = new ArrayList<>(length); - List recipients = new LinkedList<>(); + List recipients = new LinkedList<>(); + Set needsResolve = new HashSet<>(); try (ConversationReader reader = new ConversationReader(getCursor(start, length))) { ThreadRecord record; while ((record = reader.getNext()) != null && !cancellationSignal.isCanceled()) { conversations.add(new Conversation(record)); recipients.add(record.getRecipient()); + + if (!record.getRecipient().isPushV2Group()) { + needsResolve.add(record.getRecipient().getId()); + } else if (SmsDatabase.Types.isGroupUpdate(record.getType())) { + UpdateDescription description = MessageRecord.getGv2ChangeDescription(ApplicationDependencies.getApplication(), record.getBody()); + needsResolve.addAll(description.getMentioned().stream().map(sid -> RecipientId.from(sid, null)).collect(Collectors.toList())); + } } } stopwatch.split("cursor"); ApplicationDependencies.getRecipientCache().addToCache(recipients); - stopwatch.split("cache-recipients"); + Recipient.resolvedList(needsResolve); + stopwatch.split("recipient-resolve"); + stopwatch.stop(TAG); return conversations; diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java index 42bdc4f627..aca0e9d064 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/GroupsV2UpdateMessageProducer.java @@ -3,6 +3,7 @@ package org.thoughtcrime.securesms.database.model; import android.content.Context; import androidx.annotation.DrawableRes; +import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.WorkerThread; @@ -763,11 +764,9 @@ final class GroupsV2UpdateMessageProducer { interface DescribeMemberStrategy { /** - * Map an ACI to a string that describes the group member. - * @param serviceId + * Map a ServiceId to a string that describes the group member. */ @NonNull - @WorkerThread String describe(@NonNull ServiceId serviceId); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java index 990d369a57..dc0e02d730 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/LiveUpdateMessage.java @@ -12,8 +12,10 @@ import android.text.SpannableStringBuilder; import androidx.annotation.AnyThread; import androidx.annotation.ColorInt; +import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.lifecycle.LiveData; +import androidx.lifecycle.Transformations; import com.annimon.stream.Stream; @@ -34,11 +36,12 @@ public final class LiveUpdateMessage { * Creates a live data that observes the recipients mentioned in the {@link UpdateDescription} and * recreates the string asynchronously when they change. */ - @AnyThread + @MainThread public static LiveData fromMessageDescription(@NonNull Context context, @NonNull UpdateDescription updateDescription, @ColorInt int defaultTint, - boolean adjustPosition) { + boolean adjustPosition) + { if (updateDescription.isStringStatic()) { return LiveDataUtil.just(toSpannable(context, updateDescription, updateDescription.getStaticString(), defaultTint, adjustPosition)); } @@ -50,16 +53,17 @@ public final class LiveUpdateMessage { LiveData mentionedRecipientChangeStream = allMentionedRecipients.isEmpty() ? LiveDataUtil.just(new Object()) : LiveDataUtil.merge(allMentionedRecipients); - return LiveDataUtil.mapAsync(mentionedRecipientChangeStream, event -> toSpannable(context, updateDescription, updateDescription.getString(), defaultTint, adjustPosition)); + return Transformations.map(mentionedRecipientChangeStream, event -> toSpannable(context, updateDescription, updateDescription.getString(), defaultTint, adjustPosition)); } /** * Observes a single recipient and recreates the string asynchronously when they change. */ + @MainThread public static LiveData recipientToStringAsync(@NonNull RecipientId recipientId, @NonNull Function createStringInBackground) { - return LiveDataUtil.mapAsync(Recipient.live(recipientId).getLiveDataResolved(), createStringInBackground); + return Transformations.map(Recipient.live(recipientId).getLiveDataResolved(), createStringInBackground::apply); } private static @NonNull SpannableString toSpannable(@NonNull Context context, @NonNull UpdateDescription updateDescription, @NonNull String string, @ColorInt int defaultTint, boolean adjustPosition) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java index 29f64033fc..7a96feefcd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java +++ b/app/src/main/java/org/thoughtcrime/securesms/database/model/UpdateDescription.java @@ -22,7 +22,6 @@ import java.util.Set; public final class UpdateDescription { public interface StringFactory { - @WorkerThread String create(); } @@ -109,14 +108,12 @@ public final class UpdateDescription { return staticString; } - ThreadUtil.assertNotMainThread(); - //noinspection ConstantConditions return stringFactory.create(); } @AnyThread - public Collection getMentioned() { + public @NonNull Collection getMentioned() { return mentioned; } diff --git a/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java b/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java index bfcfc1a4aa..07c654a74f 100644 --- a/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java +++ b/app/src/test/java/org/thoughtcrime/securesms/database/model/UpdateDescriptionTest.java @@ -1,13 +1,6 @@ package org.thoughtcrime.securesms.database.model; -import org.junit.Before; -import org.junit.Rule; import org.junit.Test; -import org.mockito.Mock; -import org.mockito.MockedStatic; -import org.mockito.junit.MockitoJUnit; -import org.mockito.junit.MockitoRule; -import org.signal.core.util.ThreadUtil; import org.whispersystems.signalservice.api.push.ServiceId; import java.util.Arrays; @@ -22,19 +15,6 @@ import static org.junit.Assert.assertTrue; public final class UpdateDescriptionTest { - @Rule - public MockitoRule rule = MockitoJUnit.rule(); - - @Mock - private MockedStatic threadUtilMockedStatic; - - @Before - public void setup() { - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(true); - threadUtilMockedStatic.when(ThreadUtil::assertMainThread).thenCallRealMethod(); - threadUtilMockedStatic.when(ThreadUtil::assertNotMainThread).thenCallRealMethod(); - } - @Test public void staticDescription_byGetStaticString() { UpdateDescription description = UpdateDescription.staticDescription("update", 0); @@ -56,15 +36,6 @@ public final class UpdateDescriptionTest { assertEquals("update", description.getString()); } - @Test(expected = AssertionError.class) - public void stringFactory_cannot_run_on_main_thread() { - UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), () -> "update", 0); - - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(true); - - description.getString(); - } - @Test(expected = UnsupportedOperationException.class) public void stringFactory_cannot_call_static_string() { UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), () -> "update", 0); @@ -85,8 +56,6 @@ public final class UpdateDescriptionTest { assertEquals(0, factoryCalls.get()); - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false); - String string = description.getString(); assertEquals("update", string); @@ -99,8 +68,6 @@ public final class UpdateDescriptionTest { UpdateDescription.StringFactory stringFactory = () -> "call" + factoryCalls.incrementAndGet(); UpdateDescription description = UpdateDescription.mentioning(Collections.singletonList(ServiceId.from(UUID.randomUUID())), stringFactory, 0); - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false); - assertEquals("call1", description.getString()); assertEquals("call2", description.getString()); assertEquals("call3", description.getString()); @@ -143,8 +110,6 @@ public final class UpdateDescriptionTest { assertFalse(description.isStringStatic()); - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false); - assertEquals("update.11\nupdate.21", description.getString()); assertEquals("update.12\nupdate.22", description.getString()); assertEquals("update.13\nupdate.23", description.getString()); @@ -167,8 +132,6 @@ public final class UpdateDescriptionTest { assertFalse(description.isStringStatic()); - threadUtilMockedStatic.when(ThreadUtil::isMainThread).thenReturn(false); - assertEquals("update.101\nstatic\nupdate.201", description.getString()); assertEquals("update.102\nstatic\nupdate.202", description.getString()); assertEquals("update.103\nstatic\nupdate.203", description.getString());