From dc23997b1527f6d1345bfb46e4a08189604255d2 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 13:29:04 +0200 Subject: [PATCH 01/77] POScannedCard --- .../sdk/ui/card/scanner/POScannedCard.kt | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POScannedCard.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POScannedCard.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POScannedCard.kt new file mode 100644 index 000000000..fea2e20db --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POScannedCard.kt @@ -0,0 +1,18 @@ +package com.processout.sdk.ui.card.scanner + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POScannedCard( + val number: String, + val expiration: Expiration?, + val cardholderName: String? +) : Parcelable { + + @Parcelize + data class Expiration( + val month: Int, + val year: Int + ) : Parcelable +} From 445e414510552bc1f7312cc849ff892691148410 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 13:59:08 +0200 Subject: [PATCH 02/77] POCardScannerConfiguration --- .../scanner/POCardScannerConfiguration.kt | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerConfiguration.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerConfiguration.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerConfiguration.kt new file mode 100644 index 000000000..954df5611 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerConfiguration.kt @@ -0,0 +1,41 @@ +package com.processout.sdk.ui.card.scanner + +import android.os.Parcelable +import com.processout.sdk.ui.core.shared.image.PODrawableImage +import com.processout.sdk.ui.core.style.POButtonStyle +import com.processout.sdk.ui.core.style.POTextStyle +import com.processout.sdk.ui.shared.configuration.POActionConfirmationConfiguration +import com.processout.sdk.ui.shared.configuration.POCancellationConfiguration +import kotlinx.parcelize.Parcelize + +@Parcelize +data class POCardScannerConfiguration( + val title: String? = null, + val description: String? = null, + val cancelButton: CancelButton? = CancelButton(), + val cancellation: POCancellationConfiguration = POCancellationConfiguration(), + val style: Style? = null +) : Parcelable { + + /** + * Cancel button configuration. + * + * @param[text] Button text. Pass _null_ to use default text. + * @param[icon] Button icon drawable resource. Pass _null_ to hide. + * @param[confirmation] Specifies action confirmation configuration (e.g. dialog). + * Use _null_ to disable, this is a default behaviour. + */ + @Parcelize + data class CancelButton( + val text: String? = null, + val icon: PODrawableImage? = null, + val confirmation: POActionConfirmationConfiguration? = null + ) : Parcelable + + @Parcelize + data class Style( + val title: POTextStyle? = null, + val description: POTextStyle? = null, + val cancelButton: POButtonStyle? = null + ) : Parcelable +} From 63eb36e55a1ba1bf10b3d80b38dd8d53658b4d96 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 14:11:51 +0200 Subject: [PATCH 03/77] CardScannerActivity and CardScannerActivityContract --- ui/src/main/AndroidManifest.xml | 6 +++ .../ui/card/scanner/CardScannerActivity.kt | 13 +++++++ .../scanner/CardScannerActivityContract.kt | 37 +++++++++++++++++++ 3 files changed, 56 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivity.kt create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivityContract.kt diff --git a/ui/src/main/AndroidManifest.xml b/ui/src/main/AndroidManifest.xml index 0fa3dacc4..9cb9a6403 100644 --- a/ui/src/main/AndroidManifest.xml +++ b/ui/src/main/AndroidManifest.xml @@ -30,6 +30,12 @@ android:launchMode="singleTop" android:theme="@style/Theme.ProcessOut.BottomSheet" /> + + >() { + + companion object { + const val EXTRA_CONFIGURATION = "${BuildConfig.LIBRARY_PACKAGE_NAME}.EXTRA_CONFIGURATION" + const val EXTRA_RESULT = "${BuildConfig.LIBRARY_PACKAGE_NAME}.EXTRA_RESULT" + } + + override fun createIntent( + context: Context, + input: POCardScannerConfiguration + ) = Intent(context, CardScannerActivity::class.java) + .putExtra(EXTRA_CONFIGURATION, input) + + @Suppress("DEPRECATION") + override fun parseResult( + resultCode: Int, + intent: Intent? + ): ProcessOutActivityResult { + intent?.setExtrasClassLoader(ProcessOutActivityResult::class.java.classLoader) + return intent?.getParcelableExtra(EXTRA_RESULT) + ?: ProcessOutActivityResult.Failure( + code = POFailure.Code.Internal(), + message = "Activity result was not provided." + ).also { POLogger.error("%s", it) } + } +} From 3d6428545ab0c5a498aa98294c9c17bf62a4f31d Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 14:22:16 +0200 Subject: [PATCH 04/77] POCardScannerLauncher --- .../ui/card/scanner/POCardScannerLauncher.kt | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerLauncher.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerLauncher.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerLauncher.kt new file mode 100644 index 000000000..c15c18d28 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/POCardScannerLauncher.kt @@ -0,0 +1,63 @@ +package com.processout.sdk.ui.card.scanner + +import android.content.Context +import androidx.activity.ComponentActivity +import androidx.activity.result.ActivityResultLauncher +import androidx.core.app.ActivityOptionsCompat +import androidx.fragment.app.Fragment +import com.processout.sdk.R +import com.processout.sdk.core.ProcessOutActivityResult + +/** + * Launcher that starts [CardScannerActivity] and provides the result. + */ +class POCardScannerLauncher private constructor( + private val launcher: ActivityResultLauncher, + private val activityOptions: ActivityOptionsCompat +) { + + companion object { + /** + * Creates the launcher from Fragment. + * __Note:__ Required to call in _onCreate()_ to register for activity result. + */ + fun create( + from: Fragment, + callback: (ProcessOutActivityResult) -> Unit + ) = POCardScannerLauncher( + launcher = from.registerForActivityResult( + CardScannerActivityContract(), + callback + ), + activityOptions = createActivityOptions(from.requireContext()) + ) + + /** + * Creates the launcher from Activity. + * __Note:__ Required to call in _onCreate()_ to register for activity result. + */ + fun create( + from: ComponentActivity, + callback: (ProcessOutActivityResult) -> Unit + ) = POCardScannerLauncher( + launcher = from.registerForActivityResult( + CardScannerActivityContract(), + from.activityResultRegistry, + callback + ), + activityOptions = createActivityOptions(from) + ) + + private fun createActivityOptions(context: Context) = + ActivityOptionsCompat.makeCustomAnimation( + context, R.anim.po_slide_in_vertical, 0 + ) + } + + /** + * Launches the activity. + */ + fun launch(configuration: POCardScannerConfiguration) { + launcher.launch(configuration, activityOptions) + } +} From 7c786cfa26698c74bacb253731335b8109111d54 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 14:40:55 +0200 Subject: [PATCH 05/77] CardScannerBottomSheet --- .../ui/card/scanner/CardScannerActivity.kt | 9 +++ .../ui/card/scanner/CardScannerBottomSheet.kt | 68 +++++++++++++++++++ 2 files changed, 77 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivity.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivity.kt index c3cd2ce4d..7a436989c 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivity.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerActivity.kt @@ -9,5 +9,14 @@ internal class CardScannerActivity : BaseTransparentPortraitActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) enableEdgeToEdge() + if (savedInstanceState == null) { + val bottomSheet = supportFragmentManager.findFragmentByTag(CardScannerBottomSheet.tag) + if (bottomSheet == null) { + CardScannerBottomSheet().apply { + arguments = intent.extras + show(supportFragmentManager, CardScannerBottomSheet.tag) + } + } + } } } diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt new file mode 100644 index 000000000..3f6dc2fea --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt @@ -0,0 +1,68 @@ +package com.processout.sdk.ui.card.scanner + +import android.content.Context +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import androidx.compose.ui.platform.ComposeView +import androidx.compose.ui.platform.ViewCompositionStrategy +import com.processout.sdk.core.ProcessOutActivityResult +import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.ui.base.BaseBottomSheetDialogFragment +import com.processout.sdk.ui.card.scanner.CardScannerActivityContract.Companion.EXTRA_CONFIGURATION +import com.processout.sdk.ui.card.scanner.CardScannerActivityContract.Companion.EXTRA_RESULT +import com.processout.sdk.ui.core.theme.ProcessOutTheme +import kotlin.math.roundToInt + +internal class CardScannerBottomSheet : BaseBottomSheetDialogFragment() { + + companion object { + val tag: String = CardScannerBottomSheet::class.java.simpleName + } + + override var expandable = false + override val defaultViewHeight by lazy { (screenHeight * 0.5).roundToInt() } + + private var configuration: POCardScannerConfiguration? = null + + override fun onAttach(context: Context) { + super.onAttach(context) + @Suppress("DEPRECATION") + configuration = arguments?.getParcelable(EXTRA_CONFIGURATION) + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View = ComposeView(requireContext()).apply { + setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed) + setContent { + ProcessOutTheme { + // TODO + } + } + } + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + configuration?.let { apply(it.cancellation) } + } + + override fun onCancellation(failure: ProcessOutResult.Failure) { + // TODO + } + + private fun finishWithActivityResult( + resultCode: Int, + result: ProcessOutActivityResult + ) { + setActivityResult( + resultCode = resultCode, + extraName = EXTRA_RESULT, + result = result + ) + finish() + } +} From 4949f5a59ce1f1db41a6ae018155864a8b1128ba Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 15:03:24 +0200 Subject: [PATCH 06/77] States and events --- .../sdk/ui/card/scanner/CardScannerEvent.kt | 14 ++++++++++++++ .../ui/card/scanner/CardScannerInteractorState.kt | 5 +++++ .../ui/card/scanner/CardScannerViewModelState.kt | 9 +++++++++ 3 files changed, 28 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerEvent.kt create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModelState.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerEvent.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerEvent.kt new file mode 100644 index 000000000..6ffa1988a --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerEvent.kt @@ -0,0 +1,14 @@ +package com.processout.sdk.ui.card.scanner + +import com.processout.sdk.core.ProcessOutResult + +internal sealed interface CardScannerEvent { + data class Action(val id: String) : CardScannerEvent + data class Dismiss(val failure: ProcessOutResult.Failure) : CardScannerEvent +} + +internal sealed interface CardScannerCompletion { + data object Awaiting : CardScannerCompletion + data class Success(val card: POScannedCard) : CardScannerCompletion + data class Failure(val failure: ProcessOutResult.Failure) : CardScannerCompletion +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt new file mode 100644 index 000000000..e56bbeb87 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt @@ -0,0 +1,5 @@ +package com.processout.sdk.ui.card.scanner + +internal data class CardScannerInteractorState( + val card: POScannedCard? +) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModelState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModelState.kt new file mode 100644 index 000000000..247a69e63 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModelState.kt @@ -0,0 +1,9 @@ +package com.processout.sdk.ui.card.scanner + +import com.processout.sdk.ui.core.state.POActionState + +internal data class CardScannerViewModelState( + val title: String, + val description: String, + val cancelAction: POActionState? +) From f20cebb0ce9d45974f0b5266801501373216d29b Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 15:16:14 +0200 Subject: [PATCH 07/77] CardScannerInteractor --- .../ui/card/scanner/CardScannerInteractor.kt | 51 +++++++++++++++++++ .../scanner/CardScannerInteractorState.kt | 7 ++- 2 files changed, 57 insertions(+), 1 deletion(-) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractor.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractor.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractor.kt new file mode 100644 index 000000000..3b5337cb2 --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractor.kt @@ -0,0 +1,51 @@ +package com.processout.sdk.ui.card.scanner + +import android.app.Application +import com.processout.sdk.core.POFailure.Code.Cancelled +import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.core.logger.POLogger +import com.processout.sdk.ui.base.BaseInteractor +import com.processout.sdk.ui.card.scanner.CardScannerCompletion.Awaiting +import com.processout.sdk.ui.card.scanner.CardScannerCompletion.Failure +import com.processout.sdk.ui.card.scanner.CardScannerEvent.Action +import com.processout.sdk.ui.card.scanner.CardScannerEvent.Dismiss +import com.processout.sdk.ui.card.scanner.CardScannerInteractorState.ActionId +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.asStateFlow +import kotlinx.coroutines.flow.update + +internal class CardScannerInteractor( + private val app: Application, + private val configuration: POCardScannerConfiguration +) : BaseInteractor() { + + private val _completion = MutableStateFlow(Awaiting) + val completion = _completion.asStateFlow() + + private val _state = MutableStateFlow(initState()) + val state = _state.asStateFlow() + + private fun initState() = CardScannerInteractorState( + card = null + ) + + fun onEvent(event: CardScannerEvent) { + when (event) { + is Action -> when (event.id) { + ActionId.CANCEL -> cancel() + } + is Dismiss -> POLogger.info("Dismissed: %s", event.failure) + } + } + + private fun cancel() { + _completion.update { + Failure( + ProcessOutResult.Failure( + code = Cancelled, + message = "Cancelled by the user with cancel action." + ).also { POLogger.info("Cancelled: %s", it) } + ) + } + } +} diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt index e56bbeb87..853ac0f3e 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerInteractorState.kt @@ -2,4 +2,9 @@ package com.processout.sdk.ui.card.scanner internal data class CardScannerInteractorState( val card: POScannedCard? -) +) { + + object ActionId { + const val CANCEL = "cancel" + } +} From 13911660a7ce12738580e1bf12a2d5472ee1b013 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 15:25:15 +0200 Subject: [PATCH 08/77] CardScannerViewModel --- .../ui/card/scanner/CardScannerViewModel.kt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModel.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModel.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModel.kt new file mode 100644 index 000000000..730ee2aae --- /dev/null +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerViewModel.kt @@ -0,0 +1,48 @@ +package com.processout.sdk.ui.card.scanner + +import android.app.Application +import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.viewModelScope +import com.processout.sdk.ui.shared.extension.map + +internal class CardScannerViewModel( + private val app: Application, + private val configuration: POCardScannerConfiguration, + private val interactor: CardScannerInteractor +) : ViewModel() { + + class Factory( + private val app: Application, + private val configuration: POCardScannerConfiguration + ) : ViewModelProvider.Factory { + @Suppress("UNCHECKED_CAST") + override fun create(modelClass: Class): T = + CardScannerViewModel( + app = app, + configuration = configuration, + interactor = CardScannerInteractor( + app = app, + configuration = configuration + ) + ) as T + } + + val completion = interactor.completion + + val state = interactor.state.map(viewModelScope, ::map) + + init { + addCloseable(interactor.interactorScope) + } + + fun onEvent(event: CardScannerEvent) = interactor.onEvent(event) + + private fun map(state: CardScannerInteractorState) = with(configuration) { + CardScannerViewModelState( + title = "Title", + description = "Description", + cancelAction = null + ) + } +} From e16371a1110029e25d9be2f9b2a72caef854d44d Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Tue, 18 Feb 2025 15:35:05 +0200 Subject: [PATCH 09/77] Init VM and handle completion --- .../ui/card/scanner/CardScannerBottomSheet.kt | 45 ++++++++++++++++++- 1 file changed, 43 insertions(+), 2 deletions(-) diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt index 3f6dc2fea..c74a1ba4b 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt @@ -1,18 +1,27 @@ package com.processout.sdk.ui.card.scanner +import android.app.Activity import android.content.Context import android.os.Bundle import android.view.LayoutInflater import android.view.View import android.view.ViewGroup +import androidx.compose.runtime.LaunchedEffect import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy +import androidx.fragment.app.viewModels +import androidx.lifecycle.compose.collectAsStateWithLifecycle import com.processout.sdk.core.ProcessOutActivityResult import com.processout.sdk.core.ProcessOutResult +import com.processout.sdk.core.toActivityResult import com.processout.sdk.ui.base.BaseBottomSheetDialogFragment import com.processout.sdk.ui.card.scanner.CardScannerActivityContract.Companion.EXTRA_CONFIGURATION import com.processout.sdk.ui.card.scanner.CardScannerActivityContract.Companion.EXTRA_RESULT +import com.processout.sdk.ui.card.scanner.CardScannerCompletion.Failure +import com.processout.sdk.ui.card.scanner.CardScannerCompletion.Success +import com.processout.sdk.ui.card.scanner.CardScannerEvent.Dismiss import com.processout.sdk.ui.core.theme.ProcessOutTheme +import com.processout.sdk.ui.shared.component.screenModeAsState import kotlin.math.roundToInt internal class CardScannerBottomSheet : BaseBottomSheetDialogFragment() { @@ -26,6 +35,13 @@ internal class CardScannerBottomSheet : BaseBottomSheetDialogFragment finishWithActivityResult( + resultCode = Activity.RESULT_OK, + result = ProcessOutActivityResult.Success(completion.card) + ) + is Failure -> finishWithActivityResult( + resultCode = Activity.RESULT_CANCELED, + result = completion.failure.toActivityResult() + ) + else -> {} + } + + override fun onCancellation(failure: ProcessOutResult.Failure) = dismiss(failure) + + fun dismiss(failure: ProcessOutResult.Failure) { + viewModel.onEvent(Dismiss(failure)) + finishWithActivityResult( + resultCode = Activity.RESULT_CANCELED, + result = failure.toActivityResult() + ) } private fun finishWithActivityResult( From 71d8544721bc887bab1ff7a1c4974cbfea73ab87 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 19 Feb 2025 16:00:30 +0200 Subject: [PATCH 10/77] CardScannerScreen --- .../ui/card/scanner/CardScannerBottomSheet.kt | 7 ++- .../sdk/ui/card/scanner/CardScannerScreen.kt | 47 +++++++++++++++++++ 2 files changed, 53 insertions(+), 1 deletion(-) create mode 100644 ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerScreen.kt diff --git a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt index c74a1ba4b..8c1d7ff50 100644 --- a/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt +++ b/ui/src/main/kotlin/com/processout/sdk/ui/card/scanner/CardScannerBottomSheet.kt @@ -7,6 +7,7 @@ import android.view.LayoutInflater import android.view.View import android.view.ViewGroup import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.remember import androidx.compose.ui.platform.ComposeView import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.fragment.app.viewModels @@ -62,7 +63,11 @@ internal class CardScannerBottomSheet : BaseBottomSheetDialogFragment Unit, + style: CardScannerScreen.Style? +) { + Surface( + modifier = Modifier.fillMaxSize(), + shape = shapes.topRoundedCornersLarge, + color = colors.surface.default, + contentColor = Color.Unspecified + ) { + Column( + modifier = Modifier.padding(spacing.extraLarge) + ) { + POText(text = "111") + POText(text = "222") + POText(text = "333") + } + } +} + +internal object CardScannerScreen { + + @Immutable + data class Style( + val title: POText.Style, + val description: POText.Style, + val cancelButton: POButton.Style + ) +} From c903fc13b10a5a5a35bc92da7d74684fc07d1bd0 Mon Sep 17 00:00:00 2001 From: Vitalii Vanziak Date: Wed, 19 Feb 2025 16:22:53 +0200 Subject: [PATCH 11/77] Add card scanner to Example app --- .../ui/screen/features/FeaturesFragment.kt | 31 +++++++++++++++++++ .../src/main/res/layout/fragment_features.xml | 8 +++++ example/src/main/res/values/strings.xml | 3 +- .../ui/card/scanner/CardScannerBottomSheet.kt | 2 +- .../sdk/ui/card/scanner/CardScannerScreen.kt | 4 +-- 5 files changed, 43 insertions(+), 5 deletions(-) diff --git a/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt b/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt index 57450f6a7..ba88e3b9b 100644 --- a/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt +++ b/example/src/main/kotlin/com/processout/example/ui/screen/features/FeaturesFragment.kt @@ -19,6 +19,9 @@ import com.processout.sdk.api.model.request.POCardTokenizationRequest import com.processout.sdk.api.model.response.POCard import com.processout.sdk.api.model.response.POGooglePayCardTokenizationData import com.processout.sdk.core.* +import com.processout.sdk.ui.card.scanner.POCardScannerConfiguration +import com.processout.sdk.ui.card.scanner.POCardScannerLauncher +import com.processout.sdk.ui.card.scanner.POScannedCard import com.processout.sdk.ui.card.update.POCardUpdateConfiguration import com.processout.sdk.ui.card.update.POCardUpdateConfiguration.CardInformation import com.processout.sdk.ui.card.update.POCardUpdateLauncher @@ -35,6 +38,7 @@ class FeaturesFragment : BaseFragment( private val cardsRepository = ProcessOut.instance.cards private lateinit var cardUpdateLauncher: POCardUpdateLauncher + private lateinit var cardScannerLauncher: POCardScannerLauncher private lateinit var googlePayLauncher: POGooglePayCardTokenizationLauncher override fun onCreate(savedInstanceState: Bundle?) { @@ -43,6 +47,10 @@ class FeaturesFragment : BaseFragment( from = this, callback = ::handleCardUpdateResult ) + cardScannerLauncher = POCardScannerLauncher.create( + from = this, + callback = ::handleCardScannerResult + ) googlePayLauncher = POGooglePayCardTokenizationLauncher.create( from = this, walletOptions = WalletOptions.Builder() @@ -76,6 +84,7 @@ class FeaturesFragment : BaseFragment( } } setupCardUpdate() + setupCardScanner() setupGooglePay() } @@ -134,6 +143,28 @@ class FeaturesFragment : BaseFragment( } } + private fun setupCardScanner() { + binding.cardScannerButton.setOnClickListener { + cardScannerLauncher.launch(POCardScannerConfiguration()) + } + } + + private fun handleCardScannerResult(result: ProcessOutActivityResult) { + result + .onSuccess { + showAlert( + title = getString(R.string.card_scanner), + message = it.toString() + ) + } + .onFailure { + showAlert( + title = getString(R.string.card_scanner), + message = it.toMessage() + ) + } + } + private fun setupGooglePay() { lifecycleScope.launch { if (!googlePayLauncher.isReadyToPay(GooglePayConfiguration.isReadyToPayRequest())) { diff --git a/example/src/main/res/layout/fragment_features.xml b/example/src/main/res/layout/fragment_features.xml index 893993b2e..4e59e172c 100644 --- a/example/src/main/res/layout/fragment_features.xml +++ b/example/src/main/res/layout/fragment_features.xml @@ -39,6 +39,14 @@ android:layout_marginTop="@dimen/button_space_vertical" android:text="@string/card_update" /> +