Update ConversationOptionsMenuProvider to utilize snapshot data class.

This commit is contained in:
Alex Hart
2023-04-07 11:01:39 -03:00
committed by Greyson Parrelli
parent 27e3c883c3
commit 490feb358c
3 changed files with 109 additions and 121 deletions

View File

@@ -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<Boolean>,
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()
}
}

View File

@@ -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

View File

@@ -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();
}