Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add analytics for address element #5344

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
56 changes: 50 additions & 6 deletions paymentsheet/api/paymentsheet.api
Expand Up @@ -810,16 +810,20 @@ public final class com/stripe/android/paymentsheet/addresselement/AddressLaunche
public synthetic fun newArray (I)[Ljava/lang/Object;
}

public final class com/stripe/android/paymentsheet/addresselement/AddressUtilsKt {
public static final fun levenshtein (Ljava/lang/CharSequence;Ljava/lang/CharSequence;)I
}

public final class com/stripe/android/paymentsheet/addresselement/AutocompleteScreenKt {
public static final field TEST_TAG_ATTRIBUTION_DRAWABLE Ljava/lang/String;
}

public final class com/stripe/android/paymentsheet/addresselement/AutocompleteViewModel_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel_Factory;
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel_Factory;
public fun get ()Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel;
public synthetic fun get ()Ljava/lang/Object;
public static fun newInstance (Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;Lcom/stripe/android/paymentsheet/addresselement/AddressElementNavigator;Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel$Args;Landroid/app/Application;)Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel;
public static fun newInstance (Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;Lcom/stripe/android/paymentsheet/addresselement/AddressElementNavigator;Lcom/stripe/android/ui/core/elements/autocomplete/PlacesClientProxy;Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel$Args;Lcom/stripe/android/paymentsheet/addresselement/analytics/AddressLauncherEventReporter;Landroid/app/Application;)Lcom/stripe/android/paymentsheet/addresselement/AutocompleteViewModel;
}

public final class com/stripe/android/paymentsheet/addresselement/AutocompleteViewModel_Factory_MembersInjector : dagger/MembersInjector {
Expand All @@ -845,11 +849,11 @@ public final class com/stripe/android/paymentsheet/addresselement/ComposableSing
}

public final class com/stripe/android/paymentsheet/addresselement/InputAddressViewModel_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel_Factory;
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel_Factory;
public fun get ()Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel;
public synthetic fun get ()Ljava/lang/Object;
public static fun newInstance (Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;Lcom/stripe/android/paymentsheet/addresselement/AddressElementNavigator;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel;
public static fun newInstance (Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;Lcom/stripe/android/paymentsheet/addresselement/AddressElementNavigator;Lcom/stripe/android/paymentsheet/addresselement/analytics/AddressLauncherEventReporter;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel;
}

public final class com/stripe/android/paymentsheet/addresselement/InputAddressViewModel_Factory_MembersInjector : dagger/MembersInjector {
Expand All @@ -860,6 +864,14 @@ public final class com/stripe/android/paymentsheet/addresselement/InputAddressVi
public static fun injectSubComponentBuilderProvider (Lcom/stripe/android/paymentsheet/addresselement/InputAddressViewModel$Factory;Ljavax/inject/Provider;)V
}

public final class com/stripe/android/paymentsheet/addresselement/analytics/DefaultAddressLauncherEventReporter_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/addresselement/analytics/DefaultAddressLauncherEventReporter_Factory;
public fun get ()Lcom/stripe/android/paymentsheet/addresselement/analytics/DefaultAddressLauncherEventReporter;
public synthetic fun get ()Ljava/lang/Object;
public static fun newInstance (Lcom/stripe/android/core/networking/AnalyticsRequestExecutor;Lcom/stripe/android/core/networking/AnalyticsRequestFactory;Lkotlin/coroutines/CoroutineContext;)Lcom/stripe/android/paymentsheet/addresselement/analytics/DefaultAddressLauncherEventReporter;
}

public final class com/stripe/android/paymentsheet/analytics/DefaultEventReporter_Factory : dagger/internal/Factory {
public fun <init> (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/analytics/DefaultEventReporter_Factory;
Expand Down Expand Up @@ -1073,6 +1085,14 @@ public final class com/stripe/android/paymentsheet/forms/TransformSpecToElement_
public static fun newInstance (Lcom/stripe/android/ui/core/forms/resources/ResourceRepository;Lcom/stripe/android/paymentsheet/paymentdatacollection/FormFragmentArguments;Landroid/content/Context;)Lcom/stripe/android/paymentsheet/forms/TransformSpecToElement;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideAnalyticsRequestFactory$paymentsheet_releaseFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideAnalyticsRequestFactory$paymentsheet_releaseFactory;
public fun get ()Lcom/stripe/android/core/networking/AnalyticsRequestFactory;
public synthetic fun get ()Ljava/lang/Object;
public static fun provideAnalyticsRequestFactory$paymentsheet_release (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Landroid/content/Context;Ljava/lang/String;)Lcom/stripe/android/core/networking/AnalyticsRequestFactory;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideDummyInjectorKeyFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideDummyInjectorKeyFactory;
Expand All @@ -1081,6 +1101,14 @@ public final class com/stripe/android/paymentsheet/injection/AddressElementViewM
public static fun provideDummyInjectorKey (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)Ljava/lang/String;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideEventReporterFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideEventReporterFactory;
public fun get ()Lcom/stripe/android/paymentsheet/addresselement/analytics/AddressLauncherEventReporter;
public synthetic fun get ()Ljava/lang/Object;
public static fun provideEventReporter (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Lcom/stripe/android/paymentsheet/addresselement/analytics/DefaultAddressLauncherEventReporter;)Lcom/stripe/android/paymentsheet/addresselement/analytics/AddressLauncherEventReporter;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideEventReporterModeFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideEventReporterModeFactory;
Expand All @@ -1089,6 +1117,22 @@ public final class com/stripe/android/paymentsheet/injection/AddressElementViewM
public static fun provideEventReporterMode (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;)Lcom/stripe/android/paymentsheet/analytics/EventReporter$Mode;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideGooglePlacesClient$paymentsheet_releaseFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;Ljavax/inject/Provider;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvideGooglePlacesClient$paymentsheet_releaseFactory;
public fun get ()Lcom/stripe/android/ui/core/elements/autocomplete/PlacesClientProxy;
public synthetic fun get ()Ljava/lang/Object;
public static fun provideGooglePlacesClient$paymentsheet_release (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Landroid/content/Context;Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;)Lcom/stripe/android/ui/core/elements/autocomplete/PlacesClientProxy;
}

public final class com/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvidesPublishableKeyFactory : dagger/internal/Factory {
public fun <init> (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;)V
public static fun create (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Ljavax/inject/Provider;)Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule_ProvidesPublishableKeyFactory;
public synthetic fun get ()Ljava/lang/Object;
public fun get ()Ljava/lang/String;
public static fun providesPublishableKey (Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelModule;Lcom/stripe/android/paymentsheet/addresselement/AddressElementActivityContract$Args;)Ljava/lang/String;
}

public final class com/stripe/android/paymentsheet/injection/DaggerAddressElementViewModelFactoryComponent {
public static fun builder ()Lcom/stripe/android/paymentsheet/injection/AddressElementViewModelFactoryComponent$Builder;
}
Expand Down
Expand Up @@ -12,6 +12,7 @@ import androidx.compose.ui.test.onNodeWithText
import androidx.compose.ui.test.performTextInput
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
import com.stripe.android.paymentsheet.addresselement.analytics.AddressLauncherEventReporter
import com.stripe.android.ui.core.DefaultPaymentsTheme
import com.stripe.android.ui.core.elements.autocomplete.PlacesClientProxy
import com.stripe.android.ui.core.elements.autocomplete.model.AddressComponent
Expand All @@ -30,10 +31,12 @@ class AutocompleteScreenTest {
val composeTestRule = createAndroidComposeRule<ComponentActivity>()

private val args = AddressElementActivityContract.Args(
"publishableKey",
AddressLauncher.Configuration(),
"injectorKey"
)
private val application = ApplicationProvider.getApplicationContext<Application>()
private val eventReporter = FakeEventReporter()

@Test
fun ensure_elements_exist() {
Expand Down Expand Up @@ -89,15 +92,13 @@ class AutocompleteScreenTest {
viewModel = AutocompleteViewModel(
args,
AddressElementNavigator(),
mockClient,
AutocompleteViewModel.Args(
"US"
),
eventReporter,
application
).apply {
initialize {
mockClient
}
}
)
)
}
}
Expand Down Expand Up @@ -128,4 +129,18 @@ class AutocompleteScreenTest {
)
}
}

private class FakeEventReporter : AddressLauncherEventReporter {
override fun onShow(country: String) {
// no-op
}

override fun onCompleted(
country: String,
autocompleteResultSelected: Boolean,
editDistance: Int?
) {
// no-op
}
}
}
Expand Up @@ -25,12 +25,14 @@ internal class AddressElementActivityContract :
/**
* Arguments for launching [AddressElementActivity] to collect an address.
*
* @param publishableKey the Stripe publishable key
* @param config the paymentsheet configuration passed from the merchant
* @param injectorKey Parameter needed to perform dependency injection.
* If default, a new graph is created
*/
@Parcelize
data class Args internal constructor(
internal val publishableKey: String,
internal val config: AddressLauncher.Configuration?,
@InjectorKey internal val injectorKey: String = DUMMY_INJECTOR_KEY
) : ActivityStarter.Args {
Expand Down
Expand Up @@ -54,9 +54,13 @@ internal class AddressLauncher internal constructor(
)

@JvmOverloads
fun present(configuration: Configuration = Configuration()) {
fun present(
publishableKey: String,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can you add this to our api if you haven't already?

configuration: Configuration = Configuration()
) {
activityResultLauncher.launch(
AddressElementActivityContract.Args(
publishableKey,
configuration,
injectorKey
)
Expand Down
Expand Up @@ -3,6 +3,6 @@ package com.stripe.android.paymentsheet.addresselement
/**
* Callback that is invoked when a [AddressLauncherResult] is available.
*/
internal interface AddressLauncherResultCallback {
internal fun interface AddressLauncherResultCallback {
fun onAddressLauncherResult(addressLauncherResult: AddressLauncherResult)
}
@@ -0,0 +1,48 @@
package com.stripe.android.paymentsheet.addresselement

import kotlin.math.min

// https://gist.github.com/ademar111190/34d3de41308389a0d0d8

fun CharSequence.levenshtein(other: CharSequence): Int {
if (this == other) { return 0 }
if (this.isEmpty()) { return other.length }
if (other.isEmpty()) { return this.length }

val thisLength = this.length + 1
val otherLength = other.length + 1

var cost = Array(thisLength) { it }
var newCost = Array(thisLength) { 0 }

for (i in 1 until otherLength) {
newCost[0] = i

for (j in 1 until thisLength) {
val match = if (this[j - 1] == other[i - 1]) 0 else 1

val costReplace = cost[j - 1] + match
val costInsert = cost[j] + 1
val costDelete = newCost[j - 1] + 1

newCost[j] = min(min(costInsert, costDelete), costReplace)
}

val swap = cost
cost = newCost
newCost = swap
}

return cost[thisLength - 1]
}

internal fun AddressDetails.editDistance(otherAddress: AddressDetails?): Int {
var editDistance = 0
editDistance += (city ?: "").levenshtein(otherAddress?.city ?: "")
editDistance += (country ?: "").levenshtein(otherAddress?.country ?: "")
editDistance += (line1 ?: "").levenshtein(otherAddress?.line1 ?: "")
editDistance += (line2 ?: "").levenshtein(otherAddress?.line2 ?: "")
editDistance += (postalCode ?: "").levenshtein(otherAddress?.postalCode ?: "")
editDistance += (state ?: "").levenshtein(otherAddress?.state ?: "")
return editDistance
}
Expand Up @@ -56,16 +56,15 @@ internal fun AutocompleteScreen(
) {
val application = LocalContext.current.applicationContext as Application
val viewModel: AutocompleteViewModel =
viewModel<AutocompleteViewModel>(
viewModel(
factory = AutocompleteViewModel.Factory(
injector,
AutocompleteViewModel.Args(
injector = injector,
args = AutocompleteViewModel.Args(
country = country
)
) { application }
).also {
it.initialize()
}
),
applicationSupplier = { application }
)
)

AutocompleteScreenUI(viewModel = viewModel)
}
Expand Down
Expand Up @@ -7,6 +7,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import androidx.lifecycle.viewModelScope
import com.stripe.android.paymentsheet.R
import com.stripe.android.paymentsheet.addresselement.analytics.AddressLauncherEventReporter
import com.stripe.android.ui.core.injection.NonFallbackInjectable
import com.stripe.android.paymentsheet.injection.AutocompleteViewModelSubcomponent
import com.stripe.android.ui.core.elements.SimpleTextFieldConfig
Expand Down Expand Up @@ -34,11 +35,11 @@ import javax.inject.Provider
internal class AutocompleteViewModel @Inject constructor(
val args: AddressElementActivityContract.Args,
val navigator: AddressElementNavigator,
private val placesClient: PlacesClientProxy?,
private val autocompleteArgs: Args,
private val eventReporter: AddressLauncherEventReporter,
application: Application
) : AndroidViewModel(application) {
private var client: PlacesClientProxy? = null

private val _predictions = MutableStateFlow<List<AutocompletePrediction>?>(null)
val predictions: StateFlow<List<AutocompletePrediction>?>
get() = _predictions
Expand All @@ -63,20 +64,13 @@ internal class AutocompleteViewModel @Inject constructor(

private val debouncer = Debouncer()

fun initialize(
clientProvider: () -> PlacesClientProxy? = {
args.config?.googlePlacesApiKey?.let {
PlacesClientProxy.create(getApplication(), it)
}
}
) {
client = clientProvider()
init {
debouncer.startWatching(
coroutineScope = viewModelScope,
queryFlow = queryFlow,
onValidQuery = {
viewModelScope.launch {
client?.findAutocompletePredictions(
placesClient?.findAutocompletePredictions(
query = it,
country = autocompleteArgs.country
?: throw IllegalStateException("Country cannot be empty"),
Expand Down Expand Up @@ -111,12 +105,15 @@ internal class AutocompleteViewModel @Inject constructor(
}
}
}
autocompleteArgs.country?.let { country ->
eventReporter.onShow(country)
}
}

fun selectPrediction(prediction: AutocompletePrediction) {
viewModelScope.launch {
_loading.value = true
client?.fetchPlace(
placesClient?.fetchPlace(
placeId = prediction.placeId
)?.fold(
onSuccess = {
Expand Down