Skip to content

Commit f28e59c

Browse files
committed
Venmo buttons compose UI
1 parent c5fdd9a commit f28e59c

File tree

1 file changed

+163
-0
lines changed
  • UIComponents/src/main/java/com/braintreepayments/api/uicomponents/compose

1 file changed

+163
-0
lines changed
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
package com.braintreepayments.api.uicomponents.compose
2+
3+
import android.graphics.drawable.Drawable
4+
import android.widget.Toast
5+
import androidx.compose.foundation.BorderStroke
6+
import androidx.compose.foundation.Image
7+
import androidx.compose.foundation.interaction.MutableInteractionSource
8+
import androidx.compose.foundation.interaction.collectIsFocusedAsState
9+
import androidx.compose.foundation.interaction.collectIsHoveredAsState
10+
import androidx.compose.foundation.interaction.collectIsPressedAsState
11+
import androidx.compose.foundation.layout.Arrangement
12+
import androidx.compose.foundation.layout.Column
13+
import androidx.compose.foundation.layout.Row
14+
import androidx.compose.foundation.layout.defaultMinSize
15+
import androidx.compose.foundation.layout.height
16+
import androidx.compose.foundation.layout.padding
17+
import androidx.compose.foundation.layout.size
18+
import androidx.compose.foundation.layout.width
19+
import androidx.compose.foundation.shape.RoundedCornerShape
20+
import androidx.compose.material3.CircularProgressIndicator
21+
import androidx.compose.material3.Surface
22+
import androidx.compose.runtime.Composable
23+
import androidx.compose.runtime.remember
24+
import androidx.compose.ui.Alignment
25+
import androidx.compose.ui.Modifier
26+
import androidx.compose.ui.draw.drawBehind
27+
import androidx.compose.ui.geometry.CornerRadius
28+
import androidx.compose.ui.graphics.Color
29+
import androidx.compose.ui.graphics.drawscope.Stroke
30+
import androidx.compose.ui.platform.LocalContext
31+
import androidx.compose.ui.res.colorResource
32+
import androidx.compose.ui.res.dimensionResource
33+
import androidx.compose.ui.semantics.Role
34+
import androidx.compose.ui.semantics.role
35+
import androidx.compose.ui.semantics.semantics
36+
import androidx.compose.ui.tooling.preview.Preview
37+
import androidx.compose.ui.unit.dp
38+
import androidx.core.content.ContextCompat
39+
import com.braintreepayments.api.uicomponents.R
40+
import com.braintreepayments.api.uicomponents.VenmoButtonColor
41+
import com.google.accompanist.drawablepainter.rememberDrawablePainter
42+
43+
@Composable
44+
fun VenmoButton(style: VenmoButtonColor, enabled: Boolean = true, onClick: () -> Unit) {
45+
val context = LocalContext.current
46+
val ppLogoOffset = dimensionResource(R.dimen.pp_logo_offset)
47+
val desiredWidth = dimensionResource(R.dimen.pay_button_width)
48+
val desiredHeight = dimensionResource(R.dimen.pay_button_height)
49+
val minDesiredWidth = dimensionResource(R.dimen.pay_button_min_width)
50+
val borderStroke = dimensionResource(R.dimen.pay_button_border)
51+
val focusBorderWidth = dimensionResource(R.dimen.pay_button_focus_border)
52+
val focusBorderPadding = dimensionResource(R.dimen.pay_button_focus_padding)
53+
val buttonCornerRadius = dimensionResource(R.dimen.pay_button_corner_radius)
54+
55+
val interactionSource = remember { MutableInteractionSource() }
56+
val isPressed = interactionSource.collectIsPressedAsState()
57+
val isHovered = interactionSource.collectIsHoveredAsState()
58+
val isFocused = interactionSource.collectIsFocusedAsState()
59+
60+
val containerColor = colorResource(fillColor(style, isPressed.value, isHovered.value, isFocused.value))
61+
val borderColor = colorResource(borderColor(style, isPressed.value, isHovered.value, isFocused.value))
62+
val focusColor = colorResource(focusColor(style, isPressed.value, isHovered.value, isFocused.value))
63+
64+
Surface(
65+
onClick = onClick,
66+
modifier = Modifier
67+
.semantics { role = Role.Button }
68+
.drawBehind {
69+
drawRoundRect(
70+
focusColor,
71+
cornerRadius = CornerRadius(buttonCornerRadius.toPx()),
72+
style = Stroke(width = focusBorderWidth.toPx())
73+
)
74+
}
75+
.padding(focusBorderPadding),
76+
enabled = enabled,
77+
shape = RoundedCornerShape(buttonCornerRadius),
78+
color = containerColor,
79+
border = BorderStroke(borderStroke, borderColor),
80+
interactionSource = interactionSource
81+
) {
82+
Row(
83+
Modifier
84+
.defaultMinSize(minWidth = minDesiredWidth)
85+
.width(desiredWidth)
86+
.height(desiredHeight),
87+
horizontalArrangement = Arrangement.Center,
88+
verticalAlignment = Alignment.CenterVertically,
89+
content = {
90+
val loading = !enabled
91+
if (!loading) {
92+
val logo: Drawable? = ContextCompat.getDrawable(context, style.logoId)
93+
Image(
94+
painter = rememberDrawablePainter(drawable = logo),
95+
modifier = Modifier.padding(top = ppLogoOffset),
96+
contentDescription = "PayPal",
97+
)
98+
} else {
99+
val color = when (style) {
100+
VenmoButtonColor.Blue -> Color.Black
101+
VenmoButtonColor.Black -> Color.White
102+
VenmoButtonColor.White -> Color.Black
103+
}
104+
CircularProgressIndicator(
105+
modifier = Modifier.size(desiredHeight / 2),
106+
color = color,
107+
strokeWidth = 2.dp
108+
)
109+
}
110+
}
111+
)
112+
}
113+
}
114+
115+
private fun fillColor(style: VenmoButtonColor, isPressed: Boolean, isHovered: Boolean, isFocused: Boolean) = when {
116+
isPressed -> style.pressed.fill
117+
isHovered && isFocused -> style.focusHover.fill
118+
isHovered -> style.hover.fill
119+
isFocused -> style.focus.fill
120+
else -> style.default.fill
121+
}
122+
123+
private fun borderColor(style: VenmoButtonColor, isPressed: Boolean, isHovered: Boolean, isFocused: Boolean) = when {
124+
isPressed -> style.pressed.border
125+
isHovered && isFocused -> style.focusHover.border
126+
isHovered -> style.hover.border
127+
isFocused -> style.focus.border
128+
else -> style.default.border
129+
}
130+
131+
private fun focusColor(style: VenmoButtonColor, isPressed: Boolean, isHovered: Boolean, isFocused: Boolean) = when {
132+
isPressed -> style.pressed.focusIndicator
133+
isHovered && isFocused -> style.focusHover.focusIndicator
134+
isHovered -> style.hover.focusIndicator
135+
isFocused -> style.focus.focusIndicator
136+
else -> style.default.focusIndicator
137+
}
138+
139+
@Preview
140+
@Composable
141+
fun PreviewVenmoButtons() {
142+
val context = LocalContext.current
143+
Column(modifier = Modifier.padding(16.dp).width(480.dp), horizontalAlignment = Alignment.CenterHorizontally) {
144+
VenmoButton(style = VenmoButtonColor.Blue) {
145+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
146+
}
147+
VenmoButton(style = VenmoButtonColor.Black) {
148+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
149+
}
150+
VenmoButton(style = VenmoButtonColor.White) {
151+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
152+
}
153+
VenmoButton(style = VenmoButtonColor.Blue, enabled = false) {
154+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
155+
}
156+
VenmoButton(style = VenmoButtonColor.Black, enabled = false) {
157+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
158+
}
159+
VenmoButton(style = VenmoButtonColor.White, enabled = false) {
160+
Toast.makeText(context, "Clicked", Toast.LENGTH_SHORT).show()
161+
}
162+
}
163+
}

0 commit comments

Comments
 (0)