diff --git a/app/src/test/kotlin/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirementsImplTest.kt b/app/src/test/kotlin/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirementsImplTest.kt index 8edf5ebbd..b33acc0d3 100644 --- a/app/src/test/kotlin/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirementsImplTest.kt +++ b/app/src/test/kotlin/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirementsImplTest.kt @@ -37,7 +37,7 @@ class CheckConversationActionRequirementsImplTest { assertEquals( ConversationActionRequirementsResult.SmsNotCapable, - useCase(), + useCase(requiresPreferredSmsSim = true), ) } @@ -49,7 +49,7 @@ class CheckConversationActionRequirementsImplTest { assertEquals( ConversationActionRequirementsResult.NoPreferredSmsSim, - useCase(), + useCase(requiresPreferredSmsSim = true), ) } @@ -64,7 +64,7 @@ class CheckConversationActionRequirementsImplTest { assertEquals( ConversationActionRequirementsResult.MissingDefaultSmsRole, - useCase(), + useCase(requiresPreferredSmsSim = true), ) } @@ -82,7 +82,43 @@ class CheckConversationActionRequirementsImplTest { assertEquals( ConversationActionRequirementsResult.MissingDefaultSmsRole, - useCase(), + useCase(requiresPreferredSmsSim = true), + ) + } + + @Test + fun invoke_whenPreferredSmsSimIsNotRequired_ignoresMissingPreferredSmsSim() { + every { phoneUtils.isSmsCapable } returns true + every { phoneUtils.hasPreferredSmsSim } returns false + every { + roleManager.isRoleAvailable(RoleManager.ROLE_SMS) + } returns true + every { + roleManager.isRoleHeld(RoleManager.ROLE_SMS) + } returns true + val useCase = CheckConversationActionRequirementsImpl(roleManager = roleManager) + + assertEquals( + ConversationActionRequirementsResult.Ready, + useCase(requiresPreferredSmsSim = false), + ) + } + + @Test + fun invoke_whenSimNotRequiredAndSmsRoleNotHeld_returnsMissingDefaultSmsRole() { + every { phoneUtils.isSmsCapable } returns true + every { phoneUtils.hasPreferredSmsSim } returns false + every { + roleManager.isRoleAvailable(RoleManager.ROLE_SMS) + } returns true + every { + roleManager.isRoleHeld(RoleManager.ROLE_SMS) + } returns false + val useCase = CheckConversationActionRequirementsImpl(roleManager = roleManager) + + assertEquals( + ConversationActionRequirementsResult.MissingDefaultSmsRole, + useCase(requiresPreferredSmsSim = false), ) } @@ -100,7 +136,7 @@ class CheckConversationActionRequirementsImplTest { assertEquals( ConversationActionRequirementsResult.Ready, - useCase(), + useCase(requiresPreferredSmsSim = true), ) } } diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/BaseConversationDraftDelegateTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/BaseConversationDraftDelegateTest.kt index 07122935b..135531bab 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/BaseConversationDraftDelegateTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/BaseConversationDraftDelegateTest.kt @@ -141,7 +141,7 @@ internal abstract class BaseConversationDraftDelegateTest { results: List, ): CheckConversationActionRequirements { val mock = mockk() - every { mock.invoke() } returnsMany results + every { mock.invoke(requiresPreferredSmsSim = true) } returnsMany results return mock } diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/ConversationDraftDelegateSimSelectionTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/ConversationDraftDelegateSimSelectionTest.kt index 384ad53a2..dff4d55f2 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/ConversationDraftDelegateSimSelectionTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/composer/delegate/draft/ConversationDraftDelegateSimSelectionTest.kt @@ -48,7 +48,7 @@ internal class ConversationDraftDelegateSimSelectionTest { @Before fun setUp() { every { - checkConversationActionRequirements() + checkConversationActionRequirements(requiresPreferredSmsSim = true) } returns ConversationActionRequirementsResult.Ready every { diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/BaseConversationMessageSelectionDelegateTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/BaseConversationMessageSelectionDelegateTest.kt index 87139bc14..d6a106085 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/BaseConversationMessageSelectionDelegateTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/BaseConversationMessageSelectionDelegateTest.kt @@ -84,10 +84,19 @@ internal abstract class BaseConversationMessageSelectionDelegateTest { results: List, ): CheckConversationActionRequirements { val mock = mockk() - every { mock.invoke() } returnsMany results + every { mock.invoke(requiresPreferredSmsSim = any()) } returnsMany results return mock } + protected fun createNoPreferredSmsSimRequirements(): CheckConversationActionRequirements { + return CheckConversationActionRequirements { requiresPreferredSmsSim -> + when { + requiresPreferredSmsSim -> ConversationActionRequirementsResult.NoPreferredSmsSim + else -> ConversationActionRequirementsResult.Ready + } + } + } + protected fun createAttachmentPart(): ConversationMessagePartUiModel.Attachment { return ConversationMessagePartUiModel.Attachment.Image( text = null, diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateDeleteTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateDeleteTest.kt index cef415d94..a614cbc29 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateDeleteTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateDeleteTest.kt @@ -1,5 +1,6 @@ package com.android.messaging.ui.conversation.messages.delegate.selection +import app.cash.turbine.test import com.android.messaging.data.conversation.model.MessageId import com.android.messaging.ui.conversation.screen.model.ConversationMessageAction import com.android.messaging.ui.conversation.screen.model.ConversationMessageSelectionUiState @@ -99,4 +100,48 @@ internal class ConversationMessageSelectionDelegateDeleteTest : } } } + + @Test + fun deleteAction_whenPreferredSmsSimIsMissing_deletesSelectedMessage() { + runTest(context = mainDispatcherRule.testDispatcher) { + val harness = createHarness( + actionRequirements = createNoPreferredSmsSimRequirements(), + ) + + try { + harness.messagesStateFlow.value = createMessagesUiState( + createMessageUiModel(messageId = "message-1"), + ) + advanceUntilIdle() + harness.delegate.onMessageLongClick(messageId = MessageId("message-1")) + advanceUntilIdle() + + harness.delegate.effects.test { + harness.delegate.onMessageSelectionActionClick( + action = ConversationMessageAction.Delete, + ) + advanceUntilIdle() + + assertEquals( + persistentSetOf(MessageId("message-1")), + harness.delegate.state.value.deleteConfirmation?.messageIds, + ) + + harness.delegate.confirmDeleteSelectedMessages() + advanceUntilIdle() + + expectNoEvents() + cancelAndIgnoreRemainingEvents() + } + + verify(exactly = 1) { + harness.conversationsRepository.deleteMessages( + messageIds = persistentSetOf(MessageId("message-1")), + ) + } + } finally { + harness.cancel() + } + } + } } diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateResendTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateResendTest.kt index a1fbff995..e25e3a591 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateResendTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/messages/delegate/selection/ConversationMessageSelectionDelegateResendTest.kt @@ -128,9 +128,7 @@ internal class ConversationMessageSelectionDelegateResendTest : fun resendAction_whenPreferredSmsSimIsMissing_emitsNoPreferredSimMessage() { runTest(context = mainDispatcherRule.testDispatcher) { val harness = createHarness( - actionRequirements = createActionRequirementsMock( - initialResult = ConversationActionRequirementsResult.NoPreferredSmsSim, - ), + actionRequirements = createNoPreferredSmsSimRequirements(), ) try { diff --git a/app/src/test/kotlin/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegateImplTest.kt b/app/src/test/kotlin/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegateImplTest.kt index 7d02972c9..ca737479c 100644 --- a/app/src/test/kotlin/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegateImplTest.kt +++ b/app/src/test/kotlin/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegateImplTest.kt @@ -9,6 +9,7 @@ import com.android.messaging.data.conversation.model.metadata.ConversationCompos import com.android.messaging.data.conversation.model.metadata.ConversationMetadata import com.android.messaging.data.conversation.repository.ConversationsRepository import com.android.messaging.domain.conversation.usecase.action.ArchiveConversation +import com.android.messaging.domain.conversation.usecase.action.CheckConversationActionRequirements import com.android.messaging.domain.conversation.usecase.action.ConversationActionRequirementsResult import com.android.messaging.testutil.MainDispatcherRule import com.android.messaging.testutil.TEST_CALL_ACTION_PHONE_NUMBER @@ -230,6 +231,43 @@ class ConversationMetadataDelegateImplTest { } } + @Test + fun onDeleteConversationClick_whenPreferredSmsSimIsMissing_showsConfirmation() { + runTest(context = mainDispatcherRule.testDispatcher) { + val harness = createHarness( + conversationId = ConversationId("conversation-42"), + checkConversationActionRequirements = { requiresPreferredSmsSim -> + when { + requiresPreferredSmsSim -> { + ConversationActionRequirementsResult.NoPreferredSmsSim + } + + else -> ConversationActionRequirementsResult.Ready + } + }, + ) + + try { + advanceUntilIdle() + + harness.delegate.effects.test { + harness.delegate.onDeleteConversationClick() + advanceUntilIdle() + + expectNoEvents() + cancelAndIgnoreRemainingEvents() + } + + assertEquals( + true, + harness.delegate.isDeleteConversationConfirmationVisible.value, + ) + } finally { + harness.cancel() + } + } + } + @Test fun confirmDeleteConversation_deletesViaRepositoryAndEmitsCloseConversation() { runTest(context = mainDispatcherRule.testDispatcher) { @@ -308,7 +346,11 @@ class ConversationMetadataDelegateImplTest { } } - private fun createHarness(conversationId: ConversationId?): DelegateHarness { + private fun createHarness( + conversationId: ConversationId?, + checkConversationActionRequirements: CheckConversationActionRequirements = + CheckConversationActionRequirements { ConversationActionRequirementsResult.Ready }, + ): DelegateHarness { val dispatcher = mainDispatcherRule.testDispatcher val scope = TestScope(dispatcher) val conversationsRepository = mockk(relaxed = true) @@ -342,9 +384,7 @@ class ConversationMetadataDelegateImplTest { } val delegate = ConversationMetadataDelegateImpl( - checkConversationActionRequirements = { - ConversationActionRequirementsResult.Ready - }, + checkConversationActionRequirements = checkConversationActionRequirements, conversationsRepository = conversationsRepository, conversationMetadataUiStateMapper = mapper, blockedParticipantsRepository = mockk(relaxed = true), diff --git a/src/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirements.kt b/src/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirements.kt index 65d1b8e25..0c36c0755 100644 --- a/src/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirements.kt +++ b/src/com/android/messaging/domain/conversation/usecase/action/CheckConversationActionRequirements.kt @@ -5,7 +5,7 @@ import com.android.messaging.util.PhoneUtils import javax.inject.Inject internal fun interface CheckConversationActionRequirements { - operator fun invoke(): ConversationActionRequirementsResult + operator fun invoke(requiresPreferredSmsSim: Boolean): ConversationActionRequirementsResult } internal class CheckConversationActionRequirementsImpl @Inject constructor( @@ -14,11 +14,13 @@ internal class CheckConversationActionRequirementsImpl @Inject constructor( private val phoneUtils by lazy { PhoneUtils.getDefault() } - override operator fun invoke(): ConversationActionRequirementsResult { + override operator fun invoke( + requiresPreferredSmsSim: Boolean, + ): ConversationActionRequirementsResult { return when { !phoneUtils.isSmsCapable -> ConversationActionRequirementsResult.SmsNotCapable - !phoneUtils.hasPreferredSmsSim -> { + requiresPreferredSmsSim && !phoneUtils.hasPreferredSmsSim -> { ConversationActionRequirementsResult.NoPreferredSmsSim } diff --git a/src/com/android/messaging/ui/conversation/composer/delegate/ConversationDraftDelegate.kt b/src/com/android/messaging/ui/conversation/composer/delegate/ConversationDraftDelegate.kt index 43dc96ec1..8dd512d18 100644 --- a/src/com/android/messaging/ui/conversation/composer/delegate/ConversationDraftDelegate.kt +++ b/src/com/android/messaging/ui/conversation/composer/delegate/ConversationDraftDelegate.kt @@ -492,7 +492,7 @@ internal class ConversationDraftDelegateImpl @Inject constructor( } private fun sendDraftWhenActionRequirementsSatisfied(sendRequest: DraftSendRequest) { - when (checkConversationActionRequirements()) { + when (checkConversationActionRequirements(requiresPreferredSmsSim = true)) { ConversationActionRequirementsResult.Ready -> { sendDraft(sendRequest = sendRequest) } diff --git a/src/com/android/messaging/ui/conversation/messages/delegate/ConversationMessageSelectionDelegate.kt b/src/com/android/messaging/ui/conversation/messages/delegate/ConversationMessageSelectionDelegate.kt index e2fb8411e..05cc8057c 100644 --- a/src/com/android/messaging/ui/conversation/messages/delegate/ConversationMessageSelectionDelegate.kt +++ b/src/com/android/messaging/ui/conversation/messages/delegate/ConversationMessageSelectionDelegate.kt @@ -306,6 +306,7 @@ internal class ConversationMessageSelectionDelegateImpl @Inject constructor( ) { runWhenConversationActionRequirementsSatisfied( isSending = false, + requiresPreferredSmsSim = false, onReady = { messageSelectionState.update { currentState -> currentState.copy( @@ -322,6 +323,7 @@ internal class ConversationMessageSelectionDelegateImpl @Inject constructor( ) { runWhenConversationActionRequirementsSatisfied( isSending = false, + requiresPreferredSmsSim = false, onReady = { clearMessageSelection() conversationsRepository.deleteMessages(messageIds = messageIds) @@ -343,6 +345,7 @@ internal class ConversationMessageSelectionDelegateImpl @Inject constructor( ) { runWhenConversationActionRequirementsSatisfied( isSending = messageAction.isSending, + requiresPreferredSmsSim = true, onReady = { runMessageAction(messageAction = messageAction) }, @@ -354,11 +357,16 @@ internal class ConversationMessageSelectionDelegateImpl @Inject constructor( private fun runWhenConversationActionRequirementsSatisfied( isSending: Boolean, + requiresPreferredSmsSim: Boolean, onReady: () -> Unit, onBlocked: () -> Unit = {}, onMissingDefaultSmsRole: () -> Unit = {}, ) { - when (checkConversationActionRequirements()) { + val requirementsResult = checkConversationActionRequirements( + requiresPreferredSmsSim = requiresPreferredSmsSim, + ) + + when (requirementsResult) { ConversationActionRequirementsResult.Ready -> { onReady() } diff --git a/src/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegate.kt b/src/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegate.kt index 1c2868371..03b2802d4 100644 --- a/src/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegate.kt +++ b/src/com/android/messaging/ui/conversation/metadata/delegate/ConversationMetadataDelegate.kt @@ -179,7 +179,7 @@ internal class ConversationMetadataDelegateImpl @Inject constructor( return } - when (checkConversationActionRequirements()) { + when (checkConversationActionRequirements(requiresPreferredSmsSim = false)) { ConversationActionRequirementsResult.Ready -> { _isDeleteConversationConfirmationVisible.value = true }