diff --git a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/di/MockCardReaderManagerModule.kt b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/di/MockCardReaderManagerModule.kt index 388bb9eabea2..a00470a8384c 100644 --- a/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/di/MockCardReaderManagerModule.kt +++ b/WooCommerce/src/androidTest/kotlin/com/woocommerce/android/di/MockCardReaderManagerModule.kt @@ -100,6 +100,8 @@ class MockCardReaderManagerModule { override fun cancelPayment(paymentData: PaymentData) {} + override fun cancelReconnection() {} + override suspend fun startAsyncSoftwareUpdate() {} override suspend fun clearCachedCredentials() {} diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectViewModel.kt index 181ec8e92119..87f2d92a967c 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectViewModel.kt @@ -303,6 +303,10 @@ class CardReaderConnectViewModel @Inject constructor( connectionStarted = true viewState.value = provideConnectingState() } + + CardReaderStatus.Reconnecting -> { + // Reconnecting is handled by the SDK, no action needed during connection flow + } } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailFragment.kt index 11eb5ac98526..1420cd5f26d8 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailFragment.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailFragment.kt @@ -27,6 +27,7 @@ import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailVie import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.ConnectedState import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.Loading import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.NotConnectedState +import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.ReconnectingState import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.EXTERNAL import com.woocommerce.android.ui.payments.cardreader.update.CardReaderUpdateDialogFragment import com.woocommerce.android.ui.payments.cardreader.update.CardReaderUpdateViewModel.UpdateResult @@ -180,6 +181,25 @@ class CardReaderDetailFragment : BaseFragment(R.layout.fragment_card_reader_deta Loading -> { } + + is ReconnectingState -> { + with(binding.readerDisconnectedState) { + UiHelpers.setTextOrHide(cardReaderDetailConnectHeaderLabel, state.headerLabel) + UiHelpers.setImageOrHideInLandscapeOnCompactScreenHeightSizeClass( + cardReaderDetailIllustration, + state.illustration + ) + cardReaderDetailFirstHintLabel.visibility = View.GONE + cardReaderDetailFirstHintNumberLabel.visibility = View.GONE + cardReaderDetailSecondHintLabel.visibility = View.GONE + cardReaderDetailSecondHintNumberLabel.visibility = View.GONE + cardReaderDetailThirdHintLabel.visibility = View.GONE + cardReaderDetailThirdHintNumberLabel.visibility = View.GONE + UiHelpers.setTextOrHide(cardReaderDetailConnectBtn, state.cancelBtnLabel) + cardReaderDetailConnectBtn.setOnClickListener { state.onCancelClicked.invoke() } + cardReaderDetailLearnMoreTv.root.visibility = View.GONE + } + } } } } @@ -192,7 +212,10 @@ class CardReaderDetailFragment : BaseFragment(R.layout.fragment_card_reader_deta private fun makeStateVisible(binding: FragmentCardReaderDetailBinding, state: ViewState) { UiHelpers.updateVisibility(binding.readerConnectedState.root, state is ConnectedState) - UiHelpers.updateVisibility(binding.readerDisconnectedState.root, state is NotConnectedState) + UiHelpers.updateVisibility( + binding.readerDisconnectedState.root, + state is NotConnectedState || state is ReconnectingState + ) UiHelpers.updateVisibility(binding.readerConnectedLoading, state is Loading) } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModel.kt index a77fa5141254..7aa961de8320 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModel.kt @@ -13,6 +13,7 @@ import com.woocommerce.android.cardreader.connection.CardReader import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected +import com.woocommerce.android.cardreader.connection.CardReaderStatus.Reconnecting import com.woocommerce.android.cardreader.connection.ReaderType import com.woocommerce.android.cardreader.connection.event.CardReaderBatteryStatus import com.woocommerce.android.cardreader.connection.event.CardReaderBatteryStatus.StatusChanged @@ -86,6 +87,9 @@ class CardReaderDetailViewModel @Inject constructor( ) handleNotConnectedState() } + Reconnecting -> { + handleReconnectingState() + } } } } @@ -134,6 +138,16 @@ class CardReaderDetailViewModel @Inject constructor( NotConnectedState(onPrimaryActionClicked = ::onConnectBtnClicked, onLearnMoreClicked = ::onLearnMoreClicked) } + private fun handleReconnectingState() { + viewState.value = ViewState.ReconnectingState( + onCancelClicked = ::onCancelReconnectionClicked + ) + } + + private fun onCancelReconnectionClicked() { + cardReaderManager.cancelReconnection() + } + private fun cancelConnectedScopeJobs() { if (::softwareUpdateAvailabilityJob.isInitialized) softwareUpdateAvailabilityJob.cancel() if (::batteryStatusUpdateJob.isInitialized) batteryStatusUpdateJob.cancel() @@ -307,6 +321,16 @@ class CardReaderDetailViewModel @Inject constructor( } object Loading : ViewState() + + data class ReconnectingState( + val onCancelClicked: (() -> Unit), + ) : ViewState() { + val headerLabel = UiStringRes(R.string.card_reader_detail_reconnecting_header) + + @DrawableRes + val illustration = R.drawable.img_card_reader_not_connected + val cancelBtnLabel = UiStringRes(R.string.card_reader_detail_reconnecting_cancel) + } } } diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderFacade.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderFacade.kt index 6e0f1dfaa94e..4860757cda97 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderFacade.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderFacade.kt @@ -49,6 +49,10 @@ class WooPosCardReaderFacade @Inject constructor( cardReaderManager.disconnectReader() } + fun cancelReconnection() { + cardReaderManager.cancelReconnection() + } + @Suppress("DEPRECATION") private fun startActivity(intent: Intent) { val options = ActivityOptionsCompat.makeCustomAnimation( diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbar.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbar.kt index 8317d2da3e2a..57d7532baa9a 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbar.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbar.kt @@ -309,15 +309,11 @@ private fun CardReaderStatusButton( when (status) { WooPosCardReaderStatus.Connected -> WooPosTheme.colors.success WooPosCardReaderStatus.NotConnected -> WooPosTheme.colors.alert + WooPosCardReaderStatus.Reconnecting -> WooPosTheme.colors.alert } } - val title = stringResource( - id = when (state) { - WooPosCardReaderStatus.Connected -> WooPosCardReaderStatus.Connected.title - WooPosCardReaderStatus.NotConnected -> WooPosCardReaderStatus.NotConnected.title - } - ) + val title = stringResource(id = state.title) val borderColor by transition.animateColor( transitionSpec = { tween(durationMillis = animationDuration) }, @@ -326,6 +322,7 @@ private fun CardReaderStatusButton( when (status) { WooPosCardReaderStatus.Connected -> Color.Transparent WooPosCardReaderStatus.NotConnected -> MaterialTheme.colorScheme.primary + WooPosCardReaderStatus.Reconnecting -> WooPosTheme.colors.alert } } @@ -408,6 +405,9 @@ private fun getToolbarAccessibilityLabels( WooPosCardReaderStatus.NotConnected -> stringResource( id = R.string.woopos_floating_toolbar_card_reader_not_connected_status_content_description ) + WooPosCardReaderStatus.Reconnecting -> stringResource( + id = R.string.woopos_reader_reconnecting + ) } val floatingToolbarMenuOverlayContentDescription = when (menuCardDisabled) { true -> { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarState.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarState.kt index 87fe185b1fde..eddef459c231 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarState.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarState.kt @@ -11,6 +11,7 @@ data class WooPosHomeFloatingToolbarState( sealed class WooPosCardReaderStatus(@StringRes val title: Int) { data object NotConnected : WooPosCardReaderStatus(title = R.string.woopos_reader_disconnected) data object Connected : WooPosCardReaderStatus(title = R.string.woopos_reader_connected) + data object Reconnecting : WooPosCardReaderStatus(title = R.string.woopos_reader_reconnecting) } sealed class Menu { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt index a0ea933f711e..38a51be68a0d 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModel.kt @@ -11,6 +11,7 @@ import com.woocommerce.android.cardreader.connection.CardReaderStatus import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected +import com.woocommerce.android.cardreader.connection.CardReaderStatus.Reconnecting import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade import com.woocommerce.android.ui.woopos.home.ChildToParentEvent import com.woocommerce.android.ui.woopos.home.WooPosChildrenToParentEventSender @@ -128,12 +129,17 @@ class WooPosHomeFloatingToolbarViewModel @Inject constructor( cardReaderFacade.connectToReader() } } + + WooPosHomeFloatingToolbarState.WooPosCardReaderStatus.Reconnecting -> { + cardReaderFacade.cancelReconnection() + } } } private fun mapCardReaderStatusToUiState(status: CardReaderStatus) = when (status) { is Connected -> WooPosHomeFloatingToolbarState.WooPosCardReaderStatus.Connected is NotConnected, Connecting -> WooPosHomeFloatingToolbarState.WooPosCardReaderStatus.NotConnected + Reconnecting -> WooPosHomeFloatingToolbarState.WooPosCardReaderStatus.Reconnecting } private val toolbarMenuItems by lazy { diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt index 58ca9e213fe2..4c12d5d66cf2 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/home/totals/WooPosTotalsViewModel.kt @@ -9,6 +9,7 @@ import com.woocommerce.android.WooException import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connected import com.woocommerce.android.cardreader.connection.CardReaderStatus.Connecting import com.woocommerce.android.cardreader.connection.CardReaderStatus.NotConnected +import com.woocommerce.android.cardreader.connection.CardReaderStatus.Reconnecting import com.woocommerce.android.model.Order import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund import com.woocommerce.android.ui.payments.cardreader.payment.controller.CardReaderPaymentController @@ -122,6 +123,10 @@ class WooPosTotalsViewModel @Inject constructor( cancelPaymentAction() } + Reconnecting -> { + // We start payment right away so this state not worth handling + } + is Connected -> { val state = uiState.value if (state !is WooPosTotalsViewState.Checkout) return@collect diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/hardware/cardreader/WooPosSettingsHardwareCardReaderViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/hardware/cardreader/WooPosSettingsHardwareCardReaderViewModel.kt index 014390df8ab7..b1c29fc71cdd 100644 --- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/hardware/cardreader/WooPosSettingsHardwareCardReaderViewModel.kt +++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/settings/details/hardware/cardreader/WooPosSettingsHardwareCardReaderViewModel.kt @@ -115,6 +115,11 @@ class WooPosSettingsHardwareCardReaderViewModel @Inject constructor( currentSoftwareUpdateAvailable = false WooPosSettingsHardwareCardReaderUiState.Disconnected } + + CardReaderStatus.Reconnecting -> { + // Keep current state while SDK attempts to reconnect + _uiState.value + } } } } diff --git a/WooCommerce/src/main/res/values/strings.xml b/WooCommerce/src/main/res/values/strings.xml index ac0e6093b3d0..7df88370f621 100644 --- a/WooCommerce/src/main/res/values/strings.xml +++ b/WooCommerce/src/main/res/values/strings.xml @@ -1634,6 +1634,8 @@ Card Reader Detail --> Connect your card reader + Reconnecting to card reader… + Cancel reconnection <a href=\'\'>Learn more</a> about accepting mobile payments and ordering card readers Make sure card reader is charged Turn card reader on and place it next to mobile device @@ -3590,6 +3592,7 @@ Reader connected Connect your reader + Reconnecting… Check out Remove %s from cart Product %s, Price %s diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModelTest.kt index ae04d3e00d00..c8787d222305 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/payments/cardreader/detail/CardReaderDetailViewModelTest.kt @@ -22,6 +22,7 @@ import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailVie import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.ConnectedState import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.Loading import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.NotConnectedState +import com.woocommerce.android.ui.payments.cardreader.detail.CardReaderDetailViewModel.ViewState.ReconnectingState import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.STRIPE_EXTENSION_GATEWAY import com.woocommerce.android.ui.payments.cardreader.onboarding.PluginType.WOOCOMMERCE_PAYMENTS @@ -620,6 +621,33 @@ class CardReaderDetailViewModelTest : BaseUnitTest() { .isEqualTo(AppUrls.STRIPE_LEARN_MORE_ABOUT_PAYMENTS) } + @Test + fun `given reconnecting state, when view model init, then should emit reconnecting view state`() { + // GIVEN + val status = MutableStateFlow(CardReaderStatus.Reconnecting) + whenever(cardReaderManager.readerStatus).thenReturn(status) + + // WHEN + val viewModel = createViewModel() + + // THEN + assertThat(viewModel.viewStateData.value).isInstanceOf(ReconnectingState::class.java) + } + + @Test + fun `given reconnecting state, when cancel clicked, then should call cancelReconnection`() { + // GIVEN + val status = MutableStateFlow(CardReaderStatus.Reconnecting) + whenever(cardReaderManager.readerStatus).thenReturn(status) + val viewModel = createViewModel() + + // WHEN + (viewModel.viewStateData.value as ReconnectingState).onCancelClicked.invoke() + + // THEN + verify(cardReaderManager).cancelReconnection() + } + private fun verifyNotConnectedState(viewModel: CardReaderDetailViewModel) { val state = viewModel.viewStateData.value as NotConnectedState assertThat(state.headerLabel) diff --git a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModelTest.kt b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModelTest.kt index 597b5bc47dae..2ddf5fdaf9fe 100644 --- a/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModelTest.kt +++ b/WooCommerce/src/test/kotlin/com/woocommerce/android/ui/woopos/home/toolbar/WooPosHomeFloatingToolbarViewModelTest.kt @@ -257,6 +257,31 @@ class WooPosHomeFloatingToolbarViewModelTest { assertThat(viewModel.state.value.menu).isEqualTo(WooPosHomeFloatingToolbarState.Menu.Hidden) } + @Test + fun `given card reader status is Reconnecting, when initialized, then state should be Reconnecting`() = runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(MutableStateFlow(CardReaderStatus.Reconnecting)) + val viewModel = createViewModel() + + // THEN + assertThat(viewModel.state.value.cardReaderStatus) + .isEqualTo(WooPosHomeFloatingToolbarState.WooPosCardReaderStatus.Reconnecting) + } + + @Test + fun `given card reader status is Reconnecting, when OnCardReaderStatusClicked, then cancelReconnection should be called`() = + runTest { + // GIVEN + whenever(cardReaderFacade.readerStatus).thenReturn(MutableStateFlow(CardReaderStatus.Reconnecting)) + val viewModel = createViewModel() + + // WHEN + viewModel.onUiEvent(WooPosHomeFloatingToolbarUIEvent.OnCardReaderStatusClicked) + + // THEN + verify(cardReaderFacade).cancelReconnection() + } + private fun createViewModel() = WooPosHomeFloatingToolbarViewModel( cardReaderFacade, childrenToParentEventSender, diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManager.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManager.kt index b78dac62d069..4f11159f8b89 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManager.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManager.kt @@ -47,6 +47,7 @@ interface CardReaderManager { fun startConnectionToReader(cardReader: CardReader, locationId: String) suspend fun disconnectReader(): Boolean + fun cancelReconnection() suspend fun collectPayment(paymentInfo: PaymentInfo): Flow suspend fun refundInteracPayment( diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt index 3ebd9a418695..7be658f54a60 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/CardReaderManagerFactory.kt @@ -42,7 +42,7 @@ object CardReaderManagerFactory { UpdateErrorMapper(batteryLevelProvider), terminalListener ) - val tapToPayReaderListener = TapToPayReaderListenerImpl(logWrapper) + val tapToPayReaderListener = TapToPayReaderListenerImpl(logWrapper, terminalListener) val cardReaderConfigFactory = CardReaderConfigFactory() val paymentUtils = PaymentUtils(logWrapper) diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/connection/CardReaderStatus.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/connection/CardReaderStatus.kt index e1edf87595e8..8edaee24b334 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/connection/CardReaderStatus.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/connection/CardReaderStatus.kt @@ -13,4 +13,5 @@ sealed class CardReaderStatus { } data class Connected(val cardReader: CardReader) : CardReaderStatus() data object Connecting : CardReaderStatus() + data object Reconnecting : CardReaderStatus() } diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/CardReaderManagerImpl.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/CardReaderManagerImpl.kt index 6b7bfe9efb5d..0d555d206001 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/CardReaderManagerImpl.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/CardReaderManagerImpl.kt @@ -103,6 +103,11 @@ internal class CardReaderManagerImpl( return connectionManager.disconnectReader() } + override fun cancelReconnection() { + if (!terminal.isInitialized()) error("Terminal not initialized") + connectionManager.cancelReconnection() + } + override suspend fun collectPayment(paymentInfo: PaymentInfo): Flow { resetBluetoothDisplayMessage() return paymentManager.acceptPayment(paymentInfo) diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/BluetoothReaderListenerImpl.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/BluetoothReaderListenerImpl.kt index 87f5c67edafb..a0ce1ba2d68f 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/BluetoothReaderListenerImpl.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/BluetoothReaderListenerImpl.kt @@ -1,15 +1,18 @@ package com.woocommerce.android.cardreader.internal.connection +import com.stripe.stripeterminal.external.callable.Callback import com.stripe.stripeterminal.external.callable.Cancelable import com.stripe.stripeterminal.external.callable.MobileReaderListener import com.stripe.stripeterminal.external.models.BatteryStatus import com.stripe.stripeterminal.external.models.DisconnectReason +import com.stripe.stripeterminal.external.models.Reader import com.stripe.stripeterminal.external.models.ReaderDisplayMessage import com.stripe.stripeterminal.external.models.ReaderEvent import com.stripe.stripeterminal.external.models.ReaderInputOptions import com.stripe.stripeterminal.external.models.ReaderSoftwareUpdate import com.stripe.stripeterminal.external.models.TerminalException import com.woocommerce.android.cardreader.LogWrapper +import com.woocommerce.android.cardreader.connection.CardReaderImpl import com.woocommerce.android.cardreader.connection.CardReaderStatus import com.woocommerce.android.cardreader.connection.event.BluetoothCardReaderMessages import com.woocommerce.android.cardreader.connection.event.BluetoothCardReaderMessages.CardReaderNoMessage @@ -45,6 +48,7 @@ internal class BluetoothReaderListenerImpl( val batteryStatusEvents = _batteryStatusEvents.asStateFlow() var cancelUpdateAction: Cancelable? = null + var cancelReconnectAction: Cancelable? = null override fun onFinishInstallingUpdate(update: ReaderSoftwareUpdate?, e: TerminalException?) { logWrapper.d(LOG_TAG, "onFinishInstallingUpdate: $update $e") @@ -117,6 +121,28 @@ internal class BluetoothReaderListenerImpl( terminalListenerImpl.updateReaderStatus(CardReaderStatus.NotConnected(errorCode = errorCode)) } + override fun onReaderReconnectFailed(reader: Reader) { + logWrapper.d(LOG_TAG, "onReaderReconnectFailed") + cancelReconnectAction = null + terminalListenerImpl.updateReaderStatus(CardReaderStatus.NotConnected()) + } + + override fun onReaderReconnectStarted( + reader: Reader, + cancelReconnect: Cancelable, + reason: DisconnectReason + ) { + logWrapper.d(LOG_TAG, "onReaderReconnectStarted: reason=$reason") + cancelReconnectAction = cancelReconnect + terminalListenerImpl.updateReaderStatus(CardReaderStatus.Reconnecting) + } + + override fun onReaderReconnectSucceeded(reader: Reader) { + logWrapper.d(LOG_TAG, "onReaderReconnectSucceeded") + cancelReconnectAction = null + terminalListenerImpl.updateReaderStatus(CardReaderStatus.Connected(CardReaderImpl(reader))) + } + fun resetConnectionState() { _updateStatusEvents.value = SoftwareUpdateStatus.Unknown _updateAvailabilityEvents.value = SoftwareUpdateAvailability.NotAvailable @@ -125,4 +151,9 @@ internal class BluetoothReaderListenerImpl( fun resetDisplayMessage() { _displayMessagesEvents.value = CardReaderNoMessage } + + fun cancelReconnection(callback: Callback) { + cancelReconnectAction?.cancel(callback) + cancelReconnectAction = null + } } diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/ConnectionManager.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/ConnectionManager.kt index b3a82d2cc3f8..4f3526e2961b 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/ConnectionManager.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/ConnectionManager.kt @@ -163,6 +163,20 @@ internal class ConnectionManager( }) } + fun cancelReconnection() { + val callback = object : Callback { + override fun onFailure(e: TerminalException) { + updateReaderStatus(CardReaderStatus.NotConnected()) + } + + override fun onSuccess() { + updateReaderStatus(CardReaderStatus.NotConnected()) + } + } + bluetoothReaderListener.cancelReconnection(callback) + tapToPayReaderListener.cancelReconnection(callback) + } + private fun startStateResettingJobIfNeeded(currentStatus: CardReaderStatus) { if (currentStatus !is CardReaderStatus.Connecting) return diff --git a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/TapToPayReaderListenerImpl.kt b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/TapToPayReaderListenerImpl.kt index b5d08f4d6137..2c3c059088fa 100644 --- a/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/TapToPayReaderListenerImpl.kt +++ b/libs/cardreader/src/main/java/com/woocommerce/android/cardreader/internal/connection/TapToPayReaderListenerImpl.kt @@ -1,21 +1,30 @@ package com.woocommerce.android.cardreader.internal.connection +import com.stripe.stripeterminal.external.callable.Callback import com.stripe.stripeterminal.external.callable.Cancelable import com.stripe.stripeterminal.external.callable.TapToPayReaderListener import com.stripe.stripeterminal.external.models.DisconnectReason import com.stripe.stripeterminal.external.models.Reader import com.woocommerce.android.cardreader.LogWrapper +import com.woocommerce.android.cardreader.connection.CardReaderImpl +import com.woocommerce.android.cardreader.connection.CardReaderStatus import com.woocommerce.android.cardreader.internal.LOG_TAG -class TapToPayReaderListenerImpl( - private val logWrapper: LogWrapper +internal class TapToPayReaderListenerImpl( + private val logWrapper: LogWrapper, + private val terminalListenerImpl: TerminalListenerImpl ) : TapToPayReaderListener { + var cancelReconnectAction: Cancelable? = null + override fun onDisconnect(reason: DisconnectReason) { - logWrapper.d(LOG_TAG, "onDisconnect") + logWrapper.d(LOG_TAG, "onDisconnect: reason=$reason") + terminalListenerImpl.updateReaderStatus(CardReaderStatus.NotConnected()) } override fun onReaderReconnectFailed(reader: Reader) { logWrapper.d(LOG_TAG, "onReaderReconnectFailed") + cancelReconnectAction = null + terminalListenerImpl.updateReaderStatus(CardReaderStatus.NotConnected()) } override fun onReaderReconnectStarted( @@ -23,10 +32,19 @@ class TapToPayReaderListenerImpl( cancelReconnect: Cancelable, reason: DisconnectReason ) { - logWrapper.d(LOG_TAG, "onReaderReconnectStarted") + logWrapper.d(LOG_TAG, "onReaderReconnectStarted: reason=$reason") + cancelReconnectAction = cancelReconnect + terminalListenerImpl.updateReaderStatus(CardReaderStatus.Reconnecting) } override fun onReaderReconnectSucceeded(reader: Reader) { logWrapper.d(LOG_TAG, "onReaderReconnectSucceeded") + cancelReconnectAction = null + terminalListenerImpl.updateReaderStatus(CardReaderStatus.Connected(CardReaderImpl(reader))) + } + + fun cancelReconnection(callback: Callback) { + cancelReconnectAction?.cancel(callback) + cancelReconnectAction = null } }