diff --git a/WooCommerce/src/main/AndroidManifest.xml b/WooCommerce/src/main/AndroidManifest.xml
index 366945a42b1..aece3b5a748 100644
--- a/WooCommerce/src/main/AndroidManifest.xml
+++ b/WooCommerce/src/main/AndroidManifest.xml
@@ -163,6 +163,9 @@
+
{
+ parentFragmentManager.setFragmentResult(
+ WooPosCardReaderActivity.WOO_POS_CARD_CONNECTION_REQUEST_KEY,
+ Bundle(),
+ )
+ }
is CardReaderConnectEvent.ShowToast ->
ToastUtils.showToast(requireContext(), getString(event.message))
is CardReaderConnectEvent.ShowToastString ->
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectEvent.kt
index 67ac825a809..87de39f23d7 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectEvent.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/connect/CardReaderConnectEvent.kt
@@ -46,4 +46,6 @@ sealed class CardReaderConnectEvent : MultiLiveEvent.Event() {
data class OpenWPComWebView(val url: String) : CardReaderConnectEvent()
data class OpenGenericWebView(val url: String) : CardReaderConnectEvent()
+
+ data object PopBackStackForWooPOS : CardReaderConnectEvent()
}
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 f3ae59ab1e9..4a15d46862c 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
@@ -35,6 +35,7 @@ import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectE
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenLocationSettings
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenPermissionsSettings
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.OpenWPComWebView
+import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.PopBackStackForWooPOS
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestBluetoothRuntimePermissions
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestEnableBluetooth
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectEvent.RequestLocationPermissions
@@ -57,6 +58,7 @@ import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectV
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectViewState.MissingMerchantAddressError
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectViewState.MultipleExternalReadersFoundState
import com.woocommerce.android.ui.payments.cardreader.connect.CardReaderConnectViewState.ScanningFailedState
+import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderOnboardingChecker
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.BUILT_IN
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.EXTERNAL
@@ -281,6 +283,7 @@ class CardReaderConnectViewModel @Inject constructor(
Unit
}
}
+
CardReaderStatus.Connecting -> {
connectionStarted = true
viewState.value = provideConnectingState()
@@ -309,13 +312,16 @@ class CardReaderConnectViewModel @Inject constructor(
viewState.value = provideScanningState()
}
}
+
is ReadersFound -> {
tracker.trackReadersDiscovered(discoveryEvent.list.size)
onReadersFound(discoveryEvent)
}
+
Succeeded -> {
// noop
}
+
is Failed -> {
tracker.trackReaderDiscoveryFailed(discoveryEvent.msg)
WooLog.e(WooLog.T.CARD_READER, "Scanning failed: ${discoveryEvent.msg}")
@@ -330,6 +336,7 @@ class CardReaderConnectViewModel @Inject constructor(
triggerEvent(ShowToast(R.string.card_reader_detail_connected_update_failed))
exitFlow(connected = false)
}
+
CardReaderUpdateViewModel.UpdateResult.SUCCESS -> {
// noop
}
@@ -400,6 +407,7 @@ class CardReaderConnectViewModel @Inject constructor(
::onCancelClicked,
::onLearnMoreClicked
)
+
EXTERNAL -> ExternalReaderScanningState(
::onCancelClicked,
::onLearnMoreClicked
@@ -424,6 +432,7 @@ class CardReaderConnectViewModel @Inject constructor(
tracker.trackFetchingLocationSucceeded()
cardReaderManager.startConnectionToReader(cardReader, result.locationId)
}
+
is CardReaderLocationRepository.LocationIdFetchingResult.Error -> {
handleLocationFetchingError(result)
}
@@ -453,10 +462,12 @@ class CardReaderConnectViewModel @Inject constructor(
}
)
}
+
is CardReaderLocationRepository.LocationIdFetchingResult.Error.InvalidPostalCode -> {
trackLocationFailureFetching("Invalid Postal Code")
viewState.value = InvalidMerchantAddressPostCodeError(::restartFlow)
}
+
is CardReaderLocationRepository.LocationIdFetchingResult.Error.Other -> {
trackLocationFailureFetching(result.error)
onReaderConnectionFailed()
@@ -510,7 +521,13 @@ class CardReaderConnectViewModel @Inject constructor(
private fun exitFlow(connected: Boolean) {
if (!connected) {
- triggerEvent(ExitWithResult(false))
+ when (arguments.cardReaderFlowParam) {
+ is CardReaderFlowParam.CardReadersHub,
+ is CardReaderFlowParam.PaymentOrRefund.Payment,
+ is CardReaderFlowParam.PaymentOrRefund.Refund -> triggerEvent(ExitWithResult(false))
+
+ CardReaderFlowParam.WooPosConnection -> triggerEvent(PopBackStackForWooPOS)
+ }
} else {
triggerEvent(ShowCardReaderTutorial(arguments.cardReaderFlowParam, arguments.cardReaderType))
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingFragment.kt
index 2d511e2167f..2251864a406 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingFragment.kt
@@ -413,6 +413,9 @@ sealed class CardReaderFlowParam : Parcelable {
}
}
+ @Parcelize
+ data object WooPosConnection : CardReaderFlowParam()
+
sealed class PaymentOrRefund : CardReaderFlowParam() {
abstract val orderId: Long
@@ -426,6 +429,7 @@ sealed class CardReaderFlowParam : Parcelable {
ORDER,
ORDER_CREATION,
TRY_TAP_TO_PAY,
+ WOO_POS,
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModel.kt
index 079e5a5740a..cc141c638a5 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModel.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/onboarding/CardReaderOnboardingViewModel.kt
@@ -438,6 +438,9 @@ class CardReaderOnboardingViewModel @Inject constructor(
CardReaderOnboardingEvent.ContinueToConnection(params, requireNotNull(arguments.cardReaderType))
)
}
+ is CardReaderFlowParam.WooPosConnection -> {
+ error("Unsupported flow param: $params")
+ }
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/statuschecker/CardReaderStatusCheckerViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/statuschecker/CardReaderStatusCheckerViewModel.kt
index 68d0f50228a..936ef06809b 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/statuschecker/CardReaderStatusCheckerViewModel.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/statuschecker/CardReaderStatusCheckerViewModel.kt
@@ -69,6 +69,7 @@ class CardReaderStatusCheckerViewModel
handleOnboardingStatus(param)
}
}
+ is CardReaderFlowParam.WooPosConnection -> handleOnboardingStatus(param)
}
}
@@ -87,6 +88,7 @@ class CardReaderStatusCheckerViewModel
triggerEvent(StatusCheckerEvent.NavigateToWelcome(param, arguments.cardReaderType))
}
}
+
else -> triggerEvent(
StatusCheckerEvent.NavigateToOnboarding(
CardReaderOnboardingParams.Failed(param, state),
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/tutorial/CardReaderTutorialDialogFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/tutorial/CardReaderTutorialDialogFragment.kt
index 64001b78fc5..a133fe5aada 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/tutorial/CardReaderTutorialDialogFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/cardreader/tutorial/CardReaderTutorialDialogFragment.kt
@@ -15,6 +15,7 @@ import com.woocommerce.android.extensions.navigateSafely
import com.woocommerce.android.ui.payments.PaymentsBaseDialogFragment
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.BUILT_IN
+import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderActivity
import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
@@ -59,6 +60,12 @@ class CardReaderTutorialDialogFragment : PaymentsBaseDialogFragment(R.layout.car
private fun navigateNext() {
when (val param = args.cardReaderFlowParam) {
is CardReaderFlowParam.CardReadersHub -> findNavController().popBackStack()
+ is CardReaderFlowParam.WooPosConnection -> {
+ parentFragmentManager.setFragmentResult(
+ WooPosCardReaderActivity.WOO_POS_CARD_CONNECTION_REQUEST_KEY,
+ Bundle(),
+ )
+ }
is CardReaderFlowParam.PaymentOrRefund -> {
val action = CardReaderTutorialDialogFragmentDirections
.actionCardReaderTutorialDialogFragmentToCardReaderPaymentDialogFragment(param, args.cardReaderType)
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/hub/PaymentsHubViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/hub/PaymentsHubViewModel.kt
index 75bf2bbdc4b..2d8d4598a30 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/hub/PaymentsHubViewModel.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/hub/PaymentsHubViewModel.kt
@@ -507,6 +507,7 @@ class PaymentsHubViewModel @Inject constructor(
is PaymentOrRefund -> {
// no-op
}
+ is CardReaderFlowParam.WooPosConnection -> error("Unsupported card reader flow param $params")
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodEvent.kt
index a984704284b..2de1cc5b53b 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodEvent.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodEvent.kt
@@ -36,6 +36,8 @@ data class NavigateToOrderDetails(
val orderId: Long
) : MultiLiveEvent.Event()
+object ReturnResultToWooPos : MultiLiveEvent.Event()
+
data class NavigateToTapToPaySummary(
val order: Order
) : MultiLiveEvent.Event()
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodFragment.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodFragment.kt
index 9d301e498e0..bfe9fb3ed06 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodFragment.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodFragment.kt
@@ -34,6 +34,8 @@ import com.woocommerce.android.ui.payments.methodselection.SelectPaymentMethodVi
import com.woocommerce.android.ui.payments.methodselection.SelectPaymentMethodViewState.Success
import com.woocommerce.android.ui.payments.scantopay.ScanToPayDialogFragment
import com.woocommerce.android.ui.payments.taptopay.summary.TapToPaySummaryFragment
+import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderActivity
+import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderPaymentResult
import com.woocommerce.android.util.ChromeCustomTabUtils
import com.woocommerce.android.util.UiHelpers
import com.woocommerce.android.viewmodel.MultiLiveEvent.Event.ShowDialog
@@ -61,8 +63,12 @@ class SelectPaymentMethodFragment : BaseFragment(R.layout.fragment_select_paymen
savedInstanceState: Bundle?
): View {
_binding = FragmentSelectPaymentMethodBinding.inflate(inflater, container, false)
- setupToolbar()
- return binding.root
+ return if (viewModel.displayUi) {
+ setupToolbar()
+ binding.root
+ } else {
+ View(requireContext())
+ }
}
private fun setupToolbar() {
@@ -274,6 +280,18 @@ class SelectPaymentMethodFragment : BaseFragment(R.layout.fragment_select_paymen
)
)
}
+
+ is ReturnResultToWooPos -> {
+ parentFragmentManager.setFragmentResult(
+ WooPosCardReaderActivity.WOO_POS_CARD_PAYMENT_REQUEST_KEY,
+ Bundle().apply {
+ putParcelable(
+ WooPosCardReaderActivity.WOO_POS_CARD_PAYMENT_RESULT_KEY,
+ WooPosCardReaderPaymentResult.Success,
+ )
+ }
+ )
+ }
}
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodViewModel.kt
index 37503aad91f..3a5ff92f5e1 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodViewModel.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/payments/methodselection/SelectPaymentMethodViewModel.kt
@@ -27,7 +27,9 @@ import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowP
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.ORDER_CREATION
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.SIMPLE
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.TRY_TAP_TO_PAY
+import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.WOO_POS
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.PaymentOrRefund.Refund
+import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam.WooPosConnection
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.BUILT_IN
import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType.EXTERNAL
import com.woocommerce.android.ui.payments.cardreader.payment.CardReaderPaymentCollectibilityChecker
@@ -86,6 +88,9 @@ class SelectPaymentMethodViewModel @Inject constructor(
private val cardReaderPaymentFlowParam
get() = navArgs.cardReaderFlowParam as Payment
+ val displayUi: Boolean
+ get() = !isWooPOSPaymentFlow()
+
init {
checkStatus()
}
@@ -97,7 +102,6 @@ class SelectPaymentMethodViewModel @Inject constructor(
when (param) {
is Payment -> {
launch {
- // stay on screen
cardReaderTrackingInfoKeeper.setCountry(
wooCommerceStore.getStoreCountryCode(selectedSite.get())
)
@@ -109,7 +113,10 @@ class SelectPaymentMethodViewModel @Inject constructor(
cardReaderTrackingInfoKeeper.setCurrency(order.currency)
}
- showPaymentState()
+ when (param.paymentType) {
+ SIMPLE, ORDER, ORDER_CREATION, TRY_TAP_TO_PAY -> showPaymentState()
+ WOO_POS -> onBtReaderClicked()
+ }
}
Unit
}
@@ -117,6 +124,8 @@ class SelectPaymentMethodViewModel @Inject constructor(
is Refund -> triggerEvent(NavigateToCardReaderRefundFlow(param, EXTERNAL))
}
}
+
+ is WooPosConnection -> error("Unsupported card reader flow param: $param")
}
}
@@ -244,6 +253,7 @@ class SelectPaymentMethodViewModel @Inject constructor(
val messageIdForPaymentType = when (cardReaderPaymentFlowParam.paymentType) {
SIMPLE, TRY_TAP_TO_PAY -> R.string.simple_payments_cash_dlg_message
ORDER, ORDER_CREATION -> R.string.existing_order_cash_dlg_message
+ WOO_POS -> error("Unsupported card reader flow param: $cardReaderPaymentFlowParam")
}
triggerEvent(
MultiLiveEvent.Event.ShowDialog(
@@ -443,6 +453,7 @@ class SelectPaymentMethodViewModel @Inject constructor(
SIMPLE -> NavigateBackToHub(CardReadersHub())
TRY_TAP_TO_PAY -> NavigateToTapToPaySummary(order.first())
ORDER, ORDER_CREATION -> NavigateBackToOrderList(order.first())
+ WOO_POS -> ReturnResultToWooPos
}
)
}
@@ -452,7 +463,8 @@ class SelectPaymentMethodViewModel @Inject constructor(
SIMPLE -> AnalyticsTracker.VALUE_SIMPLE_PAYMENTS_FLOW
ORDER -> AnalyticsTracker.VALUE_ORDER_PAYMENTS_FLOW
TRY_TAP_TO_PAY -> AnalyticsTracker.VALUE_TTP_TRY_PAYMENT_FLOW
- Payment.PaymentType.ORDER_CREATION -> AnalyticsTracker.VALUE_ORDER_CREATION_PAYMENTS_FLOW
+ ORDER_CREATION -> AnalyticsTracker.VALUE_ORDER_CREATION_PAYMENTS_FLOW
+ WOO_POS -> AnalyticsTracker.VALUE_WOO_POS_PAYMENTS_FLOW
}
private fun onLearnMoreIppClicked() {
@@ -471,6 +483,10 @@ class SelectPaymentMethodViewModel @Inject constructor(
return currencyFormatter.formatCurrency(total, currencyCode)
}
+ private fun isWooPOSPaymentFlow() = with(navArgs.cardReaderFlowParam) {
+ this is Payment && paymentType == WOO_POS
+ }
+
companion object {
private const val DELAY_MS = 1L
const val UTM_CAMPAIGN = "feature_announcement_card"
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt
new file mode 100644
index 00000000000..9555e130e85
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderActivity.kt
@@ -0,0 +1,108 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import androidx.activity.viewModels
+import androidx.appcompat.app.AppCompatActivity
+import androidx.navigation.fragment.NavHostFragment
+import com.woocommerce.android.R
+import com.woocommerce.android.extensions.parcelable
+import com.woocommerce.android.ui.payments.cardreader.statuschecker.CardReaderStatusCheckerDialogFragmentArgs
+import com.woocommerce.android.ui.payments.methodselection.SelectPaymentMethodFragmentArgs
+import dagger.hilt.android.AndroidEntryPoint
+
+@AndroidEntryPoint
+class WooPosCardReaderActivity : AppCompatActivity(R.layout.activity_woo_pos_card_reader) {
+ val viewModel: WooPosCardReaderViewModel by viewModels()
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val navHostFragment = supportFragmentManager.findFragmentById(
+ R.id.woopos_card_reader_nav_host_fragment
+ ) as NavHostFragment
+
+ observeEvents(navHostFragment)
+ observeResult(navHostFragment)
+ }
+
+ private fun observeResult(navHostFragment: NavHostFragment) {
+ navHostFragment.childFragmentManager.setFragmentResultListener(
+ WOO_POS_CARD_PAYMENT_REQUEST_KEY,
+ this
+ ) { requestKey, bundle ->
+ when (requestKey) {
+ WOO_POS_CARD_PAYMENT_REQUEST_KEY -> {
+ val result = bundle.parcelable(WOO_POS_CARD_PAYMENT_RESULT_KEY)
+ setResult(
+ RESULT_OK,
+ Intent().apply { putExtra(WOO_POS_CARD_PAYMENT_RESULT_KEY, result) }
+ )
+ finish()
+ }
+
+ else -> error("Unknown request key: $requestKey")
+ }
+ }
+
+ navHostFragment.childFragmentManager.setFragmentResultListener(
+ WOO_POS_CARD_CONNECTION_REQUEST_KEY,
+ this
+ ) { requestKey, _ ->
+ when (requestKey) {
+ WOO_POS_CARD_CONNECTION_REQUEST_KEY -> {
+ finish()
+ }
+
+ else -> error("Unknown request key: $requestKey")
+ }
+ }
+ }
+
+ private fun observeEvents(navHostFragment: NavHostFragment) {
+ viewModel.event.observe(this) { event ->
+ when (event) {
+ is WooPosCardReaderEvent.Connection -> {
+ val navController = navHostFragment.navController
+ val graph = navController.navInflater.inflate(R.navigation.nav_graph_payment_flow).apply {
+ setStartDestination(R.id.cardReaderStatusCheckerDialogFragment)
+ }
+ navController.setGraph(
+ graph,
+ CardReaderStatusCheckerDialogFragmentArgs(
+ cardReaderFlowParam = event.cardReaderFlowParam,
+ cardReaderType = event.cardReaderType,
+ ).toBundle()
+ )
+ }
+
+ is WooPosCardReaderEvent.Payment -> {
+ val navController = navHostFragment.navController
+ val graph = navController.navInflater.inflate(R.navigation.nav_graph_payment_flow)
+ navController.setGraph(
+ graph,
+ SelectPaymentMethodFragmentArgs(cardReaderFlowParam = event.cardReaderFlowParam).toBundle()
+ )
+ }
+ }
+ }
+ }
+
+ companion object {
+ const val WOO_POS_CARD_PAYMENT_REQUEST_KEY = "woo_pos_card_payment_request"
+ const val WOO_POS_CARD_CONNECTION_REQUEST_KEY = "woo_pos_card_connection_request"
+ const val WOO_POS_CARD_PAYMENT_RESULT_KEY = "woo_pos_card_payment_result"
+ internal const val WOO_POS_CARD_READER_MODE_KEY = "card_reader_connection_mode"
+
+ fun buildIntentForCardReaderConnection(context: Context) =
+ Intent(context, WooPosCardReaderActivity::class.java).apply {
+ putExtra(WOO_POS_CARD_READER_MODE_KEY, WooPosCardReaderMode.Connection)
+ }
+
+ fun buildIntentForPayment(context: Context, orderId: Long) =
+ Intent(context, WooPosCardReaderActivity::class.java).apply {
+ putExtra(WOO_POS_CARD_READER_MODE_KEY, WooPosCardReaderMode.Payment(orderId))
+ }
+ }
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderEvent.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderEvent.kt
new file mode 100644
index 00000000000..d6cc0704332
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderEvent.kt
@@ -0,0 +1,23 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderFlowParam
+import com.woocommerce.android.ui.payments.cardreader.onboarding.CardReaderType
+import com.woocommerce.android.viewmodel.MultiLiveEvent
+
+sealed class WooPosCardReaderEvent(
+ val cardReaderFlowParam: CardReaderFlowParam,
+ val cardReaderType: CardReaderType
+) : MultiLiveEvent.Event() {
+ data object Connection : WooPosCardReaderEvent(
+ cardReaderFlowParam = CardReaderFlowParam.WooPosConnection,
+ cardReaderType = CardReaderType.EXTERNAL
+ )
+
+ data class Payment(val orderId: Long) : WooPosCardReaderEvent(
+ cardReaderFlowParam = CardReaderFlowParam.PaymentOrRefund.Payment(
+ orderId = orderId,
+ paymentType = CardReaderFlowParam.PaymentOrRefund.Payment.PaymentType.WOO_POS
+ ),
+ cardReaderType = CardReaderType.EXTERNAL
+ )
+}
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
new file mode 100644
index 00000000000..09851f52ddd
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderFacade.kt
@@ -0,0 +1,53 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import android.content.Intent
+import androidx.activity.result.ActivityResultLauncher
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.appcompat.app.AppCompatActivity
+import androidx.lifecycle.DefaultLifecycleObserver
+import androidx.lifecycle.LifecycleOwner
+import com.woocommerce.android.cardreader.CardReaderManager
+import com.woocommerce.android.cardreader.connection.CardReaderStatus
+import com.woocommerce.android.util.parcelable
+import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.suspendCancellableCoroutine
+import javax.inject.Inject
+import kotlin.coroutines.Continuation
+import kotlin.coroutines.resume
+
+class WooPosCardReaderFacade @Inject constructor(cardReaderManager: CardReaderManager) : DefaultLifecycleObserver {
+ private var paymentContinuation: Continuation? = null
+ private var paymentResultLauncher: ActivityResultLauncher? = null
+ private var activity: AppCompatActivity? = null
+
+ val readerStatus: Flow = cardReaderManager.readerStatus
+
+ override fun onCreate(owner: LifecycleOwner) {
+ activity = owner as AppCompatActivity
+ paymentResultLauncher = activity!!.registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult()
+ ) { result ->
+ val paymentResult = result.data!!.parcelable(
+ WooPosCardReaderActivity.WOO_POS_CARD_PAYMENT_RESULT_KEY
+ )
+ paymentContinuation?.resume(paymentResult!!)
+ }
+ }
+
+ override fun onDestroy(owner: LifecycleOwner) {
+ activity = null
+ paymentContinuation = null
+ paymentResultLauncher = null
+ }
+
+ fun connectToReader() {
+ activity!!.startActivity(WooPosCardReaderActivity.buildIntentForCardReaderConnection(activity!!))
+ }
+
+ suspend fun collectPayment(orderId: Long): WooPosCardReaderPaymentResult {
+ return suspendCancellableCoroutine { continuation ->
+ paymentContinuation = continuation
+ paymentResultLauncher!!.launch(WooPosCardReaderActivity.buildIntentForPayment(activity!!, orderId))
+ }
+ }
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderMode.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderMode.kt
new file mode 100644
index 00000000000..7f57fb1a874
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderMode.kt
@@ -0,0 +1,10 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+internal sealed class WooPosCardReaderMode : Parcelable {
+ data object Connection : WooPosCardReaderMode()
+ data class Payment(val orderId: Long) : WooPosCardReaderMode()
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderPaymentResult.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderPaymentResult.kt
new file mode 100644
index 00000000000..1266ad8766a
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderPaymentResult.kt
@@ -0,0 +1,10 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+sealed class WooPosCardReaderPaymentResult : Parcelable {
+ data object Success : WooPosCardReaderPaymentResult()
+ data object Failure : WooPosCardReaderPaymentResult()
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderViewModel.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderViewModel.kt
new file mode 100644
index 00000000000..adcfa5692e8
--- /dev/null
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cardreader/WooPosCardReaderViewModel.kt
@@ -0,0 +1,70 @@
+package com.woocommerce.android.ui.woopos.cardreader
+
+import android.util.Log
+import androidx.lifecycle.SavedStateHandle
+import com.woocommerce.android.R
+import com.woocommerce.android.cardreader.config.CardReaderConfigForSupportedCountry
+import com.woocommerce.android.model.Order
+import com.woocommerce.android.tools.SelectedSite
+import com.woocommerce.android.ui.orders.creation.OrderCreateEditRepository
+import com.woocommerce.android.ui.payments.cardreader.CardReaderCountryConfigProvider
+import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderActivity.Companion.WOO_POS_CARD_READER_MODE_KEY
+import com.woocommerce.android.viewmodel.ResourceProvider
+import com.woocommerce.android.viewmodel.ScopedViewModel
+import dagger.hilt.android.lifecycle.HiltViewModel
+import kotlinx.coroutines.launch
+import org.wordpress.android.fluxc.store.WooCommerceStore
+import javax.inject.Inject
+
+@HiltViewModel
+class WooPosCardReaderViewModel @Inject constructor(
+ private val orderCreateEditRepository: OrderCreateEditRepository,
+ private val resourceProvider: ResourceProvider,
+ private val cardReaderCountryConfigProvider: CardReaderCountryConfigProvider,
+ private val wooStore: WooCommerceStore,
+ private val selectedSite: SelectedSite,
+ savedStateHandle: SavedStateHandle,
+) : ScopedViewModel(savedStateHandle) {
+ init {
+ when (val mode = savedStateHandle.get(WOO_POS_CARD_READER_MODE_KEY)) {
+ is WooPosCardReaderMode.Connection -> {
+ triggerEvent(WooPosCardReaderEvent.Connection)
+ }
+
+ is WooPosCardReaderMode.Payment -> {
+ if (mode.orderId != -1L) {
+ triggerEvent(WooPosCardReaderEvent.Payment(mode.orderId))
+ } else {
+ launch {
+ createTestOrder(
+ onSuccess = { order ->
+ triggerEvent(WooPosCardReaderEvent.Payment(order.id))
+ },
+ onFailure = {
+ Log.e("WooPosCardReaderViewModel", "Failed to create test order")
+ }
+ )
+ }
+ }
+ }
+
+ null -> error("WooPosCardReaderMode not found in savedStateHandle")
+ }
+ }
+
+ private suspend fun createTestOrder(onSuccess: (Order) -> Unit, onFailure: () -> Unit) {
+ val countryConfig = cardReaderCountryConfigProvider.provideCountryConfigFor(
+ wooStore.getStoreCountryCode(selectedSite.get())
+ ) as CardReaderConfigForSupportedCountry
+
+ val result = orderCreateEditRepository.createSimplePaymentOrder(
+ countryConfig.minimumAllowedChargeAmount,
+ customerNote = resourceProvider.getString(R.string.card_reader_tap_to_pay_test_payment_note),
+ isTaxable = false,
+ )
+ result.fold(
+ onSuccess = { onSuccess(it) },
+ onFailure = { onFailure() }
+ )
+ }
+}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartNavigation.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartNavigation.kt
index 9eb461c75a6..d46fcf2cb3d 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartNavigation.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartNavigation.kt
@@ -7,14 +7,18 @@ import androidx.navigation.compose.composable
internal const val CART_ROUTE = "cart"
internal fun NavGraphBuilder.cartScreen(
- onCheckoutClick: () -> Unit
+ onCheckoutClick: () -> Unit,
+ onConnectToCardReaderClicked: () -> Unit,
+ onCollectPaymentWithCardReader: () -> Unit,
) {
composable(CART_ROUTE) {
val viewModel: WooPosCartViewModel = hiltViewModel()
WooPosCartScreen(
viewModel = viewModel,
- onCheckoutClick = onCheckoutClick
+ onCheckoutClick = onCheckoutClick,
+ onConnectToCardReaderClicked = onConnectToCardReaderClicked,
+ onCollectPaymentWithCardReader = onCollectPaymentWithCardReader,
)
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartScreen.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartScreen.kt
index 7080802376b..a2438aeb834 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartScreen.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/cart/WooPosCartScreen.kt
@@ -2,13 +2,17 @@ package com.woocommerce.android.ui.woopos.cart
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize
+import androidx.compose.foundation.layout.height
+import androidx.compose.foundation.layout.padding
import androidx.compose.material.Button
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
+import androidx.compose.ui.unit.dp
import com.woocommerce.android.ui.woopos.util.WooPosPreview
@Composable
@@ -16,17 +20,27 @@ import com.woocommerce.android.ui.woopos.util.WooPosPreview
fun WooPosCartScreen(
viewModel: WooPosCartViewModel,
onCheckoutClick: () -> Unit,
+ onConnectToCardReaderClicked: () -> Unit,
+ onCollectPaymentWithCardReader: () -> Unit,
) {
WooPosCartScreen(
- onButtonClicked = onCheckoutClick
+ onCheckoutClick = onCheckoutClick,
+ onConnectToCardReaderClicked = onConnectToCardReaderClicked,
+ onCollectPaymentWithCardReader = onCollectPaymentWithCardReader,
)
}
@Composable
-private fun WooPosCartScreen(onButtonClicked: () -> Unit) {
+private fun WooPosCartScreen(
+ onCheckoutClick: () -> Unit,
+ onConnectToCardReaderClicked: () -> Unit,
+ onCollectPaymentWithCardReader: () -> Unit,
+) {
Box(
- Modifier.fillMaxSize(),
- contentAlignment = Alignment.Center
+ Modifier
+ .fillMaxSize()
+ .padding(32.dp),
+ contentAlignment = Alignment.CenterStart
) {
Column {
Text(
@@ -34,9 +48,21 @@ private fun WooPosCartScreen(onButtonClicked: () -> Unit) {
style = MaterialTheme.typography.h3,
color = MaterialTheme.colors.primary,
)
- Button(onClick = onButtonClicked) {
+ Button(onClick = onCheckoutClick) {
Text("Checkout")
}
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Button(onClick = onConnectToCardReaderClicked) {
+ Text("Connect to Card Reader")
+ }
+
+ Spacer(modifier = Modifier.height(16.dp))
+
+ Button(onClick = onCollectPaymentWithCardReader) {
+ Text("Collect Payment with Card Reader")
+ }
}
}
}
@@ -44,5 +70,9 @@ private fun WooPosCartScreen(onButtonClicked: () -> Unit) {
@Composable
@WooPosPreview
fun WooPosCartScreenPreview() {
- WooPosCartScreen(onButtonClicked = {})
+ WooPosCartScreen(
+ onCheckoutClick = {},
+ onConnectToCardReaderClicked = {},
+ onCollectPaymentWithCardReader = {},
+ )
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt
index f6bc0edbbcd..ef722732397 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/WooPosActivity.kt
@@ -2,21 +2,48 @@ package com.woocommerce.android.ui.woopos.root
import android.content.pm.ActivityInfo
import android.os.Bundle
+import android.widget.Toast
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.material.MaterialTheme
+import androidx.lifecycle.lifecycleScope
+import com.woocommerce.android.ui.woopos.cardreader.WooPosCardReaderFacade
import com.woocommerce.android.ui.woopos.root.navigation.WooPosRootHost
import dagger.hilt.android.AndroidEntryPoint
+import kotlinx.coroutines.launch
+import javax.inject.Inject
@AndroidEntryPoint
class WooPosActivity : AppCompatActivity() {
+ @Inject
+ lateinit var wooPosCardReaderFacade: WooPosCardReaderFacade
+
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
requestedOrientation = ActivityInfo.SCREEN_ORIENTATION_LANDSCAPE
+ lifecycle.addObserver(wooPosCardReaderFacade)
+
setContent {
MaterialTheme {
- WooPosRootHost()
+ WooPosRootHost(
+ connectToCardReader = {
+ lifecycleScope.launch {
+ wooPosCardReaderFacade.connectToReader()
+ }
+ lifecycleScope.launch {
+ wooPosCardReaderFacade.readerStatus.collect {
+ Toast.makeText(this@WooPosActivity, "Reader status: $it", Toast.LENGTH_SHORT).show()
+ }
+ }
+ },
+ collectPaymentWithCardReader = {
+ lifecycleScope.launch {
+ val result = wooPosCardReaderFacade.collectPayment(-1)
+ Toast.makeText(this@WooPosActivity, "Payment result: $result", Toast.LENGTH_SHORT).show()
+ }
+ }
+ )
}
}
}
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosMainFlowGraph.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosMainFlowGraph.kt
index cd600a29857..aa1b881ca7e 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosMainFlowGraph.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosMainFlowGraph.kt
@@ -1,5 +1,6 @@
package com.woocommerce.android.ui.woopos.root.navigation
+import android.content.Context
import androidx.navigation.NavController
import androidx.navigation.NavGraphBuilder
import androidx.navigation.compose.navigation
@@ -11,14 +12,18 @@ import com.woocommerce.android.ui.woopos.checkout.navigateToCheckoutScreen
const val MAIN_GRAPH_ROUTE = "main-graph"
fun NavGraphBuilder.checkoutGraph(
- navController: NavController
+ navController: NavController,
+ onConnectToCardReaderClicked: (Context) -> Unit,
+ collectPaymentWithCardReader: (Context) -> Unit,
) {
navigation(
startDestination = CART_ROUTE,
route = MAIN_GRAPH_ROUTE,
) {
cartScreen(
- onCheckoutClick = navController::navigateToCheckoutScreen
+ onCheckoutClick = navController::navigateToCheckoutScreen,
+ onConnectToCardReaderClicked = { onConnectToCardReaderClicked(navController.context) },
+ onCollectPaymentWithCardReader = { collectPaymentWithCardReader(navController.context) },
)
checkoutScreen(
onBackClick = navController::popBackStack
diff --git a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosRootHost.kt b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosRootHost.kt
index 0709b338c4d..b578d21d479 100644
--- a/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosRootHost.kt
+++ b/WooCommerce/src/main/kotlin/com/woocommerce/android/ui/woopos/root/navigation/WooPosRootHost.kt
@@ -1,11 +1,15 @@
package com.woocommerce.android.ui.woopos.root.navigation
+import android.content.Context
import androidx.compose.runtime.Composable
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.rememberNavController
@Composable
-fun WooPosRootHost() {
+fun WooPosRootHost(
+ connectToCardReader: (context: Context) -> Unit,
+ collectPaymentWithCardReader: (context: Context) -> Unit,
+) {
val rootController = rememberNavController()
NavHost(
@@ -18,6 +22,8 @@ fun WooPosRootHost() {
) {
checkoutGraph(
navController = rootController,
+ onConnectToCardReaderClicked = { context -> connectToCardReader(context) },
+ collectPaymentWithCardReader = { context -> collectPaymentWithCardReader(context) },
)
}
}
diff --git a/WooCommerce/src/main/res/layout/activity_woo_pos_card_reader.xml b/WooCommerce/src/main/res/layout/activity_woo_pos_card_reader.xml
new file mode 100644
index 00000000000..c7dad055558
--- /dev/null
+++ b/WooCommerce/src/main/res/layout/activity_woo_pos_card_reader.xml
@@ -0,0 +1,7 @@
+
diff --git a/WooCommerce/src/main/res/values/themes.xml b/WooCommerce/src/main/res/values/themes.xml
index ecad50d3eb6..98bff8b1056 100644
--- a/WooCommerce/src/main/res/values/themes.xml
+++ b/WooCommerce/src/main/res/values/themes.xml
@@ -154,4 +154,13 @@
+
+