diff --git a/link/src/main/AndroidManifest.xml b/link/src/main/AndroidManifest.xml index c3286cbe53c..b27683b1980 100644 --- a/link/src/main/AndroidManifest.xml +++ b/link/src/main/AndroidManifest.xml @@ -4,10 +4,12 @@ + android:configChanges="orientation|keyboard|keyboardHidden|screenLayout|screenSize|smallestScreenSize" /> paneViewModel( + factory: (NativeLinkComponent) -> ViewModelProvider.Factory +): T { return viewModel(factory = factory(parentActivity().viewModel.activityRetainedComponent)) } + +/** + * Retrieves the parent [LinkActivity] from the current Compose context. + */ +@Composable +internal fun parentActivity(): LinkActivity { + return LocalContext.current.extractActivity() as LinkActivity +} diff --git a/link/src/main/java/com/stripe/android/link/LinkActivity.kt b/link/src/main/java/com/stripe/android/link/LinkActivity.kt index 4d508fea200..7de49813344 100644 --- a/link/src/main/java/com/stripe/android/link/LinkActivity.kt +++ b/link/src/main/java/com/stripe/android/link/LinkActivity.kt @@ -1,15 +1,20 @@ package com.stripe.android.link import android.app.Activity +import android.content.Context import android.content.Intent import android.os.Bundle +import androidx.activity.ComponentActivity import androidx.activity.compose.setContent import androidx.activity.viewModels import androidx.annotation.VisibleForTesting -import androidx.appcompat.app.AppCompatActivity +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.runtime.LaunchedEffect +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier import androidx.core.os.bundleOf -import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.SavedStateHandle import androidx.navigation.NavHostController import androidx.navigation.compose.NavHost import androidx.navigation.compose.composable @@ -19,11 +24,12 @@ import com.stripe.android.link.ui.paymentmenthod.PaymentMethodScreen import com.stripe.android.link.ui.signup.SignUpScreen import com.stripe.android.link.ui.verification.VerificationScreen import com.stripe.android.link.ui.wallet.WalletScreen +import com.stripe.android.ui.core.CircularProgressIndicator -internal class LinkActivity : AppCompatActivity() { - @VisibleForTesting - internal var viewModelFactory: ViewModelProvider.Factory = LinkActivityViewModel.Factory() - private val viewModel: LinkActivityViewModel by viewModels { viewModelFactory } +internal class LinkActivity : ComponentActivity() { + internal val viewModel: LinkActivityViewModel by viewModels( + factoryProducer = { LinkActivityViewModel.Factory } + ) @VisibleForTesting internal lateinit var navController: NavHostController @@ -41,8 +47,18 @@ internal class LinkActivity : AppCompatActivity() { NavHost( navController = navController, - startDestination = LinkScreen.SignUp.route + startDestination = LinkScreen.Loading.route ) { + composable(LinkScreen.Loading.route) { + Box( + modifier = Modifier + .fillMaxSize(), + contentAlignment = Alignment.Center + ) { + CircularProgressIndicator() + } + } + composable(LinkScreen.SignUp.route) { SignUpScreen() } @@ -81,4 +97,20 @@ internal class LinkActivity : AppCompatActivity() { super.onDestroy() viewModel.unregisterActivity() } + + companion object { + internal const val EXTRA_ARGS = "native_link_args" + + internal fun createIntent( + context: Context, + args: NativeLinkArgs + ): Intent { + return Intent(context, LinkActivity::class.java) + .putExtra(EXTRA_ARGS, args) + } + + internal fun getArgs(savedStateHandle: SavedStateHandle): NativeLinkArgs? { + return savedStateHandle.get(EXTRA_ARGS) + } + } } diff --git a/link/src/main/java/com/stripe/android/link/LinkActivityContract.kt b/link/src/main/java/com/stripe/android/link/LinkActivityContract.kt index 3b86fd147d0..1407ac3af9f 100644 --- a/link/src/main/java/com/stripe/android/link/LinkActivityContract.kt +++ b/link/src/main/java/com/stripe/android/link/LinkActivityContract.kt @@ -15,6 +15,18 @@ class LinkActivityContract @Inject internal constructor( ) : ActivityResultContract() { override fun createIntent(context: Context, input: Args): Intent { + return if (NativeLinkEnabled()) { + nativeIntent(context, input) + } else { + webIntent(context, input) + } + } + + override fun parseResult(resultCode: Int, intent: Intent?): LinkActivityResult { + return createLinkActivityResult(resultCode, intent) + } + + private fun webIntent(context: Context, input: Args): Intent { val paymentConfiguration = PaymentConfiguration.getInstance(context) val payload = PopupPayload.create( configuration = input.configuration, @@ -26,13 +38,21 @@ class LinkActivityContract @Inject internal constructor( return LinkForegroundActivity.createIntent(context, payload.toUrl()) } - override fun parseResult(resultCode: Int, intent: Intent?): LinkActivityResult { - return createLinkActivityResult(resultCode, intent) + private fun nativeIntent(context: Context, input: Args): Intent { + val paymentConfiguration = PaymentConfiguration.getInstance(context) + return LinkActivity.createIntent( + context = context, + args = NativeLinkArgs( + configuration = input.configuration, + stripeAccountId = paymentConfiguration.stripeAccountId, + publishableKey = paymentConfiguration.publishableKey + ) + ) } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) data class Args internal constructor( - internal val configuration: LinkConfiguration, + internal val configuration: LinkConfiguration ) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) diff --git a/link/src/main/java/com/stripe/android/link/LinkActivityViewModel.kt b/link/src/main/java/com/stripe/android/link/LinkActivityViewModel.kt index ff0d486f9a5..2f0b94cc812 100644 --- a/link/src/main/java/com/stripe/android/link/LinkActivityViewModel.kt +++ b/link/src/main/java/com/stripe/android/link/LinkActivityViewModel.kt @@ -1,10 +1,22 @@ package com.stripe.android.link +import android.app.Application +import androidx.lifecycle.SavedStateHandle import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider +import androidx.lifecycle.ViewModelProvider.AndroidViewModelFactory.Companion.APPLICATION_KEY +import androidx.lifecycle.createSavedStateHandle +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import androidx.navigation.NavHostController +import com.stripe.android.link.LinkActivity.Companion.getArgs +import com.stripe.android.link.injection.DaggerNativeLinkComponent +import com.stripe.android.link.injection.NativeLinkComponent +import javax.inject.Inject -internal class LinkActivityViewModel : ViewModel() { +internal class LinkActivityViewModel @Inject constructor( + val activityRetainedComponent: NativeLinkComponent +) : ViewModel() { var navController: NavHostController? = null var dismissWithResult: ((LinkActivityResult) -> Unit)? = null @@ -27,9 +39,23 @@ internal class LinkActivityViewModel : ViewModel() { dismissWithResult = null } - internal class Factory : ViewModelProvider.Factory { - override fun create(modelClass: Class): T { - return LinkActivityViewModel() as T + companion object { + val Factory: ViewModelProvider.Factory = viewModelFactory { + initializer { + val savedStateHandle: SavedStateHandle = createSavedStateHandle() + val app = this[APPLICATION_KEY] as Application + val args: NativeLinkArgs = + requireNotNull(getArgs(savedStateHandle)) + + DaggerNativeLinkComponent + .builder() + .configuration(args.configuration) + .publishableKeyProvider { args.publishableKey } + .stripeAccountIdProvider { args.stripeAccountId } + .context(app) + .build() + .viewModel + } } } } diff --git a/link/src/main/java/com/stripe/android/link/LinkScreen.kt b/link/src/main/java/com/stripe/android/link/LinkScreen.kt index 5756767a753..7e6c1292113 100644 --- a/link/src/main/java/com/stripe/android/link/LinkScreen.kt +++ b/link/src/main/java/com/stripe/android/link/LinkScreen.kt @@ -1,6 +1,7 @@ package com.stripe.android.link internal sealed class LinkScreen(val route: String) { + data object Loading : LinkScreen("loading") data object Verification : LinkScreen("verification") data object Wallet : LinkScreen("wallet") data object PaymentMethod : LinkScreen("paymentMethod") diff --git a/link/src/main/java/com/stripe/android/link/NativeLinkArgs.kt b/link/src/main/java/com/stripe/android/link/NativeLinkArgs.kt new file mode 100644 index 00000000000..c4416cbf717 --- /dev/null +++ b/link/src/main/java/com/stripe/android/link/NativeLinkArgs.kt @@ -0,0 +1,11 @@ +package com.stripe.android.link + +import android.os.Parcelable +import kotlinx.parcelize.Parcelize + +@Parcelize +internal data class NativeLinkArgs( + val configuration: LinkConfiguration, + val publishableKey: String, + val stripeAccountId: String? +) : Parcelable diff --git a/link/src/main/java/com/stripe/android/link/NativeLinkEnabled.kt b/link/src/main/java/com/stripe/android/link/NativeLinkEnabled.kt new file mode 100644 index 00000000000..f4a7ef257d3 --- /dev/null +++ b/link/src/main/java/com/stripe/android/link/NativeLinkEnabled.kt @@ -0,0 +1,6 @@ +package com.stripe.android.link + +object NativeLinkEnabled { + var enabled: Boolean = false + operator fun invoke() = enabled +} diff --git a/link/src/main/java/com/stripe/android/link/account/DefaultLinkAccountManager.kt b/link/src/main/java/com/stripe/android/link/account/DefaultLinkAccountManager.kt index 27b27d58672..c7b7c7912c3 100644 --- a/link/src/main/java/com/stripe/android/link/account/DefaultLinkAccountManager.kt +++ b/link/src/main/java/com/stripe/android/link/account/DefaultLinkAccountManager.kt @@ -7,7 +7,6 @@ import com.stripe.android.link.BuildConfig import com.stripe.android.link.LinkConfiguration import com.stripe.android.link.LinkPaymentDetails import com.stripe.android.link.analytics.LinkEventsReporter -import com.stripe.android.link.injection.LinkScope import com.stripe.android.link.model.AccountStatus import com.stripe.android.link.model.LinkAccount import com.stripe.android.link.repositories.LinkRepository @@ -26,7 +25,6 @@ import javax.inject.Inject /** * Manages the Link account for the current user, persisting it across app usages. */ -@LinkScope internal class DefaultLinkAccountManager @Inject constructor( private val config: LinkConfiguration, private val linkRepository: LinkRepository, diff --git a/link/src/main/java/com/stripe/android/link/injection/NativeLinkComponent.kt b/link/src/main/java/com/stripe/android/link/injection/NativeLinkComponent.kt new file mode 100644 index 00000000000..740da4213c8 --- /dev/null +++ b/link/src/main/java/com/stripe/android/link/injection/NativeLinkComponent.kt @@ -0,0 +1,52 @@ +package com.stripe.android.link.injection + +import android.content.Context +import com.stripe.android.core.injection.PUBLISHABLE_KEY +import com.stripe.android.core.injection.STRIPE_ACCOUNT_ID +import com.stripe.android.link.LinkActivity +import com.stripe.android.link.LinkActivityViewModel +import com.stripe.android.link.LinkConfiguration +import com.stripe.android.link.account.LinkAccountManager +import com.stripe.android.link.ui.signup.SignUpViewModel +import dagger.BindsInstance +import dagger.Component +import javax.inject.Named +import javax.inject.Scope + +@Scope +@Retention(AnnotationRetention.RUNTIME) +internal annotation class NativeLinkScope + +@NativeLinkScope +@Component( + modules = [ + NativeLinkModule::class, + ] +) +internal interface NativeLinkComponent { + val linkAccountManager: LinkAccountManager + val configuration: LinkConfiguration + + val viewModel: LinkActivityViewModel + + val signUpViewModelFactory: SignUpViewModel.Factory + + fun inject(activity: LinkActivity) + + @Component.Builder + interface Builder { + @BindsInstance + fun configuration(configuration: LinkConfiguration): Builder + + @BindsInstance + fun publishableKeyProvider(@Named(PUBLISHABLE_KEY) publishableKeyProvider: () -> String): Builder + + @BindsInstance + fun stripeAccountIdProvider(@Named(STRIPE_ACCOUNT_ID) stripeAccountIdProvider: () -> String?): Builder + + @BindsInstance + fun context(context: Context): Builder + + fun build(): NativeLinkComponent + } +} diff --git a/link/src/main/java/com/stripe/android/link/injection/NativeLinkModule.kt b/link/src/main/java/com/stripe/android/link/injection/NativeLinkModule.kt new file mode 100644 index 00000000000..df41abcb2dd --- /dev/null +++ b/link/src/main/java/com/stripe/android/link/injection/NativeLinkModule.kt @@ -0,0 +1,128 @@ +package com.stripe.android.link.injection + +import android.content.Context +import androidx.core.os.LocaleListCompat +import com.stripe.android.BuildConfig +import com.stripe.android.Stripe +import com.stripe.android.core.Logger +import com.stripe.android.core.injection.ENABLE_LOGGING +import com.stripe.android.core.injection.IOContext +import com.stripe.android.core.injection.PUBLISHABLE_KEY +import com.stripe.android.core.networking.AnalyticsRequestExecutor +import com.stripe.android.core.networking.AnalyticsRequestFactory +import com.stripe.android.core.networking.DefaultAnalyticsRequestExecutor +import com.stripe.android.core.networking.DefaultStripeNetworkClient +import com.stripe.android.core.networking.NetworkTypeDetector +import com.stripe.android.core.utils.ContextUtils.packageInfo +import com.stripe.android.core.utils.DefaultDurationProvider +import com.stripe.android.core.utils.DurationProvider +import com.stripe.android.core.version.StripeSdkVersion +import com.stripe.android.link.account.DefaultLinkAccountManager +import com.stripe.android.link.account.LinkAccountManager +import com.stripe.android.link.analytics.DefaultLinkEventsReporter +import com.stripe.android.link.analytics.LinkEventsReporter +import com.stripe.android.link.repositories.LinkApiRepository +import com.stripe.android.link.repositories.LinkRepository +import com.stripe.android.networking.StripeApiRepository +import com.stripe.android.networking.StripeRepository +import com.stripe.android.payments.core.analytics.ErrorReporter +import com.stripe.android.payments.core.analytics.RealErrorReporter +import com.stripe.android.payments.core.injection.PRODUCT_USAGE +import com.stripe.android.repository.ConsumersApiService +import com.stripe.android.repository.ConsumersApiServiceImpl +import dagger.Binds +import dagger.Module +import dagger.Provides +import kotlinx.coroutines.Dispatchers +import javax.inject.Named +import kotlin.coroutines.CoroutineContext + +@Module +internal interface NativeLinkModule { + @Binds + @NativeLinkScope + fun bindLinkRepository(linkApiRepository: LinkApiRepository): LinkRepository + + @Binds + @NativeLinkScope + fun bindLinkEventsReporter(linkEventsReporter: DefaultLinkEventsReporter): LinkEventsReporter + + @Binds + @NativeLinkScope + fun bindLinkAccountManager(linkAccountManager: DefaultLinkAccountManager): LinkAccountManager + + @Binds + @NativeLinkScope + fun bindsErrorReporter(errorReporter: RealErrorReporter): ErrorReporter + + @Binds + @NativeLinkScope + fun stripeRepository(stripeRepository: StripeApiRepository): StripeRepository + + companion object { + @Provides + @NativeLinkScope + fun provideConsumersApiService( + logger: Logger, + @IOContext workContext: CoroutineContext, + ): ConsumersApiService = ConsumersApiServiceImpl( + appInfo = Stripe.appInfo, + sdkVersion = StripeSdkVersion.VERSION, + apiVersion = Stripe.API_VERSION, + stripeNetworkClient = DefaultStripeNetworkClient( + logger = logger, + workContext = workContext + ) + ) + + @Provides + @NativeLinkScope + fun provideAnalyticsRequestFactory( + context: Context, + @Named(PUBLISHABLE_KEY) publishableKeyProvider: () -> String + ): AnalyticsRequestFactory = AnalyticsRequestFactory( + packageManager = context.packageManager, + packageName = context.packageName.orEmpty(), + packageInfo = context.packageInfo, + publishableKeyProvider = publishableKeyProvider, + networkTypeProvider = NetworkTypeDetector(context)::invoke, + ) + + @Provides + @NativeLinkScope + fun provideDurationProvider(): DurationProvider { + return DefaultDurationProvider.instance + } + + @Provides + @NativeLinkScope + fun provideLogger(@Named(ENABLE_LOGGING) enableLogging: Boolean) = + Logger.getInstance(enableLogging) + + @Provides + @NativeLinkScope + fun provideLocale() = + LocaleListCompat.getAdjustedDefault().takeUnless { it.isEmpty }?.get(0) + + @IOContext + @Provides + @NativeLinkScope + fun ioContext(): CoroutineContext = Dispatchers.IO + + @Provides + @NativeLinkScope + @Named(PRODUCT_USAGE) + fun provideProductUsageTokens() = setOf("PaymentSheet") + + @Provides + @NativeLinkScope + internal fun providesAnalyticsRequestExecutor( + executor: DefaultAnalyticsRequestExecutor + ): AnalyticsRequestExecutor = executor + + @Provides + @Named(ENABLE_LOGGING) + @NativeLinkScope + fun providesEnableLogging(): Boolean = BuildConfig.DEBUG + } +} diff --git a/link/src/main/java/com/stripe/android/link/repositories/LinkApiRepository.kt b/link/src/main/java/com/stripe/android/link/repositories/LinkApiRepository.kt index ba1727a942d..5214c21b3a7 100644 --- a/link/src/main/java/com/stripe/android/link/repositories/LinkApiRepository.kt +++ b/link/src/main/java/com/stripe/android/link/repositories/LinkApiRepository.kt @@ -6,7 +6,6 @@ import com.stripe.android.core.injection.PUBLISHABLE_KEY import com.stripe.android.core.injection.STRIPE_ACCOUNT_ID import com.stripe.android.core.networking.ApiRequest import com.stripe.android.link.LinkPaymentDetails -import com.stripe.android.link.injection.LinkScope import com.stripe.android.model.ConsumerPaymentDetails import com.stripe.android.model.ConsumerPaymentDetailsCreateParams import com.stripe.android.model.ConsumerPaymentDetailsCreateParams.Card.Companion.extraConfirmationParams @@ -28,7 +27,6 @@ import kotlin.coroutines.CoroutineContext /** * Repository that uses [StripeRepository] for Link services. */ -@LinkScope internal class LinkApiRepository @Inject constructor( @Named(PUBLISHABLE_KEY) private val publishableKeyProvider: () -> String, @Named(STRIPE_ACCOUNT_ID) private val stripeAccountIdProvider: () -> String?, diff --git a/link/src/main/java/com/stripe/android/link/ui/signup/SignUpViewModel.kt b/link/src/main/java/com/stripe/android/link/ui/signup/SignUpViewModel.kt index a373f37f79d..ab8da0ba2eb 100644 --- a/link/src/main/java/com/stripe/android/link/ui/signup/SignUpViewModel.kt +++ b/link/src/main/java/com/stripe/android/link/ui/signup/SignUpViewModel.kt @@ -1,15 +1,19 @@ package com.stripe.android.link.ui.signup import androidx.lifecycle.ViewModel +import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.viewModelScope +import androidx.lifecycle.viewmodel.initializer +import androidx.lifecycle.viewmodel.viewModelFactory import androidx.navigation.NavHostController import com.stripe.android.core.Logger import com.stripe.android.core.model.CountryCode import com.stripe.android.core.strings.resolvableString -import com.stripe.android.link.LinkActivityContract +import com.stripe.android.link.LinkConfiguration import com.stripe.android.link.LinkScreen import com.stripe.android.link.account.LinkAccountManager import com.stripe.android.link.analytics.LinkEventsReporter +import com.stripe.android.link.injection.NativeLinkComponent import com.stripe.android.link.model.LinkAccount import com.stripe.android.link.ui.ErrorMessage import com.stripe.android.link.ui.getErrorMessage @@ -32,21 +36,21 @@ import javax.inject.Inject import kotlin.time.Duration.Companion.seconds internal class SignUpViewModel @Inject constructor( - private val args: LinkActivityContract.Args, + private val configuration: LinkConfiguration, private val linkAccountManager: LinkAccountManager, private val linkEventsReporter: LinkEventsReporter, private val logger: Logger ) : ViewModel() { internal var navController: NavHostController? = null val emailController = EmailConfig.createController( - initialValue = args.configuration.customerInfo.email + initialValue = configuration.customerInfo.email ) val phoneNumberController = PhoneNumberController.createPhoneNumberController( - initialValue = args.configuration.customerInfo.phone.orEmpty(), - initiallySelectedCountryCode = args.configuration.customerInfo.billingCountryCode + initialValue = configuration.customerInfo.phone.orEmpty(), + initiallySelectedCountryCode = configuration.customerInfo.billingCountryCode ) val nameController = NameConfig.createController( - initialValue = args.configuration.customerInfo.name + initialValue = configuration.customerInfo.name ) private val _state = MutableStateFlow( value = SignUpScreenState( @@ -58,7 +62,7 @@ internal class SignUpViewModel @Inject constructor( private val requiresNameCollection: Boolean get() { - val countryCode = when (val stripeIntent = args.configuration.stripeIntent) { + val countryCode = when (val stripeIntent = configuration.stripeIntent) { is PaymentIntent -> stripeIntent.countryCode is SetupIntent -> stripeIntent.countryCode } @@ -188,8 +192,29 @@ internal class SignUpViewModel @Inject constructor( } } + internal class Factory @Inject constructor( + private val linkAccountManager: LinkAccountManager, + private val linkEventsReporter: LinkEventsReporter, + private val logger: Logger, + private val configuration: LinkConfiguration + ) { + fun create(): SignUpViewModel { + return SignUpViewModel(configuration, linkAccountManager, linkEventsReporter, logger) + } + } + companion object { // How long to wait before triggering a call to lookup the email internal val LOOKUP_DEBOUNCE = 1.seconds + + fun factory( + parentComponent: NativeLinkComponent + ): ViewModelProvider.Factory { + return viewModelFactory { + initializer { + parentComponent.signUpViewModelFactory.create() + } + } + } } } diff --git a/link/src/test/java/com/stripe/android/link/LinkActivityContractTest.kt b/link/src/test/java/com/stripe/android/link/LinkActivityContractTest.kt index 8054b54b41e..77fc9898bcf 100644 --- a/link/src/test/java/com/stripe/android/link/LinkActivityContractTest.kt +++ b/link/src/test/java/com/stripe/android/link/LinkActivityContractTest.kt @@ -31,7 +31,8 @@ class LinkActivityContractTest { } @Test - fun `LinkActivityContract creates intent with URL`() { + fun `LinkActivityContract creates intent with URL with native link disabled`() { + NativeLinkEnabled.enabled = false val config = LinkConfiguration( stripeIntent = StripeIntentFixtures.PI_SUCCEEDED, merchantName = "Merchant, Inc", @@ -60,4 +61,40 @@ class LinkActivityContractTest { "https://checkout.link.com/#" ) } + + @Test + fun `LinkActivityContract creates intent with with NativeLinkArgs when native link is enabled`() { + NativeLinkEnabled.enabled = true + val config = LinkConfiguration( + stripeIntent = StripeIntentFixtures.PI_SUCCEEDED, + merchantName = "Merchant, Inc", + merchantCountryCode = "US", + customerInfo = LinkConfiguration.CustomerInfo( + name = "Name", + email = "customer@email.com", + phone = "1234567890", + billingCountryCode = "US", + ), + shippingValues = null, + passthroughModeEnabled = false, + flags = emptyMap(), + cardBrandChoice = null, + ) + + val args = LinkActivityContract.Args( + config, + ) + val stripeRepository = mock() + whenever(stripeRepository.buildPaymentUserAgent(any())).thenReturn("test") + val contract = LinkActivityContract(stripeRepository) + val intent = contract.createIntent(ApplicationProvider.getApplicationContext(), args) + assertThat(intent.component?.className).isEqualTo(LinkActivity::class.java.name) + assertThat(intent.extras?.getParcelable(LinkActivity.EXTRA_ARGS)).isEqualTo( + NativeLinkArgs( + configuration = config, + publishableKey = "pk_test_abcdefg", + stripeAccountId = null + ) + ) + } } diff --git a/link/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt b/link/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt index 7fcc90c4ed4..43e35b3ce90 100644 --- a/link/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt +++ b/link/src/test/java/com/stripe/android/link/LinkActivityViewModelTest.kt @@ -20,7 +20,7 @@ import org.robolectric.RobolectricTestRunner @RunWith(RobolectricTestRunner::class) internal class LinkActivityViewModelTest { private val dispatcher = UnconfinedTestDispatcher() - private val vm = LinkActivityViewModel() + private val vm = LinkActivityViewModel(mock()) private val navController: NavHostController = mock() private val dismissWithResult: (LinkActivityResult) -> Unit = mock() diff --git a/link/src/test/java/com/stripe/android/link/ui/signup/SignUpViewModelTest.kt b/link/src/test/java/com/stripe/android/link/ui/signup/SignUpViewModelTest.kt index c7d2d192d47..fc0af16a222 100644 --- a/link/src/test/java/com/stripe/android/link/ui/signup/SignUpViewModelTest.kt +++ b/link/src/test/java/com/stripe/android/link/ui/signup/SignUpViewModelTest.kt @@ -4,7 +4,6 @@ import androidx.navigation.NavHostController import com.google.common.truth.Truth.assertThat import com.stripe.android.core.model.CountryCode import com.stripe.android.core.strings.resolvableString -import com.stripe.android.link.LinkActivityContract import com.stripe.android.link.LinkConfiguration import com.stripe.android.link.LinkScreen import com.stripe.android.link.account.FakeLinkAccountManager @@ -52,9 +51,6 @@ internal class SignUpViewModelTest { cardBrandChoice = null, passthroughModeEnabled = false ) - private val defaultArgs = LinkActivityContract.Args( - configuration = config, - ) private val navController: NavHostController = mock() @@ -388,22 +384,20 @@ internal class SignUpViewModelTest { private fun createViewModel( prefilledEmail: String? = null, - args: LinkActivityContract.Args = defaultArgs, + configuration: LinkConfiguration = config, countryCode: CountryCode = CountryCode.US, linkEventsReporter: LinkEventsReporter = SignUpLinkEventsReporter(), linkAccountManager: LinkAccountManager = FakeLinkAccountManager() ): SignUpViewModel { return SignUpViewModel( - args = args.copy( - configuration = args.configuration.copy( - customerInfo = args.configuration.customerInfo.copy( - email = prefilledEmail, - ), - stripeIntent = when (val intent = args.configuration.stripeIntent) { - is PaymentIntent -> intent.copy(countryCode = countryCode.value) - is SetupIntent -> intent.copy(countryCode = countryCode.value) - } - ) + configuration = configuration.copy( + customerInfo = configuration.customerInfo.copy( + email = prefilledEmail, + ), + stripeIntent = when (val intent = configuration.stripeIntent) { + is PaymentIntent -> intent.copy(countryCode = countryCode.value) + is SetupIntent -> intent.copy(countryCode = countryCode.value) + } ), linkAccountManager = linkAccountManager, linkEventsReporter = linkEventsReporter, diff --git a/paymentsheet-example/build.gradle b/paymentsheet-example/build.gradle index 36ab9334c41..140417c894f 100644 --- a/paymentsheet-example/build.gradle +++ b/paymentsheet-example/build.gradle @@ -34,6 +34,7 @@ dependencies { implementation project(':payments') implementation project(':stripecardscan') implementation project(':financial-connections') + implementation project(':link') implementation libs.androidx.activity implementation libs.androidx.appCompat diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/NativeLinkSettingsDefinition.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/NativeLinkSettingsDefinition.kt new file mode 100644 index 00000000000..a9c2aade6d3 --- /dev/null +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/NativeLinkSettingsDefinition.kt @@ -0,0 +1,21 @@ +package com.stripe.android.paymentsheet.example.playground.settings + +import com.stripe.android.link.NativeLinkEnabled +import com.stripe.android.paymentsheet.PaymentSheet +import com.stripe.android.paymentsheet.example.playground.PlaygroundState + +internal object NativeLinkSettingsDefinition : BooleanSettingsDefinition( + key = "nativeLink", + displayName = "Native Link", + defaultValue = false, +) { + override fun configure( + value: Boolean, + configurationBuilder: PaymentSheet.Configuration.Builder, + playgroundState: PlaygroundState.Payment, + configurationData: PlaygroundSettingDefinition.PaymentSheetConfigurationData + ) { + super.configure(value, configurationBuilder, playgroundState, configurationData) + NativeLinkEnabled.enabled = value + } +} diff --git a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt index 1085eea10cf..01490e5cfc6 100644 --- a/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt +++ b/paymentsheet-example/src/main/java/com/stripe/android/paymentsheet/example/playground/settings/PlaygroundSettings.kt @@ -378,6 +378,7 @@ internal class PlaygroundSettings private constructor( CustomerSettingsDefinition, CheckoutModeSettingsDefinition, LinkSettingsDefinition, + NativeLinkSettingsDefinition, CountrySettingsDefinition, CurrencySettingsDefinition, GooglePaySettingsDefinition,