Skip to content

Commit

Permalink
Add analytics for address element (#5344)
Browse files Browse the repository at this point in the history
  • Loading branch information
jameswoo-stripe committed Aug 1, 2022
1 parent f986673 commit a2a04d7
Show file tree
Hide file tree
Showing 18 changed files with 479 additions and 68 deletions.
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,
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

0 comments on commit a2a04d7

Please sign in to comment.