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

[Integration] Remove Mavericks #8157

Merged
merged 10 commits into from
Mar 27, 2024
Merged
3 changes: 3 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@

## XX.XX.XX - 2023-XX-XX

### Financial Connections
* [CHANGED][8157](https://github.com/stripe/stripe-android/pull/8157) Financial Connections no longer depends on the Mavericks library.

## 20.40.2 - 2024-03-25

### PaymentSheet
Expand Down
4 changes: 0 additions & 4 deletions dependencies.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,6 @@ ext.versions = [
kotlinSerializationConverter: '1.0.0',
ktlint : '0.48.2',
leakCanary : '2.13',
mavericks : '3.0.9',
material : '1.11.0',
mockito : '5.10.0',
mockitoInline : '5.2.0',
Expand Down Expand Up @@ -162,8 +161,6 @@ ext.libs = [
leakCanary : "com.squareup.leakcanary:leakcanary-android:${versions.leakCanary}",
loggingInterceptor : "com.squareup.okhttp3:logging-interceptor:${versions.okhttp}",
material : "com.google.android.material:material:${versions.material}",
mavericks : "com.airbnb.android:mavericks:${versions.mavericks}",
mavericksCompose : "com.airbnb.android:mavericks-compose:${versions.mavericks}",
microblinkCapture : "com.microblink:capture-core:${versions.microblinkCapture}",
okio : "com.squareup.okio:okio:${versions.okio}",
payButtonCompose : "com.google.pay.button:compose-pay-button:${versions.payButtonCompose}",
Expand Down Expand Up @@ -218,7 +215,6 @@ ext.testLibs = [
junit : "org.jetbrains.kotlin:kotlin-test-junit:${versions.kotlin}",
test : "org.jetbrains.kotlin:kotlin-test:${versions.kotlin}",
],
mavericks : "com.airbnb.android:mavericks-testing:${versions.mavericks}",
mockito : [
android: "org.mockito:mockito-android:${versions.mockito}",
core : "org.mockito:mockito-core:${versions.mockito}",
Expand Down
100 changes: 39 additions & 61 deletions example/dependencies/dependencies.txt

Large diffs are not rendered by default.

210 changes: 94 additions & 116 deletions financial-connections-example/dependencies/dependencies.txt

Large diffs are not rendered by default.

3 changes: 0 additions & 3 deletions financial-connections/api/financial-connections.api
Original file line number Diff line number Diff line change
Expand Up @@ -504,9 +504,6 @@ public final class com/stripe/android/financialconnections/features/success/Comp
public final fun getLambda-1$financial_connections_release ()Lkotlin/jvm/functions/Function3;
}

public final class com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityArgs$Companion {
}

public final class com/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityArgs$ForData$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/financialconnections/launcher/FinancialConnectionsSheetActivityArgs$ForData;
Expand Down
3 changes: 0 additions & 3 deletions financial-connections/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,6 @@ dependencies {
implementation libs.kotlin.coroutines
implementation libs.kotlin.coroutinesAndroid
implementation libs.kotlin.serialization
implementation libs.mavericks
implementation libs.mavericksCompose

debugImplementation libs.compose.uiTestManifest
debugImplementation libs.compose.uiTooling
Expand All @@ -64,7 +62,6 @@ dependencies {
testImplementation testLibs.mockito.core
testImplementation testLibs.mockito.inline
testImplementation testLibs.mockito.kotlin
testImplementation testLibs.mavericks
testImplementation testLibs.robolectric
testImplementation testLibs.testParameterInjector
testImplementation testLibs.turbine
Expand Down
24 changes: 0 additions & 24 deletions financial-connections/consumer-rules.txt
Original file line number Diff line number Diff line change
Expand Up @@ -3,27 +3,3 @@
*;
}

# ------------------------
# MvRx Config
# -----------------------

# MavericksViewModel loads the Companion class via reflection and thus we need to make sure we keep
# the name of the Companion object.
-keepclassmembers class ** extends com.airbnb.mvrx.MavericksViewModel {
** Companion;
}

# Members of the Kotlin data classes used as the state in Mavericks are read via Kotlin reflection which cause trouble
# with Proguard if they are not kept.
# During reflection cache warming also the types are accessed via reflection. Need to keep them too.
-keepclassmembers,includedescriptorclasses,allowobfuscation class ** implements com.airbnb.mvrx.MavericksState {
*;
}

# The MavericksState object and the names classes that implement the MavericksState interface need to be
# kept as they are accessed via reflection.
-keepnames class com.airbnb.mvrx.MavericksState
-keepnames class * implements com.airbnb.mvrx.MavericksState

# MavericksViewModelFactory is referenced via reflection using the Companion class name.
-keepnames class * implements com.airbnb.mvrx.MavericksViewModelFactory
253 changes: 115 additions & 138 deletions financial-connections/dependencies/dependencies.txt

Large diffs are not rendered by default.

3 changes: 2 additions & 1 deletion financial-connections/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
<ID>MagicNumber:LinkAccountPickerScreen.kt$3</ID>
<ID>MagicNumber:ManualEntryViewModel.kt$ManualEntryViewModel$4</ID>
<ID>MagicNumber:SharedPartnerAuth.kt$.50f</ID>
<ID>MatchingDeclarationName:FinancialConnectionsViewModel.kt$Async&lt;out T></ID>
<ID>MatchingDeclarationName:ServerDrivenUi.kt$BulletUI</ID>
<ID>MatchingDeclarationName:Type.kt$FinancialConnectionsTypography</ID>
<ID>MaxLineLength:FinancialConnectionsUrls.kt$FinancialConnectionsUrls.DataPolicy$const val merchant = "https://support.stripe.com/user/questions/what-data-does-stripe-access-from-my-linked-financial-account"</ID>
Expand All @@ -36,6 +37,6 @@
<ID>SwallowedException:PostAuthorizationSession.kt$PostAuthorizationSession$e: StripeException</ID>
<ID>TooManyFunctions:FinancialConnectionsManifestRepository.kt$FinancialConnectionsManifestRepository</ID>
<ID>TooManyFunctions:FinancialConnectionsManifestRepository.kt$FinancialConnectionsManifestRepositoryImpl : FinancialConnectionsManifestRepository</ID>
<ID>TooManyFunctions:FinancialConnectionsSheetNativeViewModel.kt$FinancialConnectionsSheetNativeViewModel : MavericksViewModelTopAppBarHost</ID>
<ID>TooManyFunctions:FinancialConnectionsSheetNativeViewModel.kt$FinancialConnectionsSheetNativeViewModel : FinancialConnectionsViewModelTopAppBarHost</ID>
</CurrentIssues>
</SmellBaseline>
6 changes: 0 additions & 6 deletions financial-connections/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -59,12 +59,6 @@
android:exported="false"
android:windowSoftInputMode="adjustResize"
android:theme="@style/StripeDefaultTheme" />

<provider
android:name=".appinitializer.FinancialConnectionsInitializer"
android:authorities="${applicationId}.financialconnections-init"
android:exported="false"
android:multiprocess="true" />
</application>

<!--
Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,14 @@
package com.stripe.android.financialconnections

import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Bundle
import android.widget.Toast
import androidx.activity.addCallback
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContracts.StartActivityForResult
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
Expand All @@ -15,9 +17,10 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import com.airbnb.mvrx.Mavericks
import com.airbnb.mvrx.MavericksView
import com.airbnb.mvrx.withState
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.SavedStateHandle
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.FinishWithResult
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.OpenAuthFlowWithUrl
import com.stripe.android.financialconnections.FinancialConnectionsSheetViewEffect.OpenNativeAuthFlow
Expand All @@ -28,14 +31,16 @@ import com.stripe.android.financialconnections.launcher.FinancialConnectionsShee
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetNativeActivityArgs
import com.stripe.android.financialconnections.ui.FinancialConnectionsSheetNativeActivity
import com.stripe.android.financialconnections.ui.theme.FinancialConnectionsTheme
import com.stripe.android.financialconnections.utils.argsOrNull
import com.stripe.android.financialconnections.utils.viewModelLazy
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch

internal class FinancialConnectionsSheetActivity : AppCompatActivity(), MavericksView {
internal class FinancialConnectionsSheetActivity : AppCompatActivity() {

val viewModel: FinancialConnectionsSheetViewModel by viewModelLazy()

val args by argsOrNull<FinancialConnectionsSheetActivityArgs>()
val viewModel: FinancialConnectionsSheetViewModel by viewModels(
factoryProducer = { FinancialConnectionsSheetViewModel.Factory }
)

private val startBrowserForResult = registerForActivityResult(StartActivityForResult()) {
viewModel.onBrowserActivityResult()
Expand All @@ -49,10 +54,10 @@ internal class FinancialConnectionsSheetActivity : AppCompatActivity(), Maverick

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
if (args == null) {
if (getArgs(intent) == null) {
finish()
} else {
viewModel.onEach { postInvalidate() }
observeViewEffects()
browserManager = BrowserManager(application)
if (savedInstanceState != null) viewModel.onActivityRecreated()
}
Expand Down Expand Up @@ -91,46 +96,72 @@ internal class FinancialConnectionsSheetActivity : AppCompatActivity(), Maverick
/**
* handle state changes here.
*/
override fun invalidate() {
withState(viewModel) { state ->
state.viewEffect?.let { viewEffect ->
when (viewEffect) {
is OpenAuthFlowWithUrl -> startBrowserForResult.launch(
browserManager.createBrowserIntentForUrl(
uri = Uri.parse(viewEffect.url)

private fun observeViewEffects() = lifecycleScope.launch {
repeatOnLifecycle(Lifecycle.State.STARTED) {
viewModel.stateFlow
.map { it.viewEffect }
.distinctUntilChanged()
.filterNotNull()
.collect { viewEffect ->
when (viewEffect) {
is OpenAuthFlowWithUrl -> startBrowserForResult.launch(
browserManager.createBrowserIntentForUrl(
uri = Uri.parse(viewEffect.url)
)
)
)

is FinishWithResult -> {
viewEffect.finishToast?.let {
Toast.makeText(this, it, Toast.LENGTH_LONG).show()
is FinishWithResult -> {
viewEffect.finishToast?.let { resId ->
Toast.makeText(
this@FinancialConnectionsSheetActivity,
resId,
Toast.LENGTH_LONG
).show()
}
finishWithResult(viewEffect.result)
}
finishWithResult(viewEffect.result)
}

is OpenNativeAuthFlow -> startNativeAuthFlowForResult.launch(
Intent(
this,
FinancialConnectionsSheetNativeActivity::class.java
).also {
it.addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
it.putExtra(
Mavericks.KEY_ARG,
FinancialConnectionsSheetNativeActivityArgs(
initialSyncResponse = viewEffect.initialSyncResponse,
configuration = viewEffect.configuration
)
)
}
)
is OpenNativeAuthFlow -> openNativeAuthFlow(viewEffect)
}
viewModel.onViewEffectLaunched()
}
viewModel.onViewEffectLaunched()
}
}
}

private fun openNativeAuthFlow(viewEffect: OpenNativeAuthFlow) {
startNativeAuthFlowForResult.launch(
FinancialConnectionsSheetNativeActivity.intent(
context = this,
args = FinancialConnectionsSheetNativeActivityArgs(
initialSyncResponse = viewEffect.initialSyncResponse,
configuration = viewEffect.configuration
)
)
)
}

private fun finishWithResult(result: FinancialConnectionsSheetActivityResult) {
setResult(RESULT_OK, Intent().putExtras(result.toBundle()))
finish()
}

companion object {

private const val EXTRA_ARGS = "FinancialConnectionsSheetActivityArgs"
fun intent(context: Context, args: FinancialConnectionsSheetActivityArgs): Intent {
return Intent(context, FinancialConnectionsSheetActivity::class.java).apply {
addFlags(Intent.FLAG_ACTIVITY_NO_ANIMATION)
putExtra(EXTRA_ARGS, args)
}
}

fun getArgs(savedStateHandle: SavedStateHandle): FinancialConnectionsSheetActivityArgs? {
return savedStateHandle.get<FinancialConnectionsSheetActivityArgs>(EXTRA_ARGS)
}

fun getArgs(intent: Intent): FinancialConnectionsSheetActivityArgs? {
return intent.getParcelableExtra(EXTRA_ARGS)
}
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.stripe.android.financialconnections

import android.os.Bundle
import androidx.annotation.StringRes
import com.airbnb.mvrx.MavericksState
import com.airbnb.mvrx.PersistState
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetActivityArgs
import com.stripe.android.financialconnections.launcher.FinancialConnectionsSheetActivityResult
import com.stripe.android.financialconnections.model.FinancialConnectionsSessionManifest
Expand All @@ -13,15 +12,28 @@ import com.stripe.android.financialconnections.model.SynchronizeSessionResponse
*/
internal data class FinancialConnectionsSheetState(
val initialArgs: FinancialConnectionsSheetActivityArgs,
val activityRecreated: Boolean = false,
@PersistState val manifest: FinancialConnectionsSessionManifest? = null,
@PersistState val webAuthFlowStatus: AuthFlowStatus = AuthFlowStatus.NONE,
val viewEffect: FinancialConnectionsSheetViewEffect? = null
) : MavericksState {
val activityRecreated: Boolean,
val manifest: FinancialConnectionsSessionManifest?,
val webAuthFlowStatus: AuthFlowStatus,
val viewEffect: FinancialConnectionsSheetViewEffect?
) {

val sessionSecret: String
get() = initialArgs.configuration.financialConnectionsSessionClientSecret

/**
* Constructor used to build the initial state.
*/
constructor(args: FinancialConnectionsSheetActivityArgs, savedState: Bundle?) : this(
initialArgs = args,
activityRecreated = false,
manifest = savedState?.getParcelable(KEY_MANIFEST),
webAuthFlowStatus = savedState?.getSerializable(KEY_WEB_AUTH_FLOW_STATUS)
as? AuthFlowStatus
?: AuthFlowStatus.NONE,
viewEffect = null
)

enum class AuthFlowStatus {
/**
* AuthFlow is happening outside of the SDK (app2app, web browser, etc).
Expand All @@ -47,12 +59,11 @@ internal data class FinancialConnectionsSheetState(
NONE
}

/**
* Constructor used by Mavericks to build the initial state.
*/
constructor(args: FinancialConnectionsSheetActivityArgs) : this(
initialArgs = args
)
companion object {
const val KEY_SAVED_STATE = "financial_connections_sheet_state"
const val KEY_MANIFEST = "financial_connections_sheet_manifest"
const val KEY_WEB_AUTH_FLOW_STATUS = "financial_connections_sheet_web_auth_flow_status"
}
}

/**
Expand Down
Loading
Loading