From 490feb358c8dbc2cfa14d8a2e4d8ad92093b8170 Mon Sep 17 00:00:00 2001 From: Alex Hart Date: Fri, 7 Apr 2023 11:01:39 -0300 Subject: [PATCH] Update ConversationOptionsMenuProvider to utilize snapshot data class. --- .../conversation/ConversationOptionsMenu.kt | 157 +++++++++--------- .../ConversationParentFragment.java | 66 +++----- .../conversation/ConversationTitleView.java | 7 +- 3 files changed, 109 insertions(+), 121 deletions(-) diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationOptionsMenu.kt b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationOptionsMenu.kt index de7a5710de..70d6c1e8e9 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationOptionsMenu.kt +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationOptionsMenu.kt @@ -5,13 +5,11 @@ import android.view.MenuInflater import android.view.MenuItem import androidx.annotation.IdRes import androidx.core.view.MenuProvider +import io.reactivex.rxjava3.core.Observable import io.reactivex.rxjava3.kotlin.subscribeBy import org.thoughtcrime.securesms.R -import org.thoughtcrime.securesms.conversation.ConversationGroupViewModel.GroupActiveState -import org.thoughtcrime.securesms.conversation.ui.groupcall.GroupCallViewModel import org.thoughtcrime.securesms.database.ThreadTable import org.thoughtcrime.securesms.keyvalue.SignalStore -import org.thoughtcrime.securesms.recipients.LiveRecipient import org.thoughtcrime.securesms.recipients.Recipient import org.thoughtcrime.securesms.util.LifecycleDisposable @@ -24,56 +22,63 @@ internal object ConversationOptionsMenu { * MenuProvider implementation for the conversation options menu. */ class Provider( - private val dependencies: Dependencies, - private val optionsMenuProviderCallback: Callback, + private val callback: Callback, private val lifecycleDisposable: LifecycleDisposable - ) : MenuProvider, Dependencies by dependencies { + ) : MenuProvider { override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) { menu.clear() - val recipient: Recipient? = liveRecipient?.get() - val groupActiveState: GroupActiveState? = groupViewModel.groupActiveState.value - val isActiveGroup = groupActiveState != null && groupActiveState.isActiveGroup - val isActiveV2Group = groupActiveState != null && groupActiveState.isActiveV2Group - val isInActiveGroup = groupActiveState != null && !groupActiveState.isActiveGroup + val ( + recipient, + isPushAvailable, + canShowAsBubble, + isActiveGroup, + isActiveV2Group, + isInActiveGroup, + hasActiveGroupCall, + distributionType, + threadId, + isInMessageRequest, + isInBubble + ) = callback.getSnapshot() - if (isInMessageRequest() && (recipient != null) && !recipient.isBlocked) { + if (isInMessageRequest && (recipient != null) && !recipient.isBlocked) { if (isActiveGroup) { menuInflater.inflate(R.menu.conversation_message_requests_group, menu) } } - if (viewModel.isPushAvailable) { + if (isPushAvailable) { if (recipient!!.expiresInSeconds > 0) { if (!isInActiveGroup) { menuInflater.inflate(R.menu.conversation_expiring_on, menu) } - titleView.showExpiring(liveRecipient!!) + callback.showExpiring(recipient) } else { if (!isInActiveGroup) { menuInflater.inflate(R.menu.conversation_expiring_off, menu) } - titleView.clearExpiring() + callback.clearExpiring() } } - if (isSingleConversation()) { - if (viewModel.isPushAvailable) { + if (recipient?.isGroup == false) { + if (isPushAvailable) { menuInflater.inflate(R.menu.conversation_callable_secure, menu) - } else if (!recipient!!.isReleaseNotes && SignalStore.misc().smsExportPhase.allowSmsFeatures()) { + } else if (!recipient.isReleaseNotes && SignalStore.misc().smsExportPhase.allowSmsFeatures()) { menuInflater.inflate(R.menu.conversation_callable_insecure, menu) } - } else if (isGroupConversation()) { + } else if (recipient?.isGroup == true) { if (isActiveV2Group) { menuInflater.inflate(R.menu.conversation_callable_groupv2, menu) - if (groupCallViewModel != null && java.lang.Boolean.TRUE == groupCallViewModel.hasActiveGroupCall().getValue()) { + if (hasActiveGroupCall) { hideMenuItem(menu, R.id.menu_video_secure) } - showGroupCallingTooltip() + callback.showGroupCallingTooltip() } menuInflater.inflate(R.menu.conversation_group_options, menu) - if (!isPushGroupConversation()) { + if (!recipient.isPushGroup) { menuInflater.inflate(R.menu.conversation_mms_group_options, menu) if (distributionType == ThreadTable.DistributionTypes.BROADCAST) { menu.findItem(R.id.menu_distribution_broadcast).isChecked = true @@ -86,22 +91,22 @@ internal object ConversationOptionsMenu { menuInflater.inflate(R.menu.conversation, menu) - if (isInMessageRequest() && !recipient!!.isBlocked) { + if (isInMessageRequest && !recipient!!.isBlocked) { hideMenuItem(menu, R.id.menu_conversation_settings) } - if (isSingleConversation() && !viewModel.isPushAvailable && !recipient!!.isReleaseNotes) { + if (recipient?.isGroup == false && !isPushAvailable && !recipient.isReleaseNotes) { menuInflater.inflate(R.menu.conversation_insecure, menu) } - if (recipient != null && recipient.isMuted) menuInflater.inflate(R.menu.conversation_muted, menu) else menuInflater.inflate(R.menu.conversation_unmuted, menu) + if (recipient?.isMuted == true) menuInflater.inflate(R.menu.conversation_muted, menu) else menuInflater.inflate(R.menu.conversation_unmuted, menu) - if (isSingleConversation() && getRecipient()!!.contactUri == null && !recipient!!.isReleaseNotes && !recipient.isSelf && recipient.hasE164()) { + if (recipient?.isGroup == false && recipient.contactUri == null && !recipient.isReleaseNotes && !recipient.isSelf && recipient.hasE164()) { menuInflater.inflate(R.menu.conversation_add_to_contacts, menu) } if (recipient != null && recipient.isSelf) { - if (viewModel.isPushAvailable) { + if (isPushAvailable) { hideMenuItem(menu, R.id.menu_call_secure) hideMenuItem(menu, R.id.menu_video_secure) } else { @@ -110,8 +115,8 @@ internal object ConversationOptionsMenu { hideMenuItem(menu, R.id.menu_mute_notifications) } - if (recipient != null && recipient.isBlocked) { - if (viewModel.isPushAvailable) { + if (recipient?.isBlocked == true) { + if (isPushAvailable) { hideMenuItem(menu, R.id.menu_call_secure) hideMenuItem(menu, R.id.menu_video_secure) hideMenuItem(menu, R.id.menu_expiring_messages) @@ -122,7 +127,7 @@ internal object ConversationOptionsMenu { hideMenuItem(menu, R.id.menu_mute_notifications) } - if (recipient != null && recipient.isReleaseNotes) { + if (recipient?.isReleaseNotes == true) { hideMenuItem(menu, R.id.menu_add_shortcut) } @@ -131,15 +136,15 @@ internal object ConversationOptionsMenu { if (isActiveV2Group) { hideMenuItem(menu, R.id.menu_mute_notifications) hideMenuItem(menu, R.id.menu_conversation_settings) - } else if (isGroupConversation()) { + } else if (recipient?.isGroup == true) { hideMenuItem(menu, R.id.menu_conversation_settings) } hideMenuItem(menu, R.id.menu_create_bubble) - lifecycleDisposable += viewModel.canShowAsBubble().subscribeBy(onNext = { canShowAsBubble: Boolean -> + lifecycleDisposable += canShowAsBubble.subscribeBy(onNext = { yes: Boolean -> val item = menu.findItem(R.id.menu_create_bubble) if (item != null) { - item.isVisible = canShowAsBubble && !isInBubble() + item.isVisible = yes && !isInBubble } }) @@ -147,77 +152,70 @@ internal object ConversationOptionsMenu { hideMenuItem(menu, R.id.menu_view_media) } - optionsMenuProviderCallback.onOptionsMenuCreated(menu) + callback.onOptionsMenuCreated(menu) } override fun onMenuItemSelected(menuItem: MenuItem): Boolean { when (menuItem.itemId) { - R.id.menu_call_secure -> optionsMenuProviderCallback.handleDial(getRecipient(), true) - R.id.menu_video_secure -> optionsMenuProviderCallback.handleVideo(getRecipient()) - R.id.menu_call_insecure -> optionsMenuProviderCallback.handleDial(getRecipient(), false) - R.id.menu_view_media -> optionsMenuProviderCallback.handleViewMedia() - R.id.menu_add_shortcut -> optionsMenuProviderCallback.handleAddShortcut() - R.id.menu_search -> optionsMenuProviderCallback.handleSearch() - R.id.menu_add_to_contacts -> optionsMenuProviderCallback.handleAddToContacts() - R.id.menu_group_recipients -> optionsMenuProviderCallback.handleDisplayGroupRecipients() - R.id.menu_distribution_broadcast -> optionsMenuProviderCallback.handleDistributionBroadcastEnabled(menuItem) - R.id.menu_distribution_conversation -> optionsMenuProviderCallback.handleDistributionConversationEnabled(menuItem) - R.id.menu_group_settings -> optionsMenuProviderCallback.handleManageGroup() - R.id.menu_leave -> optionsMenuProviderCallback.handleLeavePushGroup() - R.id.menu_invite -> optionsMenuProviderCallback.handleInviteLink() - R.id.menu_mute_notifications -> optionsMenuProviderCallback.handleMuteNotifications() - R.id.menu_unmute_notifications -> optionsMenuProviderCallback.handleUnmuteNotifications() - R.id.menu_conversation_settings -> optionsMenuProviderCallback.handleConversationSettings() - R.id.menu_expiring_messages_off, R.id.menu_expiring_messages -> optionsMenuProviderCallback.handleSelectMessageExpiration() - R.id.menu_create_bubble -> optionsMenuProviderCallback.handleCreateBubble() - R.id.home -> optionsMenuProviderCallback.handleGoHome() + R.id.menu_call_secure -> callback.handleDial(true) + R.id.menu_video_secure -> callback.handleVideo() + R.id.menu_call_insecure -> callback.handleDial(false) + R.id.menu_view_media -> callback.handleViewMedia() + R.id.menu_add_shortcut -> callback.handleAddShortcut() + R.id.menu_search -> callback.handleSearch() + R.id.menu_add_to_contacts -> callback.handleAddToContacts() + R.id.menu_group_recipients -> callback.handleDisplayGroupRecipients() + R.id.menu_distribution_broadcast -> callback.handleDistributionBroadcastEnabled(menuItem) + R.id.menu_distribution_conversation -> callback.handleDistributionConversationEnabled(menuItem) + R.id.menu_group_settings -> callback.handleManageGroup() + R.id.menu_leave -> callback.handleLeavePushGroup() + R.id.menu_invite -> callback.handleInviteLink() + R.id.menu_mute_notifications -> callback.handleMuteNotifications() + R.id.menu_unmute_notifications -> callback.handleUnmuteNotifications() + R.id.menu_conversation_settings -> callback.handleConversationSettings() + R.id.menu_expiring_messages_off, R.id.menu_expiring_messages -> callback.handleSelectMessageExpiration() + R.id.menu_create_bubble -> callback.handleCreateBubble() + R.id.home -> callback.handleGoHome() else -> return false } return true } - private fun getRecipient(): Recipient? { - return liveRecipient?.get() - } - private fun hideMenuItem(menu: Menu, @IdRes menuItem: Int) { if (menu.findItem(menuItem) != null) { menu.findItem(menuItem).isVisible = false } } - - private fun isSingleConversation(): Boolean = getRecipient()?.isGroup == false - - private fun isGroupConversation(): Boolean = getRecipient()?.isGroup == true - - private fun isPushGroupConversation(): Boolean = getRecipient()?.isPushGroup == true } /** - * Dependencies abstraction for the conversation options menu + * Data snapshot for building out menu state. */ - interface Dependencies { - val liveRecipient: LiveRecipient? - val viewModel: ConversationViewModel - val groupViewModel: ConversationGroupViewModel - val groupCallViewModel: GroupCallViewModel? - val titleView: ConversationTitleView - val distributionType: Int - val threadId: Long - fun isInMessageRequest(): Boolean - fun showGroupCallingTooltip() - fun isInBubble(): Boolean - } + data class Snapshot( + val recipient: Recipient?, + val isPushAvailable: Boolean, + val canShowAsBubble: Observable, + val isActiveGroup: Boolean, + val isActiveV2Group: Boolean, + val isInActiveGroup: Boolean, + val hasActiveGroupCall: Boolean, + val distributionType: Int, + val threadId: Long, + val isInMessageRequest: Boolean, + val isInBubble: Boolean + ) /** * Callbacks abstraction for the converstaion options menu */ interface Callback { + fun getSnapshot(): Snapshot + fun onOptionsMenuCreated(menu: Menu) - fun handleVideo(recipient: Recipient?) - fun handleDial(recipient: Recipient?, isSecure: Boolean) + fun handleVideo() + fun handleDial(isSecure: Boolean) fun handleViewMedia() fun handleAddShortcut() fun handleSearch() @@ -234,5 +232,8 @@ internal object ConversationOptionsMenu { fun handleSelectMessageExpiration() fun handleCreateBubble() fun handleGoHome() + fun showExpiring(recipient: Recipient) + fun clearExpiring() + fun showGroupCallingTooltip() } } 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 00a800224c..15ce0867aa 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationParentFragment.java @@ -363,8 +363,7 @@ public class ConversationParentFragment extends Fragment ScheduleMessageTimePickerBottomSheet.ScheduleCallback, ConversationBottomSheetCallback, ScheduleMessageDialogCallback, - ConversationOptionsMenu.Callback, - ConversationOptionsMenu.Dependencies + ConversationOptionsMenu.Callback { private static final int SHORTCUT_ICON_SIZE = Build.VERSION.SDK_INT >= 26 ? ViewUtil.dpToPx(72) : ViewUtil.dpToPx(48 + 16 * 2); @@ -493,7 +492,7 @@ public class ConversationParentFragment extends Fragment @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { disposables.bindTo(getViewLifecycleOwner()); - menuProvider = new ConversationOptionsMenu.Provider(this, this, disposables); + menuProvider = new ConversationOptionsMenu.Provider(this, disposables); SpoilerAnnotation.resetRevealedSpoilers(); if (requireActivity() instanceof Callback) { @@ -1288,8 +1287,8 @@ public class ConversationParentFragment extends Fragment } @Override - public void handleDial(final Recipient recipient, boolean isSecure) { - if (recipient == null) return; + public void handleDial(boolean isSecure) { + Recipient recipient = getRecipient(); if (isSecure) { CommunicationActions.startVoiceCall(this, recipient); @@ -1299,8 +1298,8 @@ public class ConversationParentFragment extends Fragment } @Override - public void handleVideo(final Recipient recipient) { - if (recipient == null) return; + public void handleVideo() { + Recipient recipient = getRecipient(); if (recipient.isPushV2Group() && groupCallViewModel.hasActiveGroupCall().getValue() == Boolean.FALSE && groupViewModel.isNonAdminInAnnouncementGroup()) { new MaterialAlertDialogBuilder(requireContext()).setTitle(R.string.ConversationActivity_cant_start_group_call) @@ -1971,7 +1970,7 @@ public class ConversationParentFragment extends Fragment reactionDelegate.setOnReactionSelectedListener(this); - joinGroupCallButton.setOnClickListener(v -> handleVideo(getRecipient())); + joinGroupCallButton.setOnClickListener(v -> handleVideo()); voiceNoteMediaController.getVoiceNotePlayerViewState().observe(getViewLifecycleOwner(), state -> { if (state.isPresent()) { @@ -2689,15 +2688,10 @@ public class ConversationParentFragment extends Fragment } } - @Override public boolean isInMessageRequest() { return messageRequestBottomView.getVisibility() == View.VISIBLE; } - private boolean isSingleConversation() { - return getRecipient() != null && !getRecipient().isGroup(); - } - private boolean isActiveGroup() { if (!isGroupConversation()) return false; @@ -2721,20 +2715,10 @@ public class ConversationParentFragment extends Fragment return sendButton.isManualSelection() && sendButton.getSelectedSendType().usesSmsTransport(); } - @Override - public @Nullable LiveRecipient getLiveRecipient() { - return recipient; - } - protected Recipient getRecipient() { return this.recipient.get(); } - @Override - public long getThreadId() { - return this.threadId; - } - private String getMessage() throws InvalidMessageException { String rawText = composeText.getTextTrimmed().toString(); @@ -3551,28 +3535,32 @@ public class ConversationParentFragment extends Fragment } @Override - public @NonNull ConversationViewModel getViewModel() { - return viewModel; + public @NonNull ConversationOptionsMenu.Snapshot getSnapshot() { + ConversationGroupViewModel.GroupActiveState groupActiveState = groupViewModel.getGroupActiveState().getValue(); + + return new ConversationOptionsMenu.Snapshot( + recipient != null ? recipient.get() : null, + viewModel.isPushAvailable(), + viewModel.canShowAsBubble(), + groupActiveState != null && groupActiveState.isActiveGroup(), + groupActiveState != null && groupActiveState.isActiveV2Group(), + groupActiveState != null && !groupActiveState.isActiveGroup(), + groupCallViewModel != null && groupCallViewModel.hasActiveGroupCall().getValue() == Boolean.TRUE, + distributionType, + threadId, + isInMessageRequest(), + isInBubble() + ); } @Override - public @NonNull ConversationGroupViewModel getGroupViewModel() { - return groupViewModel; + public void showExpiring(@NonNull Recipient recipient) { + titleView.showExpiring(recipient); } @Override - public @Nullable GroupCallViewModel getGroupCallViewModel() { - return groupCallViewModel; - } - - @Override - public @NonNull ConversationTitleView getTitleView() { - return titleView; - } - - @Override - public int getDistributionType() { - return distributionType; + public void clearExpiring() { + titleView.clearExpiring(); } // Listeners diff --git a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java index 6837a18fc4..536fcc0eff 100644 --- a/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java +++ b/app/src/main/java/org/thoughtcrime/securesms/conversation/ConversationTitleView.java @@ -23,7 +23,6 @@ import org.thoughtcrime.securesms.avatar.view.AvatarView; import org.thoughtcrime.securesms.badges.BadgeImageView; import org.thoughtcrime.securesms.database.model.StoryViewState; import org.thoughtcrime.securesms.mms.GlideRequests; -import org.thoughtcrime.securesms.recipients.LiveRecipient; import org.thoughtcrime.securesms.recipients.Recipient; import org.thoughtcrime.securesms.util.ContextUtil; import org.thoughtcrime.securesms.util.DrawableUtil; @@ -96,10 +95,10 @@ public class ConversationTitleView extends ConstraintLayout { } - public void showExpiring(@NonNull LiveRecipient recipient) { - isSelf = recipient.get().isSelf(); + public void showExpiring(@NonNull Recipient recipient) { + isSelf = recipient.isSelf(); - expirationBadgeTime.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(getContext(), recipient.get().getExpiresInSeconds())); + expirationBadgeTime.setText(ExpirationUtil.getExpirationAbbreviatedDisplayValue(getContext(), recipient.getExpiresInSeconds())); expirationBadgeContainer.setVisibility(View.VISIBLE); updateSubtitleVisibility(); }