mirror of
https://github.com/signalapp/Signal-Android.git
synced 2025-12-24 21:15:48 +00:00
Update ConversationOptionsMenuProvider to utilize snapshot data class.
This commit is contained in:
committed by
Greyson Parrelli
parent
27e3c883c3
commit
490feb358c
@@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user