diff --git a/payments-ui-core/src/main/java/com/stripe/android/ui/core/PaymentsTheme.kt b/payments-ui-core/src/main/java/com/stripe/android/ui/core/PaymentsTheme.kt index 0574c7dfb20..dcaee5b37fe 100644 --- a/payments-ui-core/src/main/java/com/stripe/android/ui/core/PaymentsTheme.kt +++ b/payments-ui-core/src/main/java/com/stripe/android/ui/core/PaymentsTheme.kt @@ -31,6 +31,7 @@ import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontFamily import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.TextUnit import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import androidx.core.content.res.ResourcesCompat @@ -53,88 +54,35 @@ data class PaymentsColors( ) @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -object PaymentsThemeConfig { - fun colors(isDark: Boolean): PaymentsColors { - return if (isDark) colorsDark else colorsLight - } - - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - object Shapes { - val cornerRadius = 6.dp - val borderStrokeWidth = 1.dp - val borderStrokeWidthSelected = 2.dp - } - - @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) - object Typography { - private val fontWeightBold: Int = FontWeight.Bold.weight - private val fontWeightMedium: Int = FontWeight.Medium.weight - private val fontWeightNormal: Int = FontWeight.Normal.weight - private val fontSizeMultiplier: Float = 1.0F - val fontFamily: Int = R.font.roboto - - // h4 is our largest headline. It is used for the most important labels in our UI - // ex: "Select your payment method" in Payment Sheet. - val h4 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (20.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightBold), - ) - - // h5 is our medium headline label. - // ex: "Pay $50.99" in Payment Sheet's buy button. - val h5 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (16.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightMedium), - letterSpacing = (-0.32).sp - ) - - // h6 is our smallest headline label. - // ex: Section labels in Payment Sheet - val h6 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (13.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightMedium), - letterSpacing = (-0.15).sp - ) - - // body1 is our larger body text. Used for the bulk of our elements and forms. - // ex: the text used in Payment Sheet's text form elements. - val body1 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (14.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightNormal), - ) - - // subtitle1 is our only subtitle size. Used for labeling fields. - // ex: the placeholder texts that appear when you type in Payment Sheet's forms. - val subtitle1 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (14.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightNormal), - letterSpacing = (-0.15).sp - ) +data class PaymentsShapes( + val cornerRadius: Float, + val borderStrokeWidth: Float, + val borderStrokeWidthSelected: Float, +) - // caption is used to label images in payment sheet. - // ex: the labels under our payment method selectors in Payment Sheet. - val caption = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (12.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightMedium) - ) +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +data class PaymentsTypography( + val fontWeightNormal: Int, + val fontWeightMedium: Int, + val fontWeightBold: Int, + val fontSizeMultiplier: Float, + val xxSmallFontSize: TextUnit, + val xSmallFontSize: TextUnit, + val smallFontSize: TextUnit, + val mediumFontSize: TextUnit, + val largeFontSize: TextUnit, + val xLargeFontSize: TextUnit, + @FontRes + val fontFamily: Int +) - // body2 is our smaller body text. Used for less important fields that are not required to - // read. Ex: our mandate texts in Payment Sheet. - val body2 = TextStyle.Default.copy( - fontFamily = FontFamily(Font(fontFamily)), - fontSize = (9.0 * fontSizeMultiplier).sp, - fontWeight = FontWeight(fontWeightNormal), - letterSpacing = (-0.15).sp - ) +@RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) +object PaymentsThemeDefaults { + fun colors(isDark: Boolean): PaymentsColors { + return if (isDark) colorsDark else colorsLight } - private val colorsLight = PaymentsColors( + val colorsLight = PaymentsColors( primary = Color(0xFF007AFF), surface = Color.White, component = Color.White, @@ -149,7 +97,7 @@ object PaymentsThemeConfig { error = Color.Red, ) - private val colorsDark = PaymentsColors( + val colorsDark = PaymentsColors( primary = Color(0xFF0074D4), surface = Color(0xff2e2e2e), component = Color.DarkGray, @@ -163,6 +111,26 @@ object PaymentsThemeConfig { appBarIcon = Color.White, error = Color.Red, ) + + val shapes = PaymentsShapes( + cornerRadius = 6.0f, + borderStrokeWidth = 1.0f, + borderStrokeWidthSelected = 2.0f + ) + + val typography = PaymentsTypography( + fontWeightNormal = FontWeight.Normal.weight, + fontWeightMedium = FontWeight.Medium.weight, + fontWeightBold = FontWeight.Bold.weight, + fontSizeMultiplier = 1.0F, + xxSmallFontSize = 9.sp, + xSmallFontSize = 12.sp, + smallFontSize = 13.sp, + mediumFontSize = 14.sp, + largeFontSize = 16.sp, + xLargeFontSize = 20.sp, + fontFamily = R.font.roboto + ) } @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) @@ -187,22 +155,21 @@ data class PaymentsComposeShapes( @Composable @ReadOnlyComposable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -fun PaymentsThemeConfig.toComposeColors(): PaymentsComposeColors { - val colors = colors(isSystemInDarkTheme()) +fun PaymentsColors.toComposeColors(): PaymentsComposeColors { return PaymentsComposeColors( - component = colors.component, - colorComponentBorder = colors.componentBorder, - colorComponentDivider = colors.componentDivider, - onComponent = colors.onComponent, - subtitle = colors.subtitle, - colorTextCursor = colors.textCursor, - placeholderText = colors.placeholderText, + component = component, + colorComponentBorder = componentBorder, + colorComponentDivider = componentDivider, + onComponent = onComponent, + subtitle = subtitle, + colorTextCursor = textCursor, + placeholderText = placeholderText, material = lightColors( - primary = colors.primary, - surface = colors.surface, - onSurface = colors.onSurface, - error = colors.error, + primary = primary, + surface = surface, + onSurface = onSurface, + error = error, ) ) } @@ -210,13 +177,13 @@ fun PaymentsThemeConfig.toComposeColors(): PaymentsComposeColors { @Composable @ReadOnlyComposable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -fun PaymentsThemeConfig.Shapes.toComposeShapes(): PaymentsComposeShapes { +fun PaymentsShapes.toComposeShapes(): PaymentsComposeShapes { return PaymentsComposeShapes( - borderStrokeWidth = borderStrokeWidth, - borderStrokeWidthSelected = borderStrokeWidthSelected, + borderStrokeWidth = borderStrokeWidth.dp, + borderStrokeWidthSelected = borderStrokeWidthSelected.dp, material = MaterialTheme.shapes.copy( - small = RoundedCornerShape(cornerRadius), - medium = RoundedCornerShape(cornerRadius) + small = RoundedCornerShape(cornerRadius.dp), + medium = RoundedCornerShape(cornerRadius.dp) ) ) } @@ -224,7 +191,67 @@ fun PaymentsThemeConfig.Shapes.toComposeShapes(): PaymentsComposeShapes { @Composable @ReadOnlyComposable @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) -fun PaymentsThemeConfig.Typography.toComposeTypography(): Typography { +fun PaymentsTypography.toComposeTypography(): Typography { + // h4 is our largest headline. It is used for the most important labels in our UI + // ex: "Select your payment method" in Payment Sheet. + val h4 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (xLargeFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightBold), + ) + + // h5 is our medium headline label. + // ex: "Pay $50.99" in Payment Sheet's buy button. + val h5 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (largeFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightMedium), + letterSpacing = (-0.32).sp + ) + + // h6 is our smallest headline label. + // ex: Section labels in Payment Sheet + val h6 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (smallFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightMedium), + letterSpacing = (-0.15).sp + ) + + // body1 is our larger body text. Used for the bulk of our elements and forms. + // ex: the text used in Payment Sheet's text form elements. + val body1 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (mediumFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightNormal), + ) + + // subtitle1 is our only subtitle size. Used for labeling fields. + // ex: the placeholder texts that appear when you type in Payment Sheet's forms. + val subtitle1 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (mediumFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightNormal), + letterSpacing = (-0.15).sp + ) + + // caption is used to label images in payment sheet. + // ex: the labels under our payment method selectors in Payment Sheet. + val caption = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (xSmallFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightMedium) + ) + + // body2 is our smaller body text. Used for less important fields that are not required to + // read. Ex: our mandate texts in Payment Sheet. + val body2 = TextStyle.Default.copy( + fontFamily = FontFamily(Font(fontFamily)), + fontSize = (xxSmallFontSize * fontSizeMultiplier), + fontWeight = FontWeight(fontWeightNormal), + letterSpacing = (-0.15).sp + ) + return MaterialTheme.typography.copy( body1 = body1, body2 = body2, @@ -241,10 +268,10 @@ fun PaymentsThemeConfig.Typography.toComposeTypography(): Typography { fun PaymentsTheme( content: @Composable () -> Unit ) { - val colors = PaymentsThemeConfig.toComposeColors() + val colors = PaymentsTheme.colors val localColors = staticCompositionLocalOf { colors } - val shapes = PaymentsThemeConfig.Shapes.toComposeShapes() + val shapes = PaymentsTheme.shapes val localShapes = staticCompositionLocalOf { shapes } CompositionLocalProvider( @@ -252,9 +279,9 @@ fun PaymentsTheme( localShapes provides shapes ) { MaterialTheme( - colors = PaymentsTheme.colors.material, - typography = PaymentsThemeConfig.Typography.toComposeTypography(), - shapes = PaymentsTheme.shapes.material, + colors = colors.material, + typography = PaymentsTheme.typography, + shapes = shapes.material, content = content ) } @@ -266,21 +293,25 @@ fun PaymentsTheme( @RestrictTo(RestrictTo.Scope.LIBRARY_GROUP) object PaymentsTheme { const val minContrastForWhite = 2.2 + var colorsDarkMutable = PaymentsThemeDefaults.colorsDark + var colorsLightMutable = PaymentsThemeDefaults.colorsLight val colors: PaymentsComposeColors @Composable @ReadOnlyComposable - get() = PaymentsThemeConfig.toComposeColors() + get() = (if (isSystemInDarkTheme()) colorsDarkMutable else colorsLightMutable).toComposeColors() + var shapesMutable = PaymentsThemeDefaults.shapes val shapes: PaymentsComposeShapes @Composable @ReadOnlyComposable - get() = PaymentsThemeConfig.Shapes.toComposeShapes() + get() = shapesMutable.toComposeShapes() + var typographyMutable = PaymentsThemeDefaults.typography val typography: Typography @Composable @ReadOnlyComposable - get() = PaymentsThemeConfig.Typography.toComposeTypography() + get() = typographyMutable.toComposeTypography() @Composable @ReadOnlyComposable @@ -305,14 +336,14 @@ fun Context.convertDpToPx(dp: Dp): Float { fun createTextSpanFromTextStyle( text: String?, context: Context, - textStyle: TextStyle, + fontSizeDp: Dp, color: Color, @FontRes fontFamily: Int ): SpannableString { val span = SpannableString(text ?: "") - val fontSize = context.convertDpToPx(textStyle.fontSize.value.dp) + val fontSize = context.convertDpToPx(fontSizeDp) span.setSpan(AbsoluteSizeSpan(fontSize.toInt()), 0, span.length, 0) span.setSpan(ForegroundColorSpan(color.toArgb()), 0, span.length, 0) diff --git a/paymentsheet/api/paymentsheet.api b/paymentsheet/api/paymentsheet.api index 2a2b999927f..73bd35c66f5 100644 --- a/paymentsheet/api/paymentsheet.api +++ b/paymentsheet/api/paymentsheet.api @@ -80,6 +80,39 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Address$Builder public final fun state (Ljava/lang/String;)Lcom/stripe/android/paymentsheet/PaymentSheet$Address$Builder; } +public final class com/stripe/android/paymentsheet/PaymentSheet$Appearance : android/os/Parcelable { + public static final field $stable I + public static final field CREATOR Landroid/os/Parcelable$Creator; + public fun ()V + public fun (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;)V + public synthetic fun (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;ILkotlin/jvm/internal/DefaultConstructorMarker;)V + public final fun component1 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun component2 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun component3 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes; + public final fun component4 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Typography; + public final fun copy (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance; + public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;ILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance; + public fun describeContents ()I + public fun equals (Ljava/lang/Object;)Z + public final fun getColors (Z)Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun getColorsDark ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun getColorsLight ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun getShapes ()Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes; + public final fun getTypography ()Lcom/stripe/android/paymentsheet/PaymentSheet$Typography; + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder { + public static final field $stable I + public fun ()V + public final fun colorsDark (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder; + public final fun colorsLight (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder; + public final fun shapes (Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder; + public final fun typography (Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;)Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance$Builder; +} + public final class com/stripe/android/paymentsheet/PaymentSheet$BillingDetails : android/os/Parcelable { public static final field $stable I public static final field CREATOR Landroid/os/Parcelable$Creator; @@ -114,6 +147,47 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$BillingDetails$B public final fun phone (Ljava/lang/String;)Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails$Builder; } +public final class com/stripe/android/paymentsheet/PaymentSheet$Colors : android/os/Parcelable { + public static final field $stable I + public static final field CREATOR Landroid/os/Parcelable$Creator; + public static final field Companion Lcom/stripe/android/paymentsheet/PaymentSheet$Colors$Companion; + public fun (IIIIIIIIIII)V + public final fun component1 ()I + public final fun component10 ()I + public final fun component11 ()I + public final fun component2 ()I + public final fun component3 ()I + public final fun component4 ()I + public final fun component5 ()I + public final fun component6 ()I + public final fun component7 ()I + public final fun component8 ()I + public final fun component9 ()I + public final fun copy (IIIIIIIIIII)Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Colors;IIIIIIIIIIIILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public fun describeContents ()I + public fun equals (Ljava/lang/Object;)Z + public final fun getAppBarIcon ()I + public final fun getComponent ()I + public final fun getComponentBorder ()I + public final fun getComponentDivider ()I + public final fun getError ()I + public final fun getOnComponent ()I + public final fun getOnSurface ()I + public final fun getPlaceholderText ()I + public final fun getPrimary ()I + public final fun getSubtitle ()I + public final fun getSurface ()I + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/stripe/android/paymentsheet/PaymentSheet$Colors$Companion { + public final fun getDefaultDark ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; + public final fun getDefaultLight ()Lcom/stripe/android/paymentsheet/PaymentSheet$Colors; +} + public final class com/stripe/android/paymentsheet/PaymentSheet$Configuration : android/os/Parcelable { public static final field $stable I public static final field CREATOR Landroid/os/Parcelable$Creator; @@ -123,18 +197,21 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Configuration : public fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;)V public fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;)V public fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;Z)V - public synthetic fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZILkotlin/jvm/internal/DefaultConstructorMarker;)V + public fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZLcom/stripe/android/paymentsheet/PaymentSheet$Appearance;)V + public synthetic fun (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZLcom/stripe/android/paymentsheet/PaymentSheet$Appearance;ILkotlin/jvm/internal/DefaultConstructorMarker;)V public final fun component1 ()Ljava/lang/String; public final fun component2 ()Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration; public final fun component3 ()Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration; public final fun component4 ()Landroid/content/res/ColorStateList; public final fun component5 ()Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails; public final fun component6 ()Z - public final fun copy (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;Z)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration; - public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration;Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration; + public final fun component7 ()Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance; + public final fun copy (Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZLcom/stripe/android/paymentsheet/PaymentSheet$Appearance;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration; + public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration;Ljava/lang/String;Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration;Landroid/content/res/ColorStateList;Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;ZLcom/stripe/android/paymentsheet/PaymentSheet$Appearance;ILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration; public fun describeContents ()I public fun equals (Ljava/lang/Object;)Z public final fun getAllowsDelayedPaymentMethods ()Z + public final fun getAppearance ()Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance; public final fun getCustomer ()Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration; public final fun getDefaultBillingDetails ()Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails; public final fun getGooglePay ()Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration; @@ -149,6 +226,7 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$Configuration$Bu public static final field $stable I public fun (Ljava/lang/String;)V public final fun allowsDelayedPaymentMethods (Z)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration$Builder; + public final fun appearance (Lcom/stripe/android/paymentsheet/PaymentSheet$Appearance;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration$Builder; public final fun build ()Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration; public final fun customer (Lcom/stripe/android/paymentsheet/PaymentSheet$CustomerConfiguration;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration$Builder; public final fun defaultBillingDetails (Lcom/stripe/android/paymentsheet/PaymentSheet$BillingDetails;)Lcom/stripe/android/paymentsheet/PaymentSheet$Configuration$Builder; @@ -242,6 +320,56 @@ public final class com/stripe/android/paymentsheet/PaymentSheet$GooglePayConfigu public static fun values ()[Lcom/stripe/android/paymentsheet/PaymentSheet$GooglePayConfiguration$Environment; } +public final class com/stripe/android/paymentsheet/PaymentSheet$Shapes : android/os/Parcelable { + public static final field $stable I + public static final field CREATOR Landroid/os/Parcelable$Creator; + public static final field Companion Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes$Companion; + public fun (FF)V + public final fun component1 ()F + public final fun component2 ()F + public final fun copy (FF)Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes; + public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes;FFILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes; + public fun describeContents ()I + public fun equals (Ljava/lang/Object;)Z + public final fun getBorderStrokeWidthDp ()F + public final fun getCornerRadiusDp ()F + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/stripe/android/paymentsheet/PaymentSheet$Shapes$Companion { + public final fun getDefault ()Lcom/stripe/android/paymentsheet/PaymentSheet$Shapes; +} + +public final class com/stripe/android/paymentsheet/PaymentSheet$Typography : android/os/Parcelable { + public static final field $stable I + public static final field CREATOR Landroid/os/Parcelable$Creator; + public static final field Companion Lcom/stripe/android/paymentsheet/PaymentSheet$Typography$Companion; + public fun (FIIII)V + public final fun component1 ()F + public final fun component2 ()I + public final fun component3 ()I + public final fun component4 ()I + public final fun component5 ()I + public final fun copy (FIIII)Lcom/stripe/android/paymentsheet/PaymentSheet$Typography; + public static synthetic fun copy$default (Lcom/stripe/android/paymentsheet/PaymentSheet$Typography;FIIIIILjava/lang/Object;)Lcom/stripe/android/paymentsheet/PaymentSheet$Typography; + public fun describeContents ()I + public fun equals (Ljava/lang/Object;)Z + public final fun getBoldWeight ()I + public final fun getFontResId ()I + public final fun getMediumWeight ()I + public final fun getNormalWeight ()I + public final fun getSizeScaleFactor ()F + public fun hashCode ()I + public fun toString ()Ljava/lang/String; + public fun writeToParcel (Landroid/os/Parcel;I)V +} + +public final class com/stripe/android/paymentsheet/PaymentSheet$Typography$Companion { + public final fun getDefault ()Lcom/stripe/android/paymentsheet/PaymentSheet$Typography; +} + public final class com/stripe/android/paymentsheet/PaymentSheetContract : androidx/activity/result/contract/ActivityResultContract { public static final field $stable I public static final field EXTRA_ARGS Ljava/lang/String; diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BaseAddPaymentMethodFragment.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BaseAddPaymentMethodFragment.kt index a42e44150bb..5fc8c6fcdce 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BaseAddPaymentMethodFragment.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BaseAddPaymentMethodFragment.kt @@ -9,6 +9,7 @@ import androidx.appcompat.view.ContextThemeWrapper import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue import androidx.compose.runtime.livedata.observeAsState +import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.ViewCompositionStrategy import androidx.compose.ui.res.stringResource import androidx.core.view.ViewCompat @@ -32,7 +33,6 @@ import com.stripe.android.paymentsheet.ui.AnimationConstants import com.stripe.android.paymentsheet.ui.GooglePayDividerUi import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel import com.stripe.android.ui.core.Amount -import com.stripe.android.ui.core.PaymentsThemeConfig import com.stripe.android.ui.core.elements.H4Text import com.stripe.android.ui.core.isSystemDarkTheme import com.stripe.android.ui.core.shouldUseDarkDynamicColor @@ -134,8 +134,9 @@ internal abstract class BaseAddPaymentMethodFragment : Fragment() { sheetViewModel.eventReporter.onShowNewPaymentOptionForm() val isSystemDarkMode = context?.isSystemDarkTheme() ?: false - val surfaceColor = PaymentsThemeConfig.colors(isSystemDarkMode).surface - viewBinding.googlePayButton.setBackgroundColor(surfaceColor.shouldUseDarkDynamicColor()) + sheetViewModel.config?.appearance?.getColors(isSystemDarkMode)?.surface?.let { + viewBinding.googlePayButton.setBackgroundColor(Color(it).shouldUseDarkDynamicColor()) + } } private fun attachComposeFragmentViewModel(fragment: Fragment) { diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BasePaymentMethodsListFragment.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BasePaymentMethodsListFragment.kt index 3a5c716baf9..187569e71a9 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BasePaymentMethodsListFragment.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/BasePaymentMethodsListFragment.kt @@ -6,6 +6,8 @@ import android.view.MenuInflater import android.view.MenuItem import android.view.View import androidx.annotation.VisibleForTesting +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import androidx.fragment.app.Fragment import androidx.recyclerview.widget.LinearLayoutManager import com.stripe.android.paymentsheet.databinding.FragmentPaymentsheetPaymentMethodsListBinding @@ -13,7 +15,7 @@ import com.stripe.android.paymentsheet.model.FragmentConfig import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.ui.BaseSheetActivity import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel -import com.stripe.android.ui.core.PaymentsThemeConfig +import com.stripe.android.ui.core.PaymentsThemeDefaults import com.stripe.android.ui.core.createTextSpanFromTextStyle import com.stripe.android.ui.core.isSystemDarkTheme @@ -70,16 +72,19 @@ internal abstract class BasePaymentMethodsListFragment( } private fun setEditMenuText() { + val context = context ?: return + val appearance = sheetViewModel.config?.appearance ?: return editMenuItem?.apply { - context?.let { - title = createTextSpanFromTextStyle( - text = getString(if (isEditing) R.string.done else R.string.edit), - context = it, - textStyle = PaymentsThemeConfig.Typography.h6, - color = PaymentsThemeConfig.colors(it.isSystemDarkTheme()).appBarIcon, - fontFamily = PaymentsThemeConfig.Typography.fontFamily - ) - } + title = createTextSpanFromTextStyle( + text = getString(if (isEditing) R.string.done else R.string.edit), + context = context, + fontSizeDp = ( + appearance.typography.sizeScaleFactor + * PaymentsThemeDefaults.typography.smallFontSize.value + ).dp, + color = Color(appearance.getColors(context.isSystemDarkTheme()).appBarIcon), + fontFamily = appearance.typography.fontResId + ) } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsActivity.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsActivity.kt index 1189bd2fda8..1e3cde0a1d9 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsActivity.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentOptionsActivity.kt @@ -9,6 +9,7 @@ import android.widget.TextView import androidx.activity.viewModels import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import androidx.core.os.bundleOf import androidx.core.view.doOnNextLayout @@ -23,7 +24,6 @@ import com.stripe.android.paymentsheet.databinding.ActivityPaymentOptionsBinding import com.stripe.android.paymentsheet.ui.AnimationConstants import com.stripe.android.paymentsheet.ui.BaseSheetActivity import com.stripe.android.paymentsheet.ui.PrimaryButton -import com.stripe.android.ui.core.PaymentsThemeConfig import com.stripe.android.ui.core.isSystemDarkTheme /** @@ -128,11 +128,14 @@ internal class PaymentOptionsActivity : BaseSheetActivity() viewBinding.continueButton.lockVisible = false viewBinding.continueButton.updateState(PrimaryButton.State.Ready) - viewBinding.continueButton.setDefaultBackGroundColor( - viewModel.config?.primaryButtonColor ?: ColorStateList.valueOf( - PaymentsThemeConfig.colors(baseContext.isSystemDarkTheme()).primary.toArgb() + viewModel.config?.let { + viewBinding.continueButton.setDefaultBackGroundColor( + it.primaryButtonColor ?: ColorStateList.valueOf( + Color(it.appearance.getColors(baseContext.isSystemDarkTheme()).primary).toArgb() + ) ) - ) + viewBinding.continueButton.setCornerRadius(it.appearance.shapes.cornerRadiusDp) + } addButton.setOnClickListener { viewModel.onUserSelection() diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt index 9d27e7ad17d..366179d1a5a 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheet.kt @@ -3,11 +3,15 @@ package com.stripe.android.paymentsheet import android.content.res.ColorStateList import android.os.Parcelable import androidx.activity.ComponentActivity +import androidx.annotation.ColorInt +import androidx.annotation.FontRes +import androidx.compose.ui.graphics.toArgb import androidx.fragment.app.Fragment import com.stripe.android.model.PaymentIntent import com.stripe.android.model.SetupIntent import com.stripe.android.paymentsheet.flowcontroller.FlowControllerFactory import com.stripe.android.paymentsheet.model.PaymentOption +import com.stripe.android.ui.core.PaymentsThemeDefaults import kotlinx.parcelize.Parcelize /** @@ -122,7 +126,12 @@ class PaymentSheet internal constructor( * * See [payment-notification](https://stripe.com/docs/payments/payment-methods#payment-notification). */ - val allowsDelayedPaymentMethods: Boolean = false + val allowsDelayedPaymentMethods: Boolean = false, + + /** + * Describes the appearance of Payment Sheet + */ + val appearance: Appearance = Appearance() ) : Parcelable { /** * [Configuration] builder for cleaner object creation from Java. @@ -135,6 +144,7 @@ class PaymentSheet internal constructor( private var primaryButtonColor: ColorStateList? = null private var defaultBillingDetails: BillingDetails? = null private var allowsDelayedPaymentMethods: Boolean = false + private var appearance: Appearance = Appearance() fun merchantDisplayName(merchantDisplayName: String) = apply { this.merchantDisplayName = merchantDisplayName } @@ -154,13 +164,171 @@ class PaymentSheet internal constructor( fun allowsDelayedPaymentMethods(allowsDelayedPaymentMethods: Boolean) = apply { this.allowsDelayedPaymentMethods = allowsDelayedPaymentMethods } + fun appearance(appearance: Appearance) = + apply { this.appearance = appearance } + fun build() = Configuration( merchantDisplayName, customer, googlePay, primaryButtonColor, defaultBillingDetails, - allowsDelayedPaymentMethods + allowsDelayedPaymentMethods, + appearance + ) + } + } + + @Parcelize + data class Appearance( + // Describes the colors used while the system is in light mode + val colorsLight: Colors = Colors.defaultLight, + + // Describes the colors used while the system is in dark mode + val colorsDark: Colors = Colors.defaultDark, + + // Describes the appearance of shapes + val shapes: Shapes = Shapes.default, + + // Describes the typography used for text. + val typography: Typography = Typography.default, + ) : Parcelable { + fun getColors(isDark: Boolean): Colors { + return if (isDark) colorsDark else colorsLight + } + + class Builder { + private var colorsLight = Colors.defaultLight + private var colorsDark = Colors.defaultDark + private var shapes = Shapes.default + private var typography = Typography.default + + fun colorsLight(colors: Colors) = apply { this.colorsLight = colors } + fun colorsDark(colors: Colors) = apply { this.colorsDark = colors } + fun shapes(shapes: Shapes) = apply { this.shapes = shapes } + fun typography(typography: Typography) = apply { this.typography = typography } + } + } + + @Parcelize + data class Colors( + // A primary color used throughout PaymentSheet + @ColorInt + val primary: Int, + + // The color used for the surfaces (backgrounds) of PaymentSheet + @ColorInt + val surface: Int, + + // The color used for the background of inputs, tabs, and other components + @ColorInt + val component: Int, + + // The color used for borders of inputs, tabs, and other components + @ColorInt + val componentBorder: Int, + + // The color of the divider lines used inside inputs, tabs, and other components + @ColorInt + val componentDivider: Int, + + // The default color used for text and on other elements that live on components + @ColorInt + val onComponent: Int, + + // The color used for items appearing over the background in Payment Sheet + @ColorInt + val onSurface: Int, + + // The color used for text of secondary importance. For example, this color is used for the label above input fields + @ColorInt + val subtitle: Int, + + // The color used for input placeholder text + @ColorInt + val placeholderText: Int, + + // The color used for icons in PaymentSheet, such as the close or back icons + @ColorInt + val appBarIcon: Int, + + // A color used to indicate errors or destructive actions in PaymentSheet + @ColorInt + val error: Int, + ) : Parcelable { + companion object { + val defaultLight = Colors( + primary = PaymentsThemeDefaults.colorsLight.primary.toArgb(), + surface = PaymentsThemeDefaults.colorsLight.surface.toArgb(), + component = PaymentsThemeDefaults.colorsLight.component.toArgb(), + componentBorder = PaymentsThemeDefaults.colorsLight.componentBorder.toArgb(), + componentDivider = PaymentsThemeDefaults.colorsLight.componentDivider.toArgb(), + onComponent = PaymentsThemeDefaults.colorsLight.onComponent.toArgb(), + subtitle = PaymentsThemeDefaults.colorsLight.subtitle.toArgb(), + placeholderText = PaymentsThemeDefaults.colorsLight.placeholderText.toArgb(), + onSurface = PaymentsThemeDefaults.colorsLight.onSurface.toArgb(), + appBarIcon = PaymentsThemeDefaults.colorsLight.appBarIcon.toArgb(), + error = PaymentsThemeDefaults.colorsLight.error.toArgb(), + ) + + val defaultDark = Colors( + primary = PaymentsThemeDefaults.colorsDark.primary.toArgb(), + surface = PaymentsThemeDefaults.colorsDark.surface.toArgb(), + component = PaymentsThemeDefaults.colorsDark.component.toArgb(), + componentBorder = PaymentsThemeDefaults.colorsDark.componentBorder.toArgb(), + componentDivider = PaymentsThemeDefaults.colorsDark.componentDivider.toArgb(), + onComponent = PaymentsThemeDefaults.colorsDark.onComponent.toArgb(), + subtitle = PaymentsThemeDefaults.colorsDark.subtitle.toArgb(), + placeholderText = PaymentsThemeDefaults.colorsDark.placeholderText.toArgb(), + onSurface = PaymentsThemeDefaults.colorsDark.onSurface.toArgb(), + appBarIcon = PaymentsThemeDefaults.colorsDark.appBarIcon.toArgb(), + error = PaymentsThemeDefaults.colorsDark.error.toArgb(), + ) + } + } + + @Parcelize + data class Shapes( + // The corner radius used for tabs, inputs, buttons, and other components in PaymentSheet + val cornerRadiusDp: Float, + + // The border used for inputs, tabs, and other components in PaymentSheet + val borderStrokeWidthDp: Float, + ) : Parcelable { + companion object { + val default = Shapes( + cornerRadiusDp = PaymentsThemeDefaults.shapes.cornerRadius, + borderStrokeWidthDp = PaymentsThemeDefaults.shapes.borderStrokeWidth, + ) + } + } + + @Parcelize + data class Typography( + // The scale factor for all fonts in PaymentSheet, the default value is 1.0. + // When this value increases fonts will increase in size and decrease when this value is lowered + val sizeScaleFactor: Float, + + // Base weight for text + val normalWeight: Int, + + // Medium weight for text + val mediumWeight: Int, + + // Bold weight for text + val boldWeight: Int, + + // The font used in text. This should be a resource ID value. + @FontRes + val fontResId: Int, + ) : Parcelable { + companion object { + val default = Typography( + sizeScaleFactor = PaymentsThemeDefaults.typography.fontSizeMultiplier, + normalWeight = PaymentsThemeDefaults.typography.fontWeightNormal, + mediumWeight = PaymentsThemeDefaults.typography.fontWeightMedium, + boldWeight = PaymentsThemeDefaults.typography.fontWeightBold, + fontResId = PaymentsThemeDefaults.typography.fontFamily ) } } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetActivity.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetActivity.kt index 4e0fb5d6aa9..977208eb970 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetActivity.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetActivity.kt @@ -10,7 +10,9 @@ import android.widget.TextView import androidx.activity.viewModels import androidx.annotation.IdRes import androidx.annotation.VisibleForTesting +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb +import androidx.compose.ui.unit.dp import androidx.core.os.bundleOf import androidx.core.view.doOnNextLayout import androidx.core.view.isVisible @@ -27,7 +29,7 @@ import com.stripe.android.paymentsheet.model.PaymentSheetViewState import com.stripe.android.paymentsheet.ui.AnimationConstants import com.stripe.android.paymentsheet.ui.BaseSheetActivity import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel -import com.stripe.android.ui.core.PaymentsThemeConfig +import com.stripe.android.ui.core.PaymentsThemeDefaults import com.stripe.android.ui.core.createTextSpanFromTextStyle import com.stripe.android.ui.core.isSystemDarkTheme import com.stripe.android.ui.core.shouldUseDarkDynamicColor @@ -95,13 +97,13 @@ internal class PaymentSheetActivity : BaseSheetActivity() { try { starterArgs.config?.validate() starterArgs.clientSecret.validate() + starterArgs.config?.appearance?.parseAppearance() } catch (e: InvalidParameterException) { setActivityResult(PaymentSheetResult.Failed(e)) finish() return } } - viewModel.registerFromActivity(this) viewModel.setupGooglePay( lifecycleScope, @@ -194,13 +196,18 @@ internal class PaymentSheetActivity : BaseSheetActivity() { private fun updateErrorMessage(userMessage: BaseSheetViewModel.UserErrorMessage? = null) { userMessage?.message.let { message -> - messageView.text = createTextSpanFromTextStyle( - text = message, - context = this, - textStyle = PaymentsThemeConfig.Typography.h6, - color = PaymentsThemeConfig.colors(baseContext.isSystemDarkTheme()).error, - fontFamily = PaymentsThemeConfig.Typography.fontFamily - ) + viewModel.config?.appearance?.let { + messageView.text = createTextSpanFromTextStyle( + text = message, + context = this, + fontSizeDp = ( + it.typography.sizeScaleFactor + * PaymentsThemeDefaults.typography.smallFontSize.value + ).dp, + color = Color(it.getColors(baseContext.isSystemDarkTheme()).error), + fontFamily = it.typography.fontResId + ) + } } messageView.isVisible = userMessage != null @@ -284,13 +291,15 @@ internal class PaymentSheetActivity : BaseSheetActivity() { ) is PaymentSheetListFragment if (shouldShowGooglePay) { - val surfaceColor = PaymentsThemeConfig.colors(isDark).surface - viewBinding.googlePayButton.apply { - bringToFront() - isVisible = true - setBackgroundColor(surfaceColor.shouldUseDarkDynamicColor()) + viewModel.config?.appearance?.let { + val surfaceColor = Color(it.getColors(isDark).surface) + viewBinding.googlePayButton.apply { + bringToFront() + isVisible = true + setBackgroundColor(surfaceColor.shouldUseDarkDynamicColor()) + } + viewBinding.buyButton.isVisible = false } - viewBinding.buyButton.isVisible = false } else { viewBinding.buyButton.bringToFront() viewBinding.buyButton.isVisible = true @@ -305,11 +314,14 @@ internal class PaymentSheetActivity : BaseSheetActivity() { viewModel.checkout(CheckoutIdentifier.SheetBottomGooglePay) } - viewBinding.buyButton.setDefaultBackGroundColor( - viewModel.config?.primaryButtonColor ?: ColorStateList.valueOf( - PaymentsThemeConfig.colors(isDark).primary.toArgb() + viewModel.config?.let { + viewBinding.buyButton.setDefaultBackGroundColor( + it.primaryButtonColor ?: ColorStateList.valueOf( + Color(it.appearance.getColors(isDark).primary).toArgb() + ) ) - ) + viewBinding.buyButton.setCornerRadius(it.appearance.shapes.cornerRadiusDp) + } viewBinding.buyButton.setOnClickListener { updateErrorMessage() diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetAddPaymentMethodFragment.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetAddPaymentMethodFragment.kt index 566e4c47ddd..34d749a0ae6 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetAddPaymentMethodFragment.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetAddPaymentMethodFragment.kt @@ -3,6 +3,8 @@ package com.stripe.android.paymentsheet import android.os.Bundle import android.view.View import androidx.appcompat.app.AppCompatActivity +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.unit.dp import androidx.core.view.isVisible import androidx.fragment.app.activityViewModels import androidx.lifecycle.ViewModelProvider @@ -12,7 +14,7 @@ import com.stripe.android.paymentsheet.model.PaymentSelection import com.stripe.android.paymentsheet.model.PaymentSheetViewState import com.stripe.android.paymentsheet.ui.BaseSheetActivity import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel -import com.stripe.android.ui.core.PaymentsThemeConfig +import com.stripe.android.ui.core.PaymentsThemeDefaults import com.stripe.android.ui.core.createTextSpanFromTextStyle import com.stripe.android.ui.core.isSystemDarkTheme @@ -81,16 +83,19 @@ internal class PaymentSheetAddPaymentMethodFragment() : BaseAddPaymentMethodFrag } private fun updateErrorMessage(userMessage: BaseSheetViewModel.UserErrorMessage?) { + val context = context ?: return + val appearance = sheetViewModel.config?.appearance ?: return userMessage?.message.let { message -> - context?.let { - viewBinding.message.text = createTextSpanFromTextStyle( - text = message, - context = it, - textStyle = PaymentsThemeConfig.Typography.h6, - color = PaymentsThemeConfig.colors(it.isSystemDarkTheme()).error, - fontFamily = PaymentsThemeConfig.Typography.fontFamily - ) - } + viewBinding.message.text = createTextSpanFromTextStyle( + text = message, + context = context, + fontSizeDp = ( + appearance.typography.sizeScaleFactor + * PaymentsThemeDefaults.typography.smallFontSize.value + ).dp, + color = Color(appearance.getColors(context.isSystemDarkTheme()).error), + fontFamily = appearance.typography.fontResId + ) } viewBinding.message.isVisible = userMessage != null } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetConfigurationKtx.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetConfigurationKtx.kt index 2c9a60b9db9..f3ec4000001 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetConfigurationKtx.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/PaymentSheetConfigurationKtx.kt @@ -1,5 +1,8 @@ package com.stripe.android.paymentsheet +import androidx.compose.ui.graphics.Color +import com.stripe.android.ui.core.PaymentsTheme +import com.stripe.android.ui.core.PaymentsThemeDefaults import java.security.InvalidParameterException internal fun PaymentSheet.Configuration.validate() { @@ -25,3 +28,46 @@ internal fun PaymentSheet.Configuration.validate() { } } } + +internal fun PaymentSheet.Appearance.parseAppearance() { + PaymentsTheme.colorsLightMutable = PaymentsThemeDefaults.colorsLight.copy( + primary = Color(colorsLight.primary), + surface = Color(colorsLight.surface), + component = Color(colorsLight.component), + componentBorder = Color(colorsLight.componentBorder), + componentDivider = Color(colorsLight.componentDivider), + onComponent = Color(colorsLight.onComponent), + subtitle = Color(colorsLight.subtitle), + placeholderText = Color(colorsLight.placeholderText), + onSurface = Color(colorsLight.onSurface), + appBarIcon = Color(colorsLight.appBarIcon), + error = Color(colorsLight.error) + ) + + PaymentsTheme.colorsDarkMutable = PaymentsThemeDefaults.colorsDark.copy( + primary = Color(colorsDark.primary), + surface = Color(colorsDark.surface), + component = Color(colorsDark.component), + componentBorder = Color(colorsDark.componentBorder), + componentDivider = Color(colorsDark.componentDivider), + onComponent = Color(colorsDark.onComponent), + subtitle = Color(colorsDark.subtitle), + placeholderText = Color(colorsDark.placeholderText), + onSurface = Color(colorsDark.onSurface), + appBarIcon = Color(colorsDark.appBarIcon), + error = Color(colorsDark.error) + ) + + PaymentsTheme.shapesMutable = PaymentsThemeDefaults.shapes.copy( + cornerRadius = shapes.cornerRadiusDp, + borderStrokeWidth = shapes.borderStrokeWidthDp + ) + + PaymentsTheme.typographyMutable = PaymentsThemeDefaults.typography.copy( + fontFamily = typography.fontResId, + fontWeightNormal = typography.normalWeight, + fontWeightMedium = typography.mediumWeight, + fontWeightBold = typography.boldWeight, + fontSizeMultiplier = typography.sizeScaleFactor + ) +} diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/BaseSheetActivity.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/BaseSheetActivity.kt index 1f3885f3e2b..b50df109512 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/BaseSheetActivity.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/BaseSheetActivity.kt @@ -18,6 +18,7 @@ import androidx.annotation.StringRes import androidx.annotation.VisibleForTesting import androidx.appcompat.app.AppCompatActivity import androidx.appcompat.content.res.AppCompatResources +import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.toArgb import com.google.android.material.appbar.AppBarLayout import com.google.android.material.appbar.MaterialToolbar @@ -25,7 +26,6 @@ import com.google.android.material.bottomsheet.BottomSheetBehavior import com.stripe.android.paymentsheet.BottomSheetController import com.stripe.android.paymentsheet.R import com.stripe.android.paymentsheet.viewmodels.BaseSheetViewModel -import com.stripe.android.ui.core.PaymentsThemeConfig import com.stripe.android.ui.core.isSystemDarkTheme import com.stripe.android.view.KeyboardController import kotlin.math.roundToInt @@ -117,12 +117,14 @@ internal abstract class BaseSheetActivity : AppCompatActivity() { } val isDark = baseContext.isSystemDarkTheme() - bottomSheet.setBackgroundColor( - PaymentsThemeConfig.colors(isDark).surface.toArgb() - ) - toolbar.setBackgroundColor( - PaymentsThemeConfig.colors(isDark).surface.toArgb() - ) + viewModel.config?.let { + bottomSheet.setBackgroundColor( + Color(it.appearance.getColors(isDark).surface).toArgb() + ) + toolbar.setBackgroundColor( + Color(it.appearance.getColors(isDark).surface).toArgb() + ) + } setSheetWidthForTablets() } @@ -161,14 +163,17 @@ internal abstract class BaseSheetActivity : AppCompatActivity() { ) } - val navigationIconDrawable = AppCompatResources.getDrawable(this, toolbarResources.icon) - navigationIconDrawable?.setTintList( - ColorStateList.valueOf( - PaymentsThemeConfig.colors(baseContext.isSystemDarkTheme()).appBarIcon.toArgb() + viewModel.config?.appearance?.let { + val navigationIconDrawable = AppCompatResources.getDrawable(this, toolbarResources.icon) + navigationIconDrawable?.setTintList( + ColorStateList.valueOf( + Color(it.getColors(baseContext.isSystemDarkTheme()).appBarIcon).toArgb() + ) ) - ) - toolbar.navigationIcon = navigationIconDrawable + toolbar.navigationIcon = navigationIconDrawable + } + toolbar.navigationContentDescription = resources.getString(toolbarResources.description) } diff --git a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/PrimaryButton.kt b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/PrimaryButton.kt index 8ba6b3b4361..8801212a248 100644 --- a/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/PrimaryButton.kt +++ b/paymentsheet/src/main/java/com/stripe/android/paymentsheet/ui/PrimaryButton.kt @@ -21,7 +21,7 @@ import androidx.core.view.setPadding import com.stripe.android.paymentsheet.R import com.stripe.android.paymentsheet.databinding.PrimaryButtonBinding import com.stripe.android.ui.core.PaymentsTheme -import com.stripe.android.ui.core.PaymentsThemeConfig +import com.stripe.android.ui.core.PaymentsThemeDefaults import com.stripe.android.ui.core.convertDpToPx /** @@ -53,6 +53,8 @@ internal class PrimaryButton @JvmOverloads constructor( private val confirmedIcon = viewBinding.confirmedIcon + private var cornerRadius = context.convertDpToPx(PaymentsThemeDefaults.shapes.cornerRadius.dp) + init { // This is only needed if the button is inside a fragment viewBinding.label.setViewCompositionStrategy( @@ -71,9 +73,11 @@ internal class PrimaryButton @JvmOverloads constructor( defaultTintList = tintList } - override fun setBackgroundTintList(tintList: ColorStateList?) { - val cornerRadius = context.convertDpToPx(PaymentsThemeConfig.Shapes.cornerRadius) + fun setCornerRadius(radius: Float) { + cornerRadius = context.convertDpToPx(radius.dp) + } + override fun setBackgroundTintList(tintList: ColorStateList?) { val shape = GradientDrawable() shape.shape = GradientDrawable.RECTANGLE shape.cornerRadius = cornerRadius