From 8f253ffc4340f90f5d3ad34ec50fc8b8fbc67abf Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Wed, 12 Jul 2023 15:33:34 -0300 Subject: [PATCH] Add lazy thread creation throughout in preparation for CFV2. --- .../thoughtcrime/securesms/MainNavigator.java | 30 ++++++++---- .../securesms/NewConversationActivity.java | 37 ++++++++------ .../securesms/SmsSendtoActivity.java | 4 +- .../flow/GiftFlowConfirmationFragment.kt | 12 +++-- .../securesms/calls/log/CallLogContextMenu.kt | 10 +++- .../ConversationSettingsFragment.kt | 16 +++--- .../voice/VoiceNoteNotificationManager.java | 2 +- .../conversation/ConversationFragment.java | 2 +- .../conversation/ConversationIntents.java | 49 ++++++++++++++++--- .../ConversationParentFragment.java | 2 +- .../details/AddGroupDetailsActivity.java | 2 +- .../GroupJoinBottomSheetDialogFragment.java | 2 +- .../mediapreview/MediaPreviewRepository.kt | 2 +- .../v2/NotificationConversation.kt | 2 +- .../notifications/v2/NotificationFactory.kt | 4 +- .../PaymentRecipientSelectionFragment.java | 4 +- .../service/ScheduledMessageManager.kt | 2 +- .../securesms/sharing/v2/ShareActivity.kt | 30 +++++++----- .../stories/landing/StoriesLandingFragment.kt | 8 ++- .../group/GroupStorySettingsFragment.kt | 7 ++- .../viewer/page/StoryViewerPageFragment.kt | 5 +- .../viewer/views/StoryViewsFragment.kt | 14 ++++-- .../securesms/util/CommunicationActions.java | 8 +-- .../securesms/util/ConversationUtil.java | 4 +- 24 files changed, 176 insertions(+), 82 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java index 9feb2c7f3a..fea48a4a8c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java +++ b/app/src/main/java/org/thoughtcrime/securesms/MainNavigator.java @@ -7,20 +7,27 @@ import androidx.annotation.NonNull; import androidx.fragment.app.Fragment; import androidx.fragment.app.FragmentManager; +import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.components.settings.app.AppSettingsActivity; import org.thoughtcrime.securesms.conversation.ConversationIntents; import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity; import org.thoughtcrime.securesms.insights.InsightsLauncher; import org.thoughtcrime.securesms.recipients.RecipientId; +import io.reactivex.rxjava3.disposables.Disposable; + public class MainNavigator { public static final int REQUEST_CONFIG_CHANGES = 901; - private final MainActivity activity; + private final MainActivity activity; + private final LifecycleDisposable lifecycleDisposable; public MainNavigator(@NonNull MainActivity activity) { - this.activity = activity; + this.activity = activity; + this.lifecycleDisposable = new LifecycleDisposable(); + + lifecycleDisposable.bindTo(activity); } public static MainNavigator get(@NonNull Activity activity) { @@ -33,7 +40,7 @@ public class MainNavigator { /** * @return True if the back pressed was handled in our own custom way, false if it should be given - * to the system to do the default behavior. + * to the system to do the default behavior. */ public boolean onBackPressed() { Fragment fragment = getFragmentManager().findFragmentById(R.id.fragment_container); @@ -46,13 +53,16 @@ public class MainNavigator { } public void goToConversation(@NonNull RecipientId recipientId, long threadId, int distributionType, int startingPosition) { - Intent intent = ConversationIntents.createBuilder(activity, recipientId, threadId) - .withDistributionType(distributionType) - .withStartingPosition(startingPosition) - .build(); + Disposable disposable = ConversationIntents.createBuilder(activity, recipientId, threadId) + .map(builder -> builder.withDistributionType(distributionType) + .withStartingPosition(startingPosition) + .build()) + .subscribe(intent -> { + activity.startActivity(intent); + activity.overridePendingTransition(R.anim.slide_from_end, R.anim.fade_scale_out); + }); - activity.startActivity(intent); - activity.overridePendingTransition(R.anim.slide_from_end, R.anim.fade_scale_out); + lifecycleDisposable.add(disposable); } public void goToAppSettings() { @@ -79,7 +89,7 @@ public class MainNavigator { public interface BackHandler { /** * @return True if the back pressed was handled in our own custom way, false if it should be given - * to the system to do the default behavior. + * to the system to do the default behavior. */ boolean onBackPressed(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java index af6ba683c3..e787c57729 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/NewConversationActivity.java @@ -36,6 +36,7 @@ import com.google.android.material.dialog.MaterialAlertDialogBuilder; import com.google.android.material.snackbar.Snackbar; import org.signal.core.util.DimensionUnit; +import org.signal.core.util.concurrent.LifecycleDisposable; import org.signal.core.util.concurrent.SimpleTask; import org.signal.core.util.logging.Log; import org.thoughtcrime.securesms.components.menu.ActionItem; @@ -45,14 +46,12 @@ import org.thoughtcrime.securesms.contacts.management.ContactsManagementViewMode import org.thoughtcrime.securesms.contacts.paged.ContactSearchKey; import org.thoughtcrime.securesms.contacts.sync.ContactDiscovery; import org.thoughtcrime.securesms.conversation.ConversationIntents; -import org.thoughtcrime.securesms.database.SignalDatabase; import org.thoughtcrime.securesms.groups.ui.creategroup.CreateGroupActivity; import org.thoughtcrime.securesms.keyvalue.SignalStore; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.recipients.RecipientId; import org.thoughtcrime.securesms.util.CommunicationActions; import org.thoughtcrime.securesms.util.FeatureFlags; -import org.signal.core.util.concurrent.LifecycleDisposable; import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; import java.io.IOException; @@ -64,6 +63,8 @@ import java.util.function.Consumer; import java.util.stream.Collectors; import java.util.stream.Stream; +import io.reactivex.rxjava3.disposables.Disposable; + /** * Activity container for starting a new conversation. * @@ -163,15 +164,18 @@ public class NewConversationActivity extends ContactSelectionActivity } private void launch(Recipient recipient) { - long existingThread = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId()); - Intent intent = ConversationIntents.createBuilder(this, recipient.getId(), existingThread) - .withDraftText(getIntent().getStringExtra(Intent.EXTRA_TEXT)) - .withDataUri(getIntent().getData()) - .withDataType(getIntent().getType()) - .build(); + Disposable disposable = ConversationIntents.createBuilder(this, recipient.getId(), -1L) + .map(builder -> builder + .withDraftText(getIntent().getStringExtra(Intent.EXTRA_TEXT)) + .withDataUri(getIntent().getData()) + .withDataType(getIntent().getType()) + .build()) + .subscribe(intent -> { + startActivity(intent); + finish(); + }); - startActivity(intent); - finish(); + disposables.add(disposable); } @Override @@ -233,8 +237,8 @@ public class NewConversationActivity extends ContactSelectionActivity @Override public boolean onLongClick(View anchorView, ContactSearchKey contactSearchKey, RecyclerView recyclerView) { - RecipientId recipientId = contactSearchKey.requireRecipientSearchKey().getRecipientId(); - List actions = generateContextualActionsForRecipient(recipientId); + RecipientId recipientId = contactSearchKey.requireRecipientSearchKey().getRecipientId(); + List actions = generateContextualActionsForRecipient(recipientId); if (actions.isEmpty()) { return false; } @@ -269,7 +273,12 @@ public class NewConversationActivity extends ContactSelectionActivity R.drawable.ic_chat_message_24, getString(R.string.NewConversationActivity__message), R.color.signal_colorOnSurface, - () -> startActivity(ConversationIntents.createBuilder(this, recipient.getId(), -1L).build()) + () -> { + Disposable disposable = ConversationIntents.createBuilder(this, recipient.getId(), -1L) + .subscribe(builder -> startActivity(builder.build())); + + disposables.add(disposable); + } ); } @@ -370,7 +379,7 @@ public class NewConversationActivity extends ContactSelectionActivity .show(); } - private void displaySnackbar(@StringRes int message, Object ... formatArgs) { + private void displaySnackbar(@StringRes int message, Object... formatArgs) { Snackbar.make(findViewById(android.R.id.content), getString(message, formatArgs), Snackbar.LENGTH_SHORT).show(); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java b/app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java index b472b3a49d..b349b7c764 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/SmsSendtoActivity.java @@ -48,9 +48,9 @@ public class SmsSendtoActivity extends Activity { Toast.makeText(this, R.string.ConversationActivity_specify_recipient, Toast.LENGTH_LONG).show(); } else { Recipient recipient = Recipient.external(this, destination.getDestination()); - long threadId = SignalDatabase.threads().getThreadIdIfExistsFor(recipient.getId()); + long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(recipient); - nextIntent = ConversationIntents.createBuilder(this, recipient.getId(), threadId) + nextIntent = ConversationIntents.createBuilderSync(this, recipient.getId(), threadId) .withDraftText(destination.getBody()) .build(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowConfirmationFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowConfirmationFragment.kt index ba6afa0e29..58991304ed 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowConfirmationFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/badges/gifts/flow/GiftFlowConfirmationFragment.kt @@ -264,12 +264,14 @@ class GiftFlowConfirmationFragment : override fun onPaymentComplete(gatewayRequest: GatewayRequest) { val mainActivityIntent = MainActivity.clearTop(requireContext()) - val conversationIntent = ConversationIntents - .createBuilder(requireContext(), viewModel.snapshot.recipient!!.id, -1L) - .withGiftBadge(viewModel.snapshot.giftBadge!!) - .build() - requireActivity().startActivities(arrayOf(mainActivityIntent, conversationIntent)) + lifecycleDisposable += ConversationIntents + .createBuilder(requireContext(), viewModel.snapshot.recipient!!.id, -1L) + .subscribe { conversationIntent -> + requireActivity().startActivities( + arrayOf(mainActivityIntent, conversationIntent.withGiftBadge(viewModel.snapshot.giftBadge!!).build()) + ) + } } override fun onProcessorActionProcessed() = Unit diff --git a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogContextMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogContextMenu.kt index 4516144017..abbba4181c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogContextMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/calls/log/CallLogContextMenu.kt @@ -4,6 +4,8 @@ import android.view.View import android.view.ViewGroup import androidx.fragment.app.Fragment import androidx.recyclerview.widget.RecyclerView +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.signal.core.util.concurrent.LifecycleDisposable import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.calls.links.details.CallLinkDetailsActivity import org.thoughtcrime.securesms.components.menu.ActionItem @@ -21,6 +23,9 @@ class CallLogContextMenu( private val fragment: Fragment, private val callbacks: Callbacks ) { + + private val lifecycleDisposable by lazy { LifecycleDisposable().bindTo(fragment.viewLifecycleOwner) } + fun show(recyclerView: RecyclerView, anchor: View, call: CallLogRow.Call) { recyclerView.suppressLayout(true) anchor.isSelected = true @@ -91,7 +96,10 @@ class CallLogContextMenu( iconRes = R.drawable.symbol_open_24, title = fragment.getString(R.string.CallContextMenu__go_to_chat) ) { - fragment.startActivity(ConversationIntents.createBuilder(fragment.requireContext(), call.peer.id, -1L).build()) + lifecycleDisposable += ConversationIntents.createBuilder(fragment.requireContext(), call.peer.id, -1L) + .subscribeBy { + fragment.startActivity(it.build()) + } } } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt index b78b828666..b63ffd4c9a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/components/settings/conversation/ConversationSettingsFragment.kt @@ -26,6 +26,7 @@ import app.cash.exhaustive.Exhaustive import com.google.android.flexbox.FlexboxLayoutManager import com.google.android.material.dialog.MaterialAlertDialogBuilder import com.google.android.material.snackbar.Snackbar +import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.DimensionUnit import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.getParcelableArrayListExtraCompat @@ -387,7 +388,7 @@ class ConversationSettingsFragment : DSLSettingsFragment( enabled = !state.isDeprecatedOrUnregistered, onMessageClick = { val intent = ConversationIntents - .createBuilder(requireContext(), state.recipient.id, state.threadId) + .createBuilderSync(requireContext(), state.recipient.id, state.threadId) .build() startActivity(intent) @@ -432,12 +433,15 @@ class ConversationSettingsFragment : DSLSettingsFragment( } }, onSearchClick = { - val intent = ConversationIntents.createBuilder(requireContext(), state.recipient.id, state.threadId) - .withSearchOpen(true) - .build() + lifecycleDisposable += ConversationIntents.createBuilder(requireContext(), state.recipient.id, state.threadId) + .subscribeBy { builder -> + val intent = builder + .withSearchOpen(true) + .build() - startActivity(intent) - requireActivity().finish() + startActivity(intent) + requireActivity().finish() + } } ) ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteNotificationManager.java b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteNotificationManager.java index 9f46843b23..d9189b827a 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteNotificationManager.java +++ b/app/src/main/java/org/thoughtcrime/securesms/components/voice/VoiceNoteNotificationManager.java @@ -99,7 +99,7 @@ class VoiceNoteNotificationManager { notificationManager.setColor(color); - Intent conversationActivity = ConversationIntents.createBuilder(context, recipientId, threadId) + Intent conversationActivity = ConversationIntents.createBuilderSync(context, recipientId, threadId) .withStartingPosition(startingPosition) .build(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java index 51841fdb6f..72122c9f2f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationFragment.java @@ -2050,7 +2050,7 @@ public class ConversationFragment extends LoggingFragment implements Multiselect @Override public void goToMediaPreview(ConversationItem parent, View sharedElement, MediaIntentFactory.MediaPreviewArgs args) { if (listener.isInBubble()) { - Intent intent = ConversationIntents.createBuilder(requireActivity(), recipient.getId(), threadId) + Intent intent = ConversationIntents.createBuilderSync(requireActivity(), recipient.getId(), threadId) .withStartingPosition(list.getChildAdapterPosition(parent)) .build(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java index ba22a6fef7..2501799674 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationIntents.java @@ -6,6 +6,7 @@ import android.content.Intent; import android.net.Uri; import android.os.Bundle; +import androidx.annotation.MainThread; import androidx.annotation.NonNull; import androidx.annotation.Nullable; @@ -28,6 +29,10 @@ import java.util.Collection; import java.util.List; import java.util.Objects; +import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers; +import io.reactivex.rxjava3.core.Single; +import io.reactivex.rxjava3.schedulers.Schedulers; + public class ConversationIntents { private static final String TAG = Log.tag(ConversationIntents.class); @@ -52,8 +57,26 @@ public class ConversationIntents { private ConversationIntents() { } - public static @NonNull Builder createBuilder(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { - return new Builder(context, recipientId, threadId); + /** + * Create a conversation builder for the given recipientId / threadId. Thread ids are required for CFV2, + * so we will resolve the Recipient into a ThreadId if the threadId is invalid (below 0) + * + * @param context Context for Intent creation + * @param recipientId The RecipientId to query the thread ID for if the passed one is invalid. + * @param threadId The threadId, or -1L + * + * @return A Single that will return a builder to create the conversation intent. + */ + @MainThread + public static @NonNull Single createBuilder(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { + if (threadId > 0L) { + return Single.just(createBuilderSync(context, recipientId, threadId)); + } else { + return Single.fromCallable(() -> { + long newThreadId = SignalDatabase.threads().getOrCreateThreadIdFor(Recipient.resolved(recipientId)); + return createBuilderSync(context, recipientId, newThreadId); + }).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()); + } } public static @NonNull Builder createPopUpBuilder(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { @@ -64,6 +87,20 @@ public class ConversationIntents { return new Builder(context, BubbleConversationActivity.class, recipientId, threadId).build(); } + /** + * Create a Builder for a Conversation Intent. Does not perform a lookup for the thread id if the thread id is < 1. For CFV2, this is + * considered an invalid state and will be met with an IllegalArgumentException. + * + * @param context Context for Intent creation + * @param recipientId The recipientId, only used if the threadId is not valid + * @param threadId The threadId, required for CFV2. + * + * @return A builder that can be used to create a conversation intent. + */ + public static @NonNull Builder createBuilderSync(@NonNull Context context, @NonNull RecipientId recipientId, long threadId) { + return new Builder(context, recipientId, threadId); + } + static boolean isInvalid(@NonNull Bundle arguments) { Uri uri = getIntentData(arguments); if (isBubbleIntentUri(uri)) { @@ -308,7 +345,7 @@ public class ConversationIntents { this.context = context; this.conversationActivityClass = conversationActivityClass; this.recipientId = recipientId; - this.threadId = resolveThreadId(recipientId, threadId); + this.threadId = checkThreadId(threadId); this.conversationScreenType = ConversationScreenType.fromActivityClass(conversationActivityClass); } @@ -475,11 +512,9 @@ public class ConversationIntents { } } - private static long resolveThreadId(@NonNull RecipientId recipientId, long threadId) { + private static long checkThreadId(long threadId) { if (threadId < 0 && SignalStore.internalValues().useConversationFragmentV2()) { - Log.w(TAG, "Getting thread id from database..."); - // TODO [alex] -- Yes, this hits the database. No, we shouldn't be doing this. - return SignalDatabase.threads().getOrCreateThreadIdFor(Recipient.resolved(recipientId)); + throw new IllegalArgumentException("ThreadId is a required field in CFV2"); } else { return threadId; } diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java index e5f1c59e9d..0eb27731ec 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java @@ -4451,7 +4451,7 @@ public class ConversationParentFragment extends Fragment @Override public void onNavigateToMessage(long threadId, @NonNull RecipientId threadRecipientId, @NonNull RecipientId senderId, long messageTimestamp, long messagePositionInThread) { if (threadId != ConversationParentFragment.this.threadId) { - startActivity(ConversationIntents.createBuilder(requireActivity(), threadRecipientId, threadId) + startActivity(ConversationIntents.createBuilderSync(requireActivity(), threadRecipientId, threadId) .withStartingPosition((int) messagePositionInThread) .build()); } else { diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/details/AddGroupDetailsActivity.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/details/AddGroupDetailsActivity.java index 82828e83ac..ac78d491de 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/details/AddGroupDetailsActivity.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/creategroup/details/AddGroupDetailsActivity.java @@ -73,7 +73,7 @@ public class AddGroupDetailsActivity extends PassphraseRequiredActivity implemen } void goToConversation(@NonNull RecipientId recipientId, long threadId) { - Intent intent = ConversationIntents.createBuilder(this, recipientId, threadId) + Intent intent = ConversationIntents.createBuilderSync(this, recipientId, threadId) .firstTimeInSelfCreatedGroup() .build(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java index 4590d0bd4c..ff23ee3c69 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/groups/ui/invitesandrequests/joining/GroupJoinBottomSheetDialogFragment.java @@ -132,7 +132,7 @@ public final class GroupJoinBottomSheetDialogFragment extends BottomSheetDialogF viewModel.getJoinSuccess().observe(getViewLifecycleOwner(), joinGroupSuccess -> { Log.i(TAG, "Group joined, navigating to group"); - Intent intent = ConversationIntents.createBuilder(requireContext(), joinGroupSuccess.getGroupRecipient().getId(), joinGroupSuccess.getGroupThreadId()) + Intent intent = ConversationIntents.createBuilderSync(requireContext(), joinGroupSuccess.getGroupRecipient().getId(), joinGroupSuccess.getGroupThreadId()) .build(); requireActivity().startActivity(intent); diff --git a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt index 908210b1bb..7278b1c556 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/mediapreview/MediaPreviewRepository.kt @@ -110,7 +110,7 @@ class MediaPreviewRepository { stopwatch.split("get recipient ID") stopwatch.stop(TAG) - ConversationIntents.createBuilder(context, recipientId, threadId) + ConversationIntents.createBuilderSync(context, recipientId, threadId) .withStartingPosition(messagePosition) .build() } diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt index 70ab472732..9d59b2e08e 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationConversation.kt @@ -128,7 +128,7 @@ data class NotificationConversation( ) ) } else { - ConversationIntents.createBuilder(context, recipient.id, thread.threadId) + ConversationIntents.createBuilderSync(context, recipient.id, thread.threadId) .withStartingPosition(mostRecentNotification.getStartingPosition(context)) .build() }.makeUniqueToPreventMerging() diff --git a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationFactory.kt b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationFactory.kt index eda2ecb098..6c5354dc6b 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationFactory.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/notifications/v2/NotificationFactory.kt @@ -349,7 +349,7 @@ object NotificationFactory { val intent: Intent = if (recipient.isDistributionList || thread.groupStoryId != null) { Intent(context, MyStoriesActivity::class.java) } else { - ConversationIntents.createBuilder(context, recipient.id, thread.threadId) + ConversationIntents.createBuilderSync(context, recipient.id, thread.threadId) .build() }.makeUniqueToPreventMerging() @@ -419,7 +419,7 @@ object NotificationFactory { val intent: Intent = if (recipient.isDistributionList || thread.groupStoryId != null) { Intent(context, MyStoriesActivity::class.java) } else { - ConversationIntents.createBuilder(context, recipient.id, thread.threadId) + ConversationIntents.createBuilderSync(context, recipient.id, thread.threadId) .build() }.makeUniqueToPreventMerging() diff --git a/app/src/main/java/org/thoughtcrime/securesms/payments/preferences/PaymentRecipientSelectionFragment.java b/app/src/main/java/org/thoughtcrime/securesms/payments/preferences/PaymentRecipientSelectionFragment.java index fdd11261a0..c96f9f1f54 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/payments/preferences/PaymentRecipientSelectionFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/payments/preferences/PaymentRecipientSelectionFragment.java @@ -118,7 +118,7 @@ public class PaymentRecipientSelectionFragment extends LoggingFragment implement private void openConversation(@NonNull RecipientId recipientId) { SimpleTask.run(getViewLifecycleOwner().getLifecycle(), - () -> SignalDatabase.threads().getThreadIdIfExistsFor(recipientId), - threadId -> startActivity(ConversationIntents.createBuilder(requireContext(), recipientId, threadId).build())); + () -> SignalDatabase.threads().getOrCreateThreadIdFor(Recipient.resolved(recipientId)), + threadId -> startActivity(ConversationIntents.createBuilderSync(requireContext(), recipientId, threadId).build())); } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/service/ScheduledMessageManager.kt b/app/src/main/java/org/thoughtcrime/securesms/service/ScheduledMessageManager.kt index 5b291f1942..dc05d150a0 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/service/ScheduledMessageManager.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/service/ScheduledMessageManager.kt @@ -72,7 +72,7 @@ class ScheduledMessageManager( @WorkerThread override fun scheduleAlarm(application: Application, event: Event, delay: Long) { - val conversationIntent = ConversationIntents.createBuilder(application, event.recipientId, event.threadId).build() + val conversationIntent = ConversationIntents.createBuilderSync(application, event.recipientId, event.threadId).build() trySetExactAlarm( application, diff --git a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt index e7ba2433f1..09e383c5f4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/sharing/v2/ShareActivity.kt @@ -13,6 +13,7 @@ import androidx.activity.viewModels import androidx.core.content.ContextCompat import androidx.core.content.pm.ShortcutManagerCompat import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers +import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.Result import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.getParcelableArrayListCompat @@ -164,11 +165,13 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C Result.success(UnresolvedShareData.ExternalPrimitiveShare(stringBuilder)) } ?: Result.failure(IntentError.SEND_MULTIPLE_TEXT) } + intent.action == Intent.ACTION_SEND_MULTIPLE && intent.hasExtra(Intent.EXTRA_STREAM) -> { intent.getParcelableArrayListExtraCompat(Intent.EXTRA_STREAM, Uri::class.java)?.let { Result.success(UnresolvedShareData.ExternalMultiShare(it)) } ?: Result.failure(IntentError.SEND_MULTIPLE_STREAM) } + intent.action == Intent.ACTION_SEND && intent.hasExtra(Intent.EXTRA_STREAM) -> { val uri: Uri? = intent.getParcelableExtraCompat(Intent.EXTRA_STREAM, Uri::class.java) if (uri == null) { @@ -178,9 +181,11 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C Result.success(UnresolvedShareData.ExternalSingleShare(uri, intent.type, text)) } } + intent.action == Intent.ACTION_SEND && intent.hasExtra(Intent.EXTRA_TEXT) -> { extractSingleExtraTextFromIntent() } + else -> null } ?: Result.failure(IntentError.UNKNOWN) } @@ -221,18 +226,21 @@ class ShareActivity : PassphraseRequiredActivity(), MultiselectForwardFragment.C Log.d(TAG, "Opening conversation...") val multiShareArgs = shareEvent.getMultiShareArgs() - val conversationIntentBuilder = ConversationIntents.createBuilder(this, shareEvent.contact.recipientId, -1L) - .withDataUri(multiShareArgs.dataUri) - .withDataType(multiShareArgs.dataType) - .withMedia(multiShareArgs.media) - .withDraftText(multiShareArgs.draftText) - .withStickerLocator(multiShareArgs.stickerLocator) - .asBorderless(multiShareArgs.isBorderless) - .withShareDataTimestamp(System.currentTimeMillis()) + lifecycleDisposable += ConversationIntents.createBuilder(this, shareEvent.contact.recipientId, -1L) + .subscribeBy { conversationIntentBuilder -> + conversationIntentBuilder + .withDataUri(multiShareArgs.dataUri) + .withDataType(multiShareArgs.dataType) + .withMedia(multiShareArgs.media) + .withDraftText(multiShareArgs.draftText) + .withStickerLocator(multiShareArgs.stickerLocator) + .asBorderless(multiShareArgs.isBorderless) + .withShareDataTimestamp(System.currentTimeMillis()) - val mainActivityIntent = MainActivity.clearTop(this) - finish() - startActivities(arrayOf(mainActivityIntent, conversationIntentBuilder.build())) + val mainActivityIntent = MainActivity.clearTop(this) + finish() + startActivities(arrayOf(mainActivityIntent, conversationIntentBuilder.build())) + } } private fun openMediaInterstitial(shareEvent: ShareEvent.OpenMediaInterstitial) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/landing/StoriesLandingFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/landing/StoriesLandingFragment.kt index e715ee5adf..299afd7958 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/landing/StoriesLandingFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/landing/StoriesLandingFragment.kt @@ -183,6 +183,7 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l R.id.reminder_action_update_now -> { PlayStoreUtil.openPlayStoreOrOurApkDownloadPage(requireContext()) } + R.id.reminder_action_re_register -> { startActivity(RegistrationNavigationActivity.newIntentForReRegistration(requireContext())) } @@ -324,8 +325,11 @@ class StoriesLandingFragment : DSLSettingsFragment(layoutId = R.layout.stories_l MultiselectForwardFragment.showBottomSheet(childFragmentManager, args) } }, - onGoToChat = { - startActivityIfAble(ConversationIntents.createBuilder(requireContext(), it.data.storyRecipient.id, -1L).build()) + onGoToChat = { model -> + lifecycleDisposable += ConversationIntents.createBuilder(requireContext(), model.data.storyRecipient.id, -1L) + .subscribeBy { + startActivityIfAble(it.build()) + } }, onHideStory = { if (!it.data.isHidden) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/group/GroupStorySettingsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/group/GroupStorySettingsFragment.kt index 99c293a57b..3a0e2857a8 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/settings/group/GroupStorySettingsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/settings/group/GroupStorySettingsFragment.kt @@ -6,6 +6,7 @@ import androidx.appcompat.widget.Toolbar import androidx.core.content.ContextCompat import androidx.fragment.app.viewModels import androidx.navigation.fragment.findNavController +import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.dp import org.thoughtcrime.securesms.R @@ -45,8 +46,10 @@ class GroupStorySettingsFragment : DSLSettingsFragment(menuId = R.menu.story_gro iconRes = R.drawable.ic_open_24_tinted, title = getString(R.string.StoriesLandingItem__go_to_chat), action = { - lifecycleDisposable += viewModel.getConversationData().subscribe { data -> - startActivity(ConversationIntents.createBuilder(requireContext(), data.groupRecipientId, data.groupThreadId).build()) + lifecycleDisposable += viewModel.getConversationData().flatMap { data -> + ConversationIntents.createBuilder(requireContext(), data.groupRecipientId, data.groupThreadId) + }.subscribeBy { + startActivity(it.build()) } } ) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt index cc4e0b8d8d..176908048d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/page/StoryViewerPageFragment.kt @@ -34,6 +34,7 @@ import com.google.android.material.progressindicator.CircularProgressIndicatorSp import com.google.android.material.progressindicator.IndeterminateDrawable import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers import io.reactivex.rxjava3.core.Observable +import io.reactivex.rxjava3.kotlin.subscribeBy import org.signal.core.util.DimensionUnit import org.signal.core.util.concurrent.LifecycleDisposable import org.signal.core.util.dp @@ -1064,7 +1065,9 @@ class StoryViewerPageFragment : } }, onGoToChat = { - startActivity(ConversationIntents.createBuilder(requireContext(), storyViewerPageArgs.recipientId, -1L).build()) + lifecycleDisposable += ConversationIntents.createBuilder(requireContext(), storyViewerPageArgs.recipientId, -1L).subscribeBy { + startActivity(it.build()) + } }, onHide = { viewModel.setIsDisplayingHideDialog(true) diff --git a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt index 09500f5c46..f04c6bd4d4 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/stories/viewer/views/StoryViewsFragment.kt @@ -5,6 +5,8 @@ import android.view.View import androidx.fragment.app.Fragment import androidx.fragment.app.viewModels import com.google.android.material.dialog.MaterialAlertDialogBuilder +import io.reactivex.rxjava3.kotlin.subscribeBy +import org.signal.core.util.concurrent.LifecycleDisposable import org.thoughtcrime.securesms.R import org.thoughtcrime.securesms.components.settings.DSLConfiguration import org.thoughtcrime.securesms.components.settings.DSLSettingsFragment @@ -37,9 +39,13 @@ class StoryViewsFragment : private val storyId: Long get() = requireArguments().getLong(ARG_STORY_ID) + private val lifecycleDisposable = LifecycleDisposable() + override fun bindAdapter(adapter: MappingAdapter) { StoryViewItem.register(adapter) + lifecycleDisposable.bindTo(viewLifecycleOwner) + val emptyNotice: View = requireView().findViewById(R.id.empty_notice) val disabledNotice: View = requireView().findViewById(R.id.disabled_notice) val disabledButton: View = requireView().findViewById(R.id.disabled_button) @@ -74,9 +80,11 @@ class StoryViewsFragment : StoryViewItem.Model( storyViewItemData = storyViewItemData, canRemoveMember = state.storyRecipient?.isDistributionList ?: false, - goToChat = { - val chatIntent = ConversationIntents.createBuilder(requireContext(), it.storyViewItemData.recipient.id, -1L).build() - startActivity(chatIntent) + goToChat = { model -> + lifecycleDisposable += ConversationIntents.createBuilder(requireContext(), model.storyViewItemData.recipient.id, -1L).subscribeBy { + val chatIntent = it.build() + startActivity(chatIntent) + } }, removeFromStory = { if (state.storyRecipient?.isDistributionList == true) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java index b5f3f3e503..ec6e8cc961 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/CommunicationActions.java @@ -48,6 +48,7 @@ import org.thoughtcrime.securesms.util.views.SimpleProgressDialog; import org.whispersystems.signalservice.api.push.ServiceId; import java.io.IOException; +import java.util.Objects; import java.util.Optional; import java.util.concurrent.TimeUnit; @@ -142,13 +143,12 @@ public class CommunicationActions { new AsyncTask() { @Override protected Long doInBackground(Void... voids) { - return SignalDatabase.threads().getThreadIdFor(recipient.getId()); + return SignalDatabase.threads().getOrCreateThreadIdFor(recipient); } @Override - protected void onPostExecute(@Nullable Long threadId) { - // TODO [alex] -- ThreadID should *always* exist - ConversationIntents.Builder builder = ConversationIntents.createBuilder(context, recipient.getId(), threadId != null ? threadId : -1); + protected void onPostExecute(@NonNull Long threadId) { + ConversationIntents.Builder builder = ConversationIntents.createBuilderSync(context, recipient.getId(), Objects.requireNonNull(threadId)); if (!TextUtils.isEmpty(text)) { builder.withDraftText(text); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java index 879f0fa597..6d702491f2 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java +++ b/app/src/main/java/org/thoughtcrime/securesms/util/ConversationUtil.java @@ -216,14 +216,14 @@ public final class ConversationUtil { { Recipient resolved = recipient.resolve(); Person[] persons = buildPersons(context, resolved); - Long threadId = SignalDatabase.threads().getThreadIdFor(resolved.getId()); + long threadId = SignalDatabase.threads().getOrCreateThreadIdFor(resolved); String shortName = resolved.isSelf() ? context.getString(R.string.note_to_self) : resolved.getShortDisplayName(context); String longName = resolved.isSelf() ? context.getString(R.string.note_to_self) : resolved.getDisplayName(context); String shortcutId = getShortcutId(resolved); ShortcutInfoCompat.Builder builder = new ShortcutInfoCompat.Builder(context, shortcutId) .setLongLived(true) - .setIntent(ConversationIntents.createBuilder(context, resolved.getId(), threadId != null ? threadId : -1).build()) + .setIntent(ConversationIntents.createBuilderSync(context, resolved.getId(), threadId).build()) .setShortLabel(shortName) .setLongLabel(longName) .setIcon(AvatarUtil.getIconCompatForShortcut(context, resolved))