diff --git a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java index cd78485e4d..29a509f5fd 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/BindableConversationListItem.java @@ -17,9 +17,11 @@ public interface BindableConversationListItem extends Unbindable { @NonNull ThreadRecord thread, @NonNull RequestManager requestManager, @NonNull Locale locale, @NonNull Set typingThreads, - @NonNull ConversationSet selectedConversations); + @NonNull ConversationSet selectedConversations, + long activeThreadId); void setSelectedConversations(@NonNull ConversationSet conversations); + void setActiveThreadId(long activeThreadId); void updateTypingIndicator(@NonNull Set typingThreads); void updateTimestamp(); } diff --git a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt index 57a85a1cdb..42cd1913a6 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/MainActivity.kt @@ -211,7 +211,7 @@ class MainActivity : PassphraseRequiredActivity(), VoiceNoteMediaControllerOwner setContent { val listHostState = rememberFragmentState() - val detailLocation by mainNavigationViewModel.detailLocation.collectAsStateWithLifecycle(MainNavigationDetailLocation.Empty) + val detailLocation by mainNavigationViewModel.detailLocationRequests.collectAsStateWithLifecycle(MainNavigationDetailLocation.Empty) val snackbar by mainNavigationViewModel.snackbar.collectAsStateWithLifecycle() val mainToolbarState by toolbarViewModel.state.collectAsStateWithLifecycle() val megaphone by mainNavigationViewModel.megaphone.collectAsStateWithLifecycle() diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java index f69beb0336..91252feda9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListAdapter.java @@ -44,7 +44,8 @@ class ConversationListAdapter extends ListAdapter typingSet = new HashSet<>(); private ConversationSet selectedConversations = new ConversationSet(); + private long activeThreadId = 0; private PagingController pagingController; protected ConversationListAdapter(@NonNull LifecycleOwner lifecycleOwner, @@ -148,6 +150,7 @@ class ConversationListAdapter extends ListAdapter vh.getConversationListItem().updateTypingIndicator(typingSet); case SELECTION -> vh.getConversationListItem().setSelectedConversations(selectedConversations); case TIMESTAMP -> vh.getConversationListItem().updateTimestamp(); + case ACTIVE -> vh.getConversationListItem().setActiveThreadId(activeThreadId); } } } @@ -165,7 +168,8 @@ class ConversationListAdapter extends ListAdapter { + if (location instanceof MainNavigationDetailLocation.Conversation) { + Intent intent = ((MainNavigationDetailLocation.Conversation) location).getIntent(); + ConversationIntents.Args args = ConversationIntents.Args.from(Objects.requireNonNull(intent.getExtras())); + long threadId = args.getThreadId(); + + defaultAdapter.setActiveThreadId(threadId); + } + })); + } else { + defaultAdapter.setActiveThreadId(0); + } + requireCallback().bindScrollHelper(list, getViewLifecycleOwner(), chatFolderList, color -> { for (int i = 0; i < chatFolderList.getChildCount(); i++) { View child = chatFolderList.getChildAt(i); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java index f8f1f45301..838d94874f 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItem.java @@ -65,6 +65,7 @@ import org.thoughtcrime.securesms.components.emoji.EmojiStrings; import org.thoughtcrime.securesms.components.emoji.EmojiTextView; import org.thoughtcrime.securesms.contacts.paged.ContactSearchData; import org.thoughtcrime.securesms.conversation.MessageStyler; +import org.thoughtcrime.securesms.conversationlist.model.Conversation; import org.thoughtcrime.securesms.conversationlist.model.ConversationSet; import org.thoughtcrime.securesms.database.MessageTypes; import org.thoughtcrime.securesms.database.ThreadTable; @@ -213,9 +214,10 @@ public final class ConversationListItem extends ConstraintLayout implements Bind @NonNull RequestManager glideRequests, @NonNull Locale locale, @NonNull Set typingThreads, - @NonNull ConversationSet selectedConversations) + @NonNull ConversationSet selectedConversations, + long activeThreadId) { - bindThread(lifecycleOwner, thread, glideRequests, locale, typingThreads, selectedConversations, null, false, true); + bindThread(lifecycleOwner, thread, glideRequests, locale, typingThreads, selectedConversations, null, false, true, activeThreadId); } public void bindThread(@NonNull LifecycleOwner lifecycleOwner, @@ -226,7 +228,8 @@ public final class ConversationListItem extends ConstraintLayout implements Bind @NonNull ConversationSet selectedConversations, @Nullable String highlightSubstring, boolean appendSystemContactIcon, - boolean showPinned) + boolean showPinned, + long activeThreadId) { this.threadId = thread.getThreadId(); this.requestManager = requestManager; @@ -282,6 +285,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind this.archivedView.setVisibility(View.GONE); } + setActiveThreadId(activeThreadId); setStatusIcons(thread); setSelectedConversations(selectedConversations); setBadgeFromRecipient(recipient.get()); @@ -326,6 +330,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind alertView.setNone(); setSelectedConversations(new ConversationSet()); + setActiveThreadId(0); setBadgeFromRecipient(recipient.get()); contactPhotoImage.setAvatar(requestManager, recipient.get(), !batchMode, false); } @@ -363,6 +368,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind alertView.setNone(); setSelectedConversations(new ConversationSet()); + setActiveThreadId(0); setBadgeFromRecipient(recipient.get()); contactPhotoImage.setAvatar(requestManager, recipient.get(), !batchMode); } @@ -383,6 +389,7 @@ public final class ConversationListItem extends ConstraintLayout implements Bind if (this.recipient != null) { observeRecipient(null, null); setSelectedConversations(new ConversationSet()); + setActiveThreadId(0); contactPhotoImage.setAvatar(requestManager, null, !batchMode); } @@ -391,6 +398,11 @@ public final class ConversationListItem extends ConstraintLayout implements Bind updateDateView = null; } + @Override + public void setActiveThreadId(long activeThreadId) { + setActivated(activeThreadId > 0 && this.threadId == activeThreadId); + } + @Override public void setSelectedConversations(@NonNull ConversationSet conversations) { this.batchMode = !conversations.isEmpty(); diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java index 8a2e5cd3d0..b57797444c 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListItemAction.java @@ -46,7 +46,8 @@ public class ConversationListItemAction extends FrameLayout implements BindableC @NonNull RequestManager requestManager, @NonNull Locale locale, @NonNull Set typingThreads, - @NonNull ConversationSet selectedConversations) + @NonNull ConversationSet selectedConversations, + long activeThreadId) { this.description.setText(getContext().getString(R.string.ConversationListItemAction_archived_conversations_d, thread.getUnreadCount())); } @@ -56,6 +57,11 @@ public class ConversationListItemAction extends FrameLayout implements BindableC } + @Override + public void setActiveThreadId(long activeThreadId) { + + } + @Override public void setSelectedConversations(@NonNull ConversationSet conversations) { diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt index 5c291dc905..8312871e37 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversationlist/ConversationListSearchAdapter.kt @@ -122,7 +122,8 @@ class ConversationListSearchAdapter( ConversationSet(), model.thread.query, true, - false + false, + 0 ) } } diff --git a/app/src/main/java/org/thoughtcrime/securesms/main/MainNavigationViewModel.kt b/app/src/main/java/org/thoughtcrime/securesms/main/MainNavigationViewModel.kt index 76df752052..a44158dd2d 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/main/MainNavigationViewModel.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/main/MainNavigationViewModel.kt @@ -30,8 +30,18 @@ import org.thoughtcrime.securesms.stories.Stories class MainNavigationViewModel(initialListLocation: MainNavigationListLocation = MainNavigationListLocation.CHATS) : ViewModel() { private val megaphoneRepository = AppDependencies.megaphoneRepository - private val detailLocationFlow = MutableSharedFlow() - val detailLocation: SharedFlow = detailLocationFlow + /** + * A shared flow of detail location requests that the MainActivity will service. + */ + private val detailLocationRequestFlow = MutableSharedFlow() + val detailLocationRequests: SharedFlow = detailLocationRequestFlow + + /** + * The latest detail location that has been requested, for consumption by other components. + */ + private val detailLocationFlow = MutableStateFlow(MainNavigationDetailLocation.Empty) + val detailLocation: StateFlow = detailLocationFlow + val detailLocationObservable: Observable = detailLocationFlow.asObservable() private val internalMegaphone = MutableStateFlow(Megaphone.NONE) val megaphone: StateFlow = internalMegaphone @@ -73,6 +83,7 @@ class MainNavigationViewModel(initialListLocation: MainNavigationListLocation = fun goTo(location: MainNavigationDetailLocation) { viewModelScope.launch { + detailLocationRequestFlow.emit(location) detailLocationFlow.emit(location) } } diff --git a/app/src/main/res/drawable-v21/conversation_list_item_background.xml b/app/src/main/res/drawable-v21/conversation_list_item_background.xml deleted file mode 100644 index cccf562b19..0000000000 --- a/app/src/main/res/drawable-v21/conversation_list_item_background.xml +++ /dev/null @@ -1,39 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/app/src/main/res/drawable/conversation_list_item_background.xml b/app/src/main/res/drawable/conversation_list_item_background.xml index 117cef6023..9e4942a656 100644 --- a/app/src/main/res/drawable/conversation_list_item_background.xml +++ b/app/src/main/res/drawable/conversation_list_item_background.xml @@ -1,7 +1,58 @@ - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +