Skip to content

Commit

Permalink
native link dagger setup (#9465)
Browse files Browse the repository at this point in the history
  • Loading branch information
toluo-stripe authored Oct 21, 2024
1 parent 2baaa76 commit cdb260f
Show file tree
Hide file tree
Showing 19 changed files with 506 additions and 49 deletions.
10 changes: 10 additions & 0 deletions link/api/link.api
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ public final class com/stripe/android/link/ComposableSingletons$LinkActivityKt {
public static field lambda-3 Lkotlin/jvm/functions/Function4;
public static field lambda-4 Lkotlin/jvm/functions/Function4;
public static field lambda-5 Lkotlin/jvm/functions/Function4;
public static field lambda-6 Lkotlin/jvm/functions/Function4;
public fun <init> ()V
public final fun getLambda-1$link_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-2$link_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-3$link_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-4$link_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-5$link_release ()Lkotlin/jvm/functions/Function4;
public final fun getLambda-6$link_release ()Lkotlin/jvm/functions/Function4;
}

public final class com/stripe/android/link/LinkActivityContract$Companion {
Expand Down Expand Up @@ -87,6 +89,14 @@ public final class com/stripe/android/link/LinkPaymentDetails$Saved$Creator : an
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/link/NativeLinkArgs$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/link/NativeLinkArgs;
public synthetic fun createFromParcel (Landroid/os/Parcel;)Ljava/lang/Object;
public final fun newArray (I)[Lcom/stripe/android/link/NativeLinkArgs;
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/link/ui/ComposableSingletons$LinkButtonKt {
public static final field INSTANCE Lcom/stripe/android/link/ui/ComposableSingletons$LinkButtonKt;
public static field lambda-1 Lkotlin/jvm/functions/Function2;
Expand Down
8 changes: 5 additions & 3 deletions link/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
<application>
<activity
android:name="com.stripe.android.link.LinkActivity"
android:theme="@style/StripeLinkBaseTheme"
android:exported="false"
android:label="@string/stripe_link"
android:windowSoftInputMode="adjustResize"
android:autoRemoveFromRecents="true"
android:configChanges="orientation|keyboard|keyboardHidden|screenLayout|screenSize|smallestScreenSize"
android:launchMode="singleTop"
android:theme="@style/StripeTransparentTheme" />
android:configChanges="orientation|keyboard|keyboardHidden|screenLayout|screenSize|smallestScreenSize" />

<activity
android:name="com.stripe.android.link.LinkForegroundActivity"
Expand Down
59 changes: 49 additions & 10 deletions link/src/main/java/com/stripe/android/link/LinkActivity.kt
Original file line number Diff line number Diff line change
@@ -1,48 +1,71 @@
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.SavedStateHandle
import androidx.lifecycle.ViewModelProvider
import androidx.navigation.NavHostController
import androidx.navigation.compose.NavHost
import androidx.navigation.compose.composable
import androidx.navigation.compose.rememberNavController
import com.stripe.android.core.Logger
import com.stripe.android.link.ui.cardedit.CardEditScreen
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 var viewModel: LinkActivityViewModel? = null

@VisibleForTesting
internal lateinit var navController: NavHostController

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

try {
viewModel = ViewModelProvider(this, LinkActivityViewModel.factory())[LinkActivityViewModel::class.java]
} catch (e: NoArgsException) {
Logger.getInstance(BuildConfig.DEBUG).error("Failed to create LinkActivityViewModel", e)
setResult(Activity.RESULT_CANCELED)
finish()
}

setContent {
navController = rememberNavController()

LaunchedEffect(Unit) {
viewModel.navController = navController
viewModel.dismissWithResult = ::dismissWithResult
viewModel?.navController = navController
viewModel?.dismissWithResult = ::dismissWithResult
}

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()
}
Expand Down Expand Up @@ -79,6 +102,22 @@ internal class LinkActivity : AppCompatActivity() {

override fun onDestroy() {
super.onDestroy()
viewModel.unregisterActivity()
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<NativeLinkArgs>(EXTRA_ARGS)
}
}
}
27 changes: 24 additions & 3 deletions link/src/main/java/com/stripe/android/link/LinkActivityContract.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import android.content.Intent
import androidx.activity.result.contract.ActivityResultContract
import androidx.annotation.RestrictTo
import com.stripe.android.PaymentConfiguration
import com.stripe.android.core.utils.FeatureFlags
import com.stripe.android.link.serialization.PopupPayload
import com.stripe.android.networking.StripeRepository
import javax.inject.Inject
Expand All @@ -15,6 +16,18 @@ class LinkActivityContract @Inject internal constructor(
) : ActivityResultContract<LinkActivityContract.Args, LinkActivityResult>() {

override fun createIntent(context: Context, input: Args): Intent {
return if (FeatureFlags.nativeLinkEnabled.isEnabled) {
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,
Expand All @@ -26,13 +39,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)
Expand Down
Original file line number Diff line number Diff line change
@@ -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

Expand All @@ -27,9 +39,24 @@ internal class LinkActivityViewModel : ViewModel() {
dismissWithResult = null
}

internal class Factory : ViewModelProvider.Factory {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return LinkActivityViewModel() as T
companion object {
fun factory(savedStateHandle: SavedStateHandle? = null): ViewModelProvider.Factory = viewModelFactory {
initializer {
val handle: SavedStateHandle = savedStateHandle ?: createSavedStateHandle()
val app = this[APPLICATION_KEY] as Application
val args: NativeLinkArgs = getArgs(handle) ?: throw NoArgsException()

DaggerNativeLinkComponent
.builder()
.configuration(args.configuration)
.publishableKeyProvider { args.publishableKey }
.stripeAccountIdProvider { args.stripeAccountId }
.context(app)
.build()
.viewModel
}
}
}
}

internal class NoArgsException : IllegalArgumentException("NativeLinkArgs not found")
1 change: 1 addition & 0 deletions link/src/main/java/com/stripe/android/link/LinkScreen.kt
Original file line number Diff line number Diff line change
@@ -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")
Expand Down
13 changes: 13 additions & 0 deletions link/src/main/java/com/stripe/android/link/NativeLinkArgs.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.stripe.android.link

import android.os.Parcelable
import androidx.annotation.RestrictTo
import kotlinx.parcelize.Parcelize

@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
@Parcelize
internal data class NativeLinkArgs(
val configuration: LinkConfiguration,
val publishableKey: String,
val stripeAccountId: String?
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package com.stripe.android.link.injection

import android.content.Context
import com.stripe.android.core.Logger
import com.stripe.android.core.injection.PUBLISHABLE_KEY
import com.stripe.android.core.injection.STRIPE_ACCOUNT_ID
import com.stripe.android.link.LinkActivityViewModel
import com.stripe.android.link.LinkConfiguration
import com.stripe.android.link.account.LinkAccountManager
import com.stripe.android.link.analytics.LinkEventsReporter
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 linkEventsReporter: LinkEventsReporter
val logger: Logger
val viewModel: LinkActivityViewModel

@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
}
}
Loading

0 comments on commit cdb260f

Please sign in to comment.