Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/master' into poc-mavericks
Browse files Browse the repository at this point in the history
  • Loading branch information
carlosmuvi-stripe committed May 16, 2022
2 parents da1207b + 9045019 commit 041e91a
Show file tree
Hide file tree
Showing 30 changed files with 401 additions and 78 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
# CHANGELOG
## XX.X.X - 2022-05.XX
This release adds `us_bank_account` PaymentMethod to PaymentSheet.

### PaymentSheet
* [FIXED][5011](https://github.com/stripe/stripe-android/pull/5011) fix flying payment sheet by downgrading material from 1.6 to 1.5
* [ADDED][4964](https://github.com/stripe/stripe-android/pull/4964) `us_bank_account` PaymentMethod is now available in PaymentSheet

## 20.2.2 - 2022-05-09
This release contains bug fixes in PaymentSheet.

Expand Down
6 changes: 4 additions & 2 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ buildscript {
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlinVersion"
classpath "org.jetbrains.dokka:dokka-gradle-plugin:$dokkaVersion"
classpath "org.jetbrains.kotlinx:binary-compatibility-validator:0.9.0"
classpath 'com.karumi:shot:5.14.0'
classpath 'com.karumi:shot:5.14.1'
classpath buildLibs.detektGradlePlugin
}
}
Expand Down Expand Up @@ -78,7 +78,9 @@ ext {
kotlinSerializationVersion = '1.3.3'
flowlayoutVersion = '0.23.1'
ktlintVersion = '0.45.2'
materialVersion = '1.6.0'
// material 1.6 causes paymentsheet to not render correctly.
// see here: https://github.com/material-components/material-components-android/issues/2702
materialVersion = '1.5.0'
daggerVersion = '2.41'
playServicesWalletVersion = '19.1.0'

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -160,7 +160,13 @@ class Camera1Adapter(
CameraPreviewImage(
image = NV21Image(imageWidth, imageHeight, bytes)
.toBitmap(getRenderScript(activity))
.rotate(mRotation.toFloat()),
.rotate(
if (currentCameraId == Camera.CameraInfo.CAMERA_FACING_BACK) {
mRotation.toFloat()
} else {
-mRotation.toFloat()
}
),
viewBounds = Rect(
previewView.left,
previewView.top,
Expand Down Expand Up @@ -405,7 +411,7 @@ class Camera1Adapter(
private fun setCameraDisplayOrientation(activity: Activity) {
val camera = mCamera ?: return
val info = Camera.CameraInfo()
Camera.getCameraInfo(Camera.CameraInfo.CAMERA_FACING_BACK, info)
Camera.getCameraInfo(currentCameraId, info)

val rotation = activity.windowManager.defaultDisplay.rotation
val degrees = rotation.rotationToDegrees()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,16 @@
package com.stripe.android.stripecardscan.framework.util
package com.stripe.android.camera.framework.util

import androidx.annotation.CheckResult
import androidx.annotation.RestrictTo
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import java.util.LinkedList

/**
* Save data frames for later retrieval.
*/
internal abstract class FrameSaver<Identifier, Frame, MetaData> {
@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP)
abstract class FrameSaver<Identifier, Frame, MetaData> {

private val saveFrameMutex = Mutex()
private val savedFrames = mutableMapOf<Identifier, LinkedList<Frame>>()
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package com.stripe.android.stripecardscan.framework.util
package com.stripe.android.camera.framework.util

import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.test.runTest
Expand Down
9 changes: 7 additions & 2 deletions financial-connections-example/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,19 @@
android:supportsRtl="true"
android:theme="@style/AppTheme">
<activity
android:name="com.stripe.android.financialconnections.example.FinancialConnectionsExampleActivity"
android:theme="@style/AppTheme.NoActionBar"
android:name=".FinancialConnectionsLauncherActivity"
android:exported="true">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name="com.stripe.android.financialconnections.example.FinancialConnectionsDataExampleActivity"
android:theme="@style/AppTheme.NoActionBar" />
<activity
android:name="com.stripe.android.financialconnections.example.FinancialConnectionsBankAccountTokenExampleActivity"
android:theme="@style/AppTheme.NoActionBar" />
</application>

</manifest>
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package com.stripe.android.financialconnections.example

import android.os.Bundle
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.stripe.android.financialconnections.example.databinding.ActivityFinancialconnectionsExampleBinding
import com.stripe.android.financialconnections.FinancialConnectionsSheet
import com.stripe.android.financialconnections.example.FinancialConnectionsExampleViewEffect.OpenFinancialConnectionsSheetExample

class FinancialConnectionsBankAccountTokenExampleActivity : AppCompatActivity() {

private val viewModel by viewModels<FinancialConnectionsExampleViewModel>()

private lateinit var financialConnectionsSheet: FinancialConnectionsSheet

override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
with(ActivityFinancialconnectionsExampleBinding.inflate(layoutInflater)) {
setContentView(root)
setupViews()
observeViews()
observeState()
}
}

private fun ActivityFinancialconnectionsExampleBinding.setupViews() {
toolbar.setTitle(R.string.collect_bank_account_for_bank_account_token_title)
setSupportActionBar(toolbar)
financialConnectionsSheet = FinancialConnectionsSheet.createForBankAccountToken(
activity = this@FinancialConnectionsBankAccountTokenExampleActivity,
callback = viewModel::onFinancialConnectionsSheetForBankAccountTokenResult
)
}

private fun ActivityFinancialconnectionsExampleBinding.observeViews() {
launchConnectionsSheet.setOnClickListener { viewModel.startFinancialConnectionsSessionForToken() }
}

private fun ActivityFinancialconnectionsExampleBinding.observeState() {
lifecycleScope.launchWhenStarted {
viewModel.state.collect {
bindState(it, this@observeState)
}
}
lifecycleScope.launchWhenStarted {
viewModel.viewEffect.collect {
when (it) {
is OpenFinancialConnectionsSheetExample -> financialConnectionsSheet.present(it.configuration)
}
}
}
}

private fun bindState(
financialConnectionsExampleState: FinancialConnectionsExampleState,
viewBinding: ActivityFinancialconnectionsExampleBinding
) {
viewBinding.status.text = financialConnectionsExampleState.status
viewBinding.launchConnectionsSheet.isEnabled =
financialConnectionsExampleState.loading.not()
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,10 @@ import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.lifecycleScope
import com.stripe.android.financialconnections.example.databinding.ActivityFinancialconnectionsExampleBinding
import com.stripe.android.financialconnections.example.FinancialConnectionsExampleViewEffect.OpenConnectionsSheetExample
import com.stripe.android.financialconnections.FinancialConnectionsSheet
import com.stripe.android.financialconnections.FinancialConnectionsSheet
import com.stripe.android.financialconnections.example.FinancialConnectionsExampleViewEffect.OpenFinancialConnectionsSheetExample

class FinancialConnectionsExampleActivity : AppCompatActivity() {
class FinancialConnectionsDataExampleActivity : AppCompatActivity() {

private val viewModel by viewModels<FinancialConnectionsExampleViewModel>()

Expand All @@ -25,15 +25,16 @@ class FinancialConnectionsExampleActivity : AppCompatActivity() {
}

private fun ActivityFinancialconnectionsExampleBinding.setupViews() {
toolbar.setTitle(R.string.collect_bank_account_for_data_title)
setSupportActionBar(toolbar)
financialConnectionsSheet = FinancialConnectionsSheet.create(
activity = this@FinancialConnectionsExampleActivity,
activity = this@FinancialConnectionsDataExampleActivity,
callback = viewModel::onFinancialConnectionsSheetResult
)
}

private fun ActivityFinancialconnectionsExampleBinding.observeViews() {
launchConnectionsSheet.setOnClickListener { viewModel.startFinancialConnectionsSession() }
launchConnectionsSheet.setOnClickListener { viewModel.startFinancialConnectionsSessionForData() }
}

private fun ActivityFinancialconnectionsExampleBinding.observeState() {
Expand All @@ -45,7 +46,7 @@ class FinancialConnectionsExampleActivity : AppCompatActivity() {
lifecycleScope.launchWhenStarted {
viewModel.viewEffect.collect {
when (it) {
is OpenConnectionsSheetExample -> financialConnectionsSheet.present(it.configuration)
is OpenFinancialConnectionsSheetExample -> financialConnectionsSheet.present(it.configuration)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.stripe.android.financialconnections.example
import com.stripe.android.financialconnections.FinancialConnectionsSheet

sealed class FinancialConnectionsExampleViewEffect {
data class OpenConnectionsSheetExample(
data class OpenFinancialConnectionsSheetExample(
val configuration: FinancialConnectionsSheet.Configuration
) : FinancialConnectionsExampleViewEffect()
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,16 @@ package com.stripe.android.financialconnections.example

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.stripe.android.financialconnections.FinancialConnectionsSheetForTokenResult
import com.stripe.android.financialconnections.FinancialConnectionsSheet
import com.stripe.android.financialconnections.FinancialConnectionsSheetResult
import com.stripe.android.financialconnections.FinancialConnectionsSheetResult.Canceled
import com.stripe.android.financialconnections.FinancialConnectionsSheetResult.Completed
import com.stripe.android.financialconnections.FinancialConnectionsSheetResult.Failed
import com.stripe.android.financialconnections.example.FinancialConnectionsExampleViewEffect.OpenConnectionsSheetExample
import com.stripe.android.financialconnections.example.FinancialConnectionsExampleViewEffect.OpenFinancialConnectionsSheetExample
import com.stripe.android.financialconnections.example.data.BackendRepository
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.update
import kotlinx.coroutines.launch

class FinancialConnectionsExampleViewModel : ViewModel() {
Expand All @@ -25,25 +24,15 @@ class FinancialConnectionsExampleViewModel : ViewModel() {
private val _viewEffect = MutableSharedFlow<FinancialConnectionsExampleViewEffect>()
val viewEffect: SharedFlow<FinancialConnectionsExampleViewEffect> = _viewEffect

fun startFinancialConnectionsSession() {
fun startFinancialConnectionsSessionForData() {
viewModelScope.launch {
setState {
copy(
loading = true,
status = "Fetching financial connections session from example backend!"
)
}
showLoadingWithMessage("Fetching link account session from example backend!")
kotlin.runCatching { repository.createLinkAccountSession() }
// Success creating session: open FinancialConnectionsSheet with received secret
// Success creating session: open the financial connections sheet with received secret
.onSuccess {
setState {
copy(
loading = false,
status = "Session created, opening FinancialConnectionsSheet."
)
}
showLoadingWithMessage("Session created, opening FinancialConnectionsSheet.")
_viewEffect.emit(
OpenConnectionsSheetExample(
OpenFinancialConnectionsSheetExample(
configuration = FinancialConnectionsSheet.Configuration(
it.clientSecret,
it.publishableKey
Expand All @@ -52,38 +41,73 @@ class FinancialConnectionsExampleViewModel : ViewModel() {
)
}
// Error retrieving session: display error.
.onFailure {
setState {
copy(
loading = false,
status = "Error retrieving financial connections session: $it"
.onFailure(::showError)
}
}

fun startFinancialConnectionsSessionForToken() {
viewModelScope.launch {
showLoadingWithMessage("Fetching link account session from example backend!")
kotlin.runCatching { repository.createLinkAccountSessionForToken() }
// Success creating session: open the financial connections sheet with received secret
.onSuccess {
showLoadingWithMessage("Session created, opening FinancialConnectionsSheet.")
_viewEffect.emit(
OpenFinancialConnectionsSheetExample(
configuration = FinancialConnectionsSheet.Configuration(
financialConnectionsSessionClientSecret = it.clientSecret,
publishableKey = it.publishableKey
)
)
}
)
}
// Error retrieving session: display error.
.onFailure(::showError)
}
}

private fun showError(error: Throwable) {
_state.update {
it.copy(
loading = false,
status = "Error starting linked account session: $error"
)
}
}

private fun showLoadingWithMessage(message: String) {
_state.update {
it.copy(
loading = true,
status = message
)
}
}

fun onFinancialConnectionsSheetResult(result: FinancialConnectionsSheetResult) {
val statusText = when (result) {
is Completed -> {
is FinancialConnectionsSheetResult.Completed -> {
val accountList = result.financialConnectionsSession.accounts
accountList.data.joinToString("\n") {
"${it.institutionName} - ${it.displayName} - ${it.last4} - ${it.category}/${it.subcategory}"
}
}
is Failed -> "Failed! ${result.error}"
is Canceled -> "Cancelled!"
}
viewModelScope.launch {
setState { copy(status = statusText) }
is FinancialConnectionsSheetResult.Failed -> "Failed! ${result.error}"
is FinancialConnectionsSheetResult.Canceled -> "Cancelled!"
}
_state.update { it.copy(loading = false, status = statusText) }
}

/**
* Helper function to mutate state.
*/
private suspend fun setState(block: FinancialConnectionsExampleState.() -> FinancialConnectionsExampleState) {
val newState = block(state.value)
_state.emit(newState)
fun onFinancialConnectionsSheetForBankAccountTokenResult(
result: FinancialConnectionsSheetForTokenResult
) {
val statusText = when (result) {
is FinancialConnectionsSheetForTokenResult.Completed -> {
"Token ${result.token.id} generated for bank account ${result.token.bankAccount?.last4}"
}
is FinancialConnectionsSheetForTokenResult.Failed -> "Failed! ${result.error}"
is FinancialConnectionsSheetForTokenResult.Canceled -> "Cancelled!"
}
_state.update { it.copy(loading = false, status = statusText) }
}
}
Loading

0 comments on commit 041e91a

Please sign in to comment.