Skip to content

Commit

Permalink
[Identity] - support consent page for Butter 2.0 (#7412)
Browse files Browse the repository at this point in the history
  • Loading branch information
ccen-stripe authored Oct 11, 2023
1 parent 23fff4c commit 971e015
Show file tree
Hide file tree
Showing 20 changed files with 884 additions and 483 deletions.
15 changes: 15 additions & 0 deletions identity/api/identity.api
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,14 @@ public final class com/stripe/android/identity/networking/models/VerificationPag
public synthetic fun newArray (I)[Ljava/lang/Object;
}

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

public final class com/stripe/android/identity/networking/models/VerificationPageStaticContentBottomSheetContent$Creator : android/os/Parcelable$Creator {
public fun <init> ()V
public final fun createFromParcel (Landroid/os/Parcel;)Lcom/stripe/android/identity/networking/models/VerificationPageStaticContentBottomSheetContent;
Expand Down Expand Up @@ -334,6 +342,13 @@ public final class com/stripe/android/identity/ui/ComposableSingletons$BottomShe
public final fun getLambda-2$identity_release ()Lkotlin/jvm/functions/Function2;
}

public final class com/stripe/android/identity/ui/ComposableSingletons$ConsentScreenKt {
public static final field INSTANCE Lcom/stripe/android/identity/ui/ComposableSingletons$ConsentScreenKt;
public static field lambda-1 Lkotlin/jvm/functions/Function2;
public fun <init> ()V
public final fun getLambda-1$identity_release ()Lkotlin/jvm/functions/Function2;
}

public final class com/stripe/android/identity/ui/ComposableSingletons$DebugScreenKt {
public static final field INSTANCE Lcom/stripe/android/identity/ui/ComposableSingletons$DebugScreenKt;
public static field lambda-1 Lkotlin/jvm/functions/Function3;
Expand Down
6 changes: 2 additions & 4 deletions identity/detekt-baseline.xml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@
<ID>LongMethod:AddressSection.kt$@Composable internal fun AddressSection( enabled: Boolean, identityViewModel: IdentityViewModel, addressCountries: List&lt;Country>, addressNotListedText: String, navController: NavController, onAddressCollected: (Resource&lt;RequiredInternationalAddress>) -> Unit )</ID>
<ID>LongMethod:CameraScreenLaunchedEffect.kt$@Composable internal fun CameraScreenLaunchedEffect( identityViewModel: IdentityViewModel, identityScanViewModel: IdentityScanViewModel, verificationPage: VerificationPage, navController: NavController, cameraManager: IdentityCameraManager, onCameraReady: () -> Unit )</ID>
<ID>LongMethod:ConfirmationScreen.kt$@Composable internal fun ConfirmationScreen( navController: NavController, identityViewModel: IdentityViewModel, verificationFlowFinishable: VerificationFlowFinishable )</ID>
<ID>LongMethod:ConsentScreen.kt$@Composable private fun ConsentHeader( merchantLogoUri: Uri, title: String, privacyPolicy: String, timeEstimate: String )</ID>
<ID>LongMethod:ConsentScreen.kt$@Composable private fun SuccessUI( merchantLogoUri: Uri, verificationPage: VerificationPage, onConsentAgreed: () -> Unit, onConsentDeclined: () -> Unit )</ID>
<ID>LongMethod:ConsentScreen.kt$@Composable private fun SuccessUI( merchantLogoUri: Uri, consentPage: VerificationPageStaticContentConsentPage, bottomSheets: Map&lt;String, VerificationPageStaticContentBottomSheetContent>?, visitedIndividualWelcomePage: Boolean, onConsentAgreed: () -> Unit, onConsentDeclined: () -> Unit )</ID>
<ID>LongMethod:ConsentWelcomeHeader.kt$@Composable internal fun ConsentWelcomeHeader( modifier: Modifier = Modifier, merchantLogoUri: Uri, title: String?, showLogos: Boolean = true )</ID>
<ID>LongMethod:DebugScreen.kt$@Composable internal fun CompleteWithTestDataSection( onClickSubmit: (CompleteOption) -> Unit )</ID>
<ID>LongMethod:DebugScreen.kt$@Composable internal fun DebugScreen( navController: NavController, identityViewModel: IdentityViewModel, verificationFlowFinishable: VerificationFlowFinishable )</ID>
<ID>LongMethod:DocSelectionScreen.kt$@Composable internal fun DocSelectionScreen( navController: NavController, identityViewModel: IdentityViewModel, cameraPermissionEnsureable: CameraPermissionEnsureable )</ID>
Expand Down Expand Up @@ -78,8 +78,6 @@
<ID>TooManyFunctions:IdentityRepository.kt$IdentityRepository</ID>
<ID>TooManyFunctions:IdentityViewModel.kt$IdentityViewModel : AndroidViewModel</ID>
<ID>TooManyFunctions:UploadDestinations.kt$DocumentUploadDestination$Companion</ID>
<ID>TopLevelPropertyNaming:ConfirmationScreen.kt$internal const val confirmationConfirmButtonTag = "ConfirmButton"</ID>
<ID>TopLevelPropertyNaming:ConfirmationScreen.kt$internal const val confirmationTitleTag = "ConfirmationTitle"</ID>
<ID>TopLevelPropertyNaming:DocSelectionScreen.kt$internal const val docSelectionTitleTag = "Title"</ID>
<ID>TopLevelPropertyNaming:DocSelectionScreen.kt$internal const val singleSelectionTag = "SingleSelection"</ID>
<ID>UnnecessaryAbstractClass:IdentityCommonModule.kt$IdentityCommonModule$IdentityCommonModule</ID>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ package com.stripe.android.identity.networking
internal const val BASE_URL = "https://api.stripe.com/v1"
internal const val IDENTITY_VERIFICATION_PAGES = "identity/verification_pages"
internal const val IDENTITY_STRIPE_API_VERSION_WITH_BETA_HEADER =
"2020-08-27;identity_client_api=v4"
"2020-08-27;identity_client_api=v5"

internal const val SUBMIT = "submit"
internal const val DATA = "data"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,9 @@ internal data class VerificationPage(
@SerialName("unsupported_client")
val unsupportedClient: Boolean,
@SerialName("welcome")
val welcome: VerificationPageStaticContentTextPage? = null
val welcome: VerificationPageStaticContentTextPage? = null,
@SerialName("bottomsheet")
val bottomSheet: Map<String, VerificationPageStaticContentBottomSheetContent>? = null
) : Parcelable {
@Serializable
internal enum class Status {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ internal fun VerificationPageIconType.getResourceId() =
when (this) {
VerificationPageIconType.CLOUD -> R.drawable.stripe_cloud_icon
VerificationPageIconType.DOCUMENT -> R.drawable.stripe_document_icon
VerificationPageIconType.CREATE_IDENTITY_VERIFICATION -> R.drawable.stripe_create_identity_verification_icon
VerificationPageIconType.CREATE_IDENTITY_VERIFICATION ->
R.drawable.stripe_create_identity_verification_icon
VerificationPageIconType.LOCK -> R.drawable.stripe_lock_icon
VerificationPageIconType.MOVED -> R.drawable.stripe_moved_icon
VerificationPageIconType.WALLET -> R.drawable.stripe_wallet_icon
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.stripe.android.identity.networking.models

import android.os.Parcelable
import kotlinx.parcelize.Parcelize
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable

@Serializable
@Parcelize
internal data class VerificationPageStaticConsentLineContent(
@SerialName("icon")
val icon: VerificationPageIconType,
@SerialName("content")
val content: String
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,14 @@ import kotlinx.serialization.Serializable
internal data class VerificationPageStaticContentConsentPage(
@SerialName("accept_button_text")
val acceptButtonText: String,
@SerialName("body")
val body: String,
@SerialName("decline_button_text")
val declineButtonText: String,
@SerialName("scroll_to_continue_button_text")
val scrollToContinueButtonText: String,
@SerialName("title")
val title: String?,
@SerialName("privacy_policy")
val privacyPolicy: String?,
@SerialName("time_estimate")
val timeEstimate: String?
val privacyPolicy: String,
@SerialName("lines")
val lines: List<VerificationPageStaticConsentLineContent>
) : Parcelable
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,10 @@ import kotlinx.serialization.Serializable
internal data class VerificationPageStaticContentIndividualWelcomePage(
@SerialName("get_started_button_text")
val getStartedButtonText: String,
@SerialName("body")
val body: String,
@SerialName("title")
val title: String,
@SerialName("privacy_policy")
val privacyPolicy: String,
@SerialName("time_estimate")
val timeEstimate: String
@SerialName("lines")
val lines: List<VerificationPageStaticConsentLineContent>
) : Parcelable
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.stripe.android.identity.ui

import android.content.Intent
import android.net.Uri
import android.util.Log
import android.webkit.URLUtil
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.TextStyle
import androidx.compose.ui.text.style.TextDecoration
import androidx.lifecycle.viewmodel.compose.viewModel
import com.stripe.android.identity.networking.STRIPE_BOTTOM_SHEET
import com.stripe.android.identity.networking.models.VerificationPageStaticContentBottomSheetContent
import com.stripe.android.identity.viewmodel.BottomSheetViewModel
import com.stripe.android.uicore.text.HtmlWithCustomOnClick

/**
* Draw Html with the ability to open a web link or bottomsheet
*/
@Composable
@ExperimentalMaterialApi
internal fun BottomSheetHTML(
html: String,
modifier: Modifier = Modifier,
bottomSheets: Map<String, VerificationPageStaticContentBottomSheetContent>?,
color: Color = Color.Unspecified,
style: TextStyle,
urlSpanStyle: SpanStyle = SpanStyle(textDecoration = TextDecoration.Underline)
) {
val context = LocalContext.current
val bottomSheetViewModel = viewModel<BottomSheetViewModel>()
HtmlWithCustomOnClick(
html = html,
modifier = modifier,
color = color,
style = style,
urlSpanStyle = urlSpanStyle
) { annotatedStringRanges ->
annotatedStringRanges.firstOrNull()?.item?.let { urlString ->
when {
(URLUtil.isNetworkUrl(urlString)) -> {
val openURL = Intent(Intent.ACTION_VIEW)
openURL.data = Uri.parse(urlString)
context.startActivity(openURL)
}

urlString.startsWith(STRIPE_BOTTOM_SHEET) -> {
val bottomSheetId = urlString.substringAfterLast('/')
bottomSheets?.get(bottomSheetId)?.let { bottomSheetContent ->
bottomSheetViewModel.showBottomSheet(bottomSheetContent)
} ?: run {
Log.e(
BottomSheetHTMLTAG,
"Fail to present buttomsheet with id $bottomSheetId"
)
}
}

else -> {
Log.e(BottomSheetHTMLTAG, "unknown url string: $urlString")
}
}
}
}
}

internal const val BottomSheetHTMLTAG = "BottomSheetHTML"
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ internal fun ConfirmationScreen(
vertical = dimensionResource(id = R.dimen.stripe_item_vertical_margin)
)
.semantics {
testTag = confirmationTitleTag
testTag = CONFIRMATION_TITLE_TAG
},
fontSize = 24.sp,
fontWeight = FontWeight.Bold
Expand All @@ -113,7 +113,7 @@ internal fun ConfirmationScreen(
modifier = Modifier
.padding(bottom = dimensionResource(id = R.dimen.stripe_item_vertical_margin))
.semantics {
testTag = BODY_TAG
testTag = CONFIRMATION_BODY_TAG
},
color = MaterialTheme.colors.onBackground,
urlSpanStyle = SpanStyle(
Expand All @@ -132,7 +132,7 @@ internal fun ConfirmationScreen(
modifier = Modifier
.fillMaxWidth()
.semantics {
testTag = confirmationConfirmButtonTag
testTag = CONFIRMATION_BUTTON_TAG
}
) {
Text(text = successPage.buttonText.uppercase())
Expand All @@ -141,5 +141,6 @@ internal fun ConfirmationScreen(
}
}

internal const val confirmationTitleTag = "ConfirmationTitle"
internal const val confirmationConfirmButtonTag = "ConfirmButton"
internal const val CONFIRMATION_TITLE_TAG = "ConfirmationTitle"
internal const val CONFIRMATION_BUTTON_TAG = "ConfirmButton"
internal const val CONFIRMATION_BODY_TAG = "Body"
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
package com.stripe.android.identity.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.LocalTextStyle
import androidx.compose.material.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.SpanStyle
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.stripe.android.identity.R
import com.stripe.android.identity.networking.models.VerificationPageStaticConsentLineContent
import com.stripe.android.identity.networking.models.VerificationPageStaticContentBottomSheetContent
import com.stripe.android.identity.networking.models.getContentDescriptionId
import com.stripe.android.identity.networking.models.getResourceId

@OptIn(ExperimentalMaterialApi::class)
@Composable
internal fun ConsentLines(
lines: List<VerificationPageStaticConsentLineContent>,
bottomSheets: Map<String, VerificationPageStaticContentBottomSheetContent>?
) {
for (line in lines) {
Row(
modifier = Modifier
.testTag(CONSENT_LINE_TAG)
.padding(top = dimensionResource(id = R.dimen.stripe_item_vertical_margin))
) {
Image(
painter = painterResource(id = line.icon.getResourceId()),
modifier = Modifier
.size(28.dp)
.padding(end = 8.dp),
contentDescription = stringResource(id = line.icon.getContentDescriptionId())
)
BottomSheetHTML(
html = line.content,
color = MaterialTheme.colors.onSurface.copy(
alpha = 0.6f
),
style = LocalTextStyle.current.merge(fontSize = 16.sp),
bottomSheets = bottomSheets,
urlSpanStyle = SpanStyle(
textDecoration = TextDecoration.Underline,
color = MaterialTheme.colors.secondary
)
)
}
}
}

internal const val CONSENT_LINE_TAG = "consentLineTag"
Loading

0 comments on commit 971e015

Please sign in to comment.