Skip to content

Commit 9a47445

Browse files
committed
feat: Pin pad WIP
1 parent 5df1906 commit 9a47445

File tree

5 files changed

+296
-31
lines changed

5 files changed

+296
-31
lines changed

app/src/main/java/to/bitkit/ui/MainActivity.kt

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -175,7 +175,9 @@ class MainActivity : FragmentActivity() {
175175

176176
if (!isAuthenticated) {
177177
AuthCheckView(
178-
onSuccess = { appViewModel.setIsAuthenticated(true) }
178+
onSuccess = { appViewModel.setIsAuthenticated(true) },
179+
showLogoOnPin = true,
180+
appViewModel = appViewModel,
179181
)
180182
} else {
181183
ContentView(
Lines changed: 106 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,37 +1,56 @@
11
package to.bitkit.ui.components
22

3+
import androidx.compose.foundation.Image
4+
import androidx.compose.foundation.background
5+
import androidx.compose.foundation.border
36
import androidx.compose.foundation.layout.Box
47
import androidx.compose.foundation.layout.Column
8+
import androidx.compose.foundation.layout.Row
59
import androidx.compose.foundation.layout.fillMaxSize
10+
import androidx.compose.foundation.layout.height
11+
import androidx.compose.foundation.layout.navigationBarsPadding
612
import androidx.compose.foundation.layout.padding
13+
import androidx.compose.foundation.layout.size
14+
import androidx.compose.foundation.shape.CircleShape
715
import androidx.compose.runtime.Composable
16+
import androidx.compose.runtime.LaunchedEffect
817
import androidx.compose.runtime.getValue
918
import androidx.compose.runtime.mutableStateOf
19+
import androidx.compose.runtime.remember
1020
import androidx.compose.runtime.saveable.rememberSaveable
1121
import androidx.compose.runtime.setValue
1222
import androidx.compose.ui.Alignment
1323
import androidx.compose.ui.Modifier
24+
import androidx.compose.ui.draw.clip
25+
import androidx.compose.ui.layout.ContentScale
26+
import androidx.compose.ui.res.painterResource
1427
import androidx.compose.ui.res.stringResource
1528
import androidx.compose.ui.tooling.preview.Preview
1629
import androidx.compose.ui.unit.dp
1730
import androidx.lifecycle.compose.collectAsStateWithLifecycle
31+
import kotlinx.coroutines.delay
1832
import to.bitkit.R
19-
import to.bitkit.ui.appViewModel
2033
import to.bitkit.ui.theme.AppThemeSurface
34+
import to.bitkit.ui.theme.Colors
2135
import to.bitkit.ui.utils.rememberBiometricAuthSupported
36+
import to.bitkit.viewmodels.AppViewModel
37+
38+
private const val PIN_LENGTH = 4
2239

2340
@Composable
2441
fun AuthCheckView(
2542
onSuccess: (() -> Unit)? = null,
2643
isBiometrySupported: Boolean = rememberBiometricAuthSupported(),
44+
showLogoOnPin: Boolean,
45+
appViewModel: AppViewModel,
2746
) {
28-
val app = appViewModel ?: return
29-
val isBiometricsEnabled by app.isBiometricEnabled.collectAsStateWithLifecycle()
47+
val isBiometricsEnabled by appViewModel.isBiometricEnabled.collectAsStateWithLifecycle()
3048

3149
AuthCheckViewContent(
3250
onSuccess = onSuccess,
3351
isBiometricsEnabled = isBiometricsEnabled,
3452
isBiometrySupported = isBiometrySupported,
53+
showLogoOnPin = showLogoOnPin,
3554
)
3655
}
3756

@@ -40,35 +59,106 @@ private fun AuthCheckViewContent(
4059
onSuccess: (() -> Unit)? = null,
4160
isBiometricsEnabled: Boolean,
4261
isBiometrySupported: Boolean,
62+
showLogoOnPin: Boolean,
4363
) {
4464
var showBio by rememberSaveable { mutableStateOf(isBiometricsEnabled) }
4565

4666
Box(
4767
contentAlignment = Alignment.Center,
48-
modifier = Modifier.fillMaxSize()
68+
modifier = Modifier
69+
.fillMaxSize()
70+
.navigationBarsPadding()
4971
) {
50-
Column(
51-
horizontalAlignment = Alignment.CenterHorizontally,
52-
) {
53-
72+
Column {
5473
if (showBio && isBiometrySupported) {
5574
BiometricsView(
5675
onSuccess = { onSuccess?.invoke() },
5776
onFailure = { showBio = false },
5877
)
5978
} else {
60-
Subtitle(text = "TODO: Pin code auth")
61-
PrimaryButton(
62-
text = stringResource(R.string.common__skip),
63-
onClick = { onSuccess?.invoke() },
64-
fullWidth = false,
65-
modifier = Modifier.padding(top = 24.dp),
79+
PinPad(
80+
showLogo = showLogoOnPin,
81+
onSuccess = onSuccess,
6682
)
6783
}
6884
}
6985
}
7086
}
7187

88+
@Composable
89+
private fun PinPad(
90+
showLogo: Boolean = false,
91+
onSuccess: (() -> Unit)?,
92+
) {
93+
var pin by remember { mutableStateOf("") }
94+
95+
LaunchedEffect(pin) {
96+
if (pin.length == PIN_LENGTH) {
97+
delay(500) // Simulate pin check delay
98+
// TODO: Implement actual PIN verification logic here
99+
onSuccess?.invoke()
100+
pin = ""
101+
}
102+
}
103+
104+
Column(
105+
horizontalAlignment = Alignment.CenterHorizontally,
106+
) {
107+
Box(
108+
contentAlignment = Alignment.BottomCenter,
109+
modifier = Modifier.weight(1f)
110+
) {
111+
if (showLogo) {
112+
Image(
113+
painter = painterResource(R.drawable.bitkit_logo),
114+
contentDescription = null,
115+
contentScale = ContentScale.Fit,
116+
modifier = Modifier.size(256.dp)
117+
)
118+
}
119+
}
120+
Subtitle(text = stringResource(R.string.security__pin_enter))
121+
PinDots(
122+
pin = pin,
123+
modifier = Modifier.padding(vertical = 32.dp),
124+
)
125+
PinNumberPad(
126+
modifier = Modifier.height(310.dp),
127+
onPress = { key ->
128+
if (key == KEY_DELETE) {
129+
if (pin.isNotEmpty()) {
130+
pin = pin.dropLast(1)
131+
}
132+
} else if (pin.length < PIN_LENGTH) {
133+
pin += key
134+
}
135+
},
136+
)
137+
}
138+
}
139+
140+
141+
@Composable
142+
private fun PinDots(
143+
pin: String,
144+
modifier: Modifier = Modifier,
145+
) {
146+
Row(
147+
modifier = modifier,
148+
) {
149+
repeat(PIN_LENGTH) { index ->
150+
Box(
151+
modifier = Modifier
152+
.padding(horizontal = 12.dp)
153+
.size(20.dp)
154+
.clip(CircleShape)
155+
.border(1.dp, Colors.Brand, CircleShape)
156+
.background(if (index < pin.length) Colors.Brand else Colors.Brand08)
157+
)
158+
}
159+
}
160+
}
161+
72162
@Preview(showBackground = true)
73163
@Composable
74164
private fun PreviewBio() {
@@ -77,6 +167,7 @@ private fun PreviewBio() {
77167
onSuccess = {},
78168
isBiometricsEnabled = true,
79169
isBiometrySupported = true,
170+
showLogoOnPin = true,
80171
)
81172
}
82173
}
@@ -89,6 +180,7 @@ private fun PreviewPin() {
89180
onSuccess = {},
90181
isBiometricsEnabled = false,
91182
isBiometrySupported = true,
183+
showLogoOnPin = true,
92184
)
93185
}
94186
}
Lines changed: 22 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,12 @@
11
package to.bitkit.ui.components
22

3+
import androidx.compose.foundation.layout.Column
34
import androidx.compose.foundation.layout.Spacer
45
import androidx.compose.foundation.layout.height
56
import androidx.compose.foundation.layout.size
67
import androidx.compose.material3.Icon
78
import androidx.compose.runtime.Composable
9+
import androidx.compose.ui.Alignment
810
import androidx.compose.ui.Modifier
911
import androidx.compose.ui.res.painterResource
1012
import androidx.compose.ui.res.stringResource
@@ -17,20 +19,24 @@ fun BiometricsView(
1719
onSuccess: (() -> Unit)? = null,
1820
onFailure: (() -> Unit)? = null,
1921
) {
20-
BiometricPrompt(
21-
onSuccess = { onSuccess?.invoke() },
22-
onError = { onFailure?.invoke() },
23-
)
24-
Icon(
25-
painter = painterResource(R.drawable.ic_fingerprint),
26-
contentDescription = null,
27-
modifier = Modifier.Companion.size(64.dp),
28-
)
29-
Spacer(modifier = Modifier.Companion.height(16.dp))
30-
Subtitle(
31-
text = run {
32-
val biometricsName = stringResource(R.string.security__bio)
33-
stringResource(R.string.security__bio_auth_with).replace("{biometricsName}", biometricsName)
34-
}
35-
)
22+
Column(
23+
horizontalAlignment = Alignment.CenterHorizontally,
24+
) {
25+
BiometricPrompt(
26+
onSuccess = { onSuccess?.invoke() },
27+
onError = { onFailure?.invoke() },
28+
)
29+
Icon(
30+
painter = painterResource(R.drawable.ic_fingerprint),
31+
contentDescription = null,
32+
modifier = Modifier.size(64.dp),
33+
)
34+
Spacer(modifier = Modifier.height(16.dp))
35+
Subtitle(
36+
text = run {
37+
val biometricsName = stringResource(R.string.security__bio)
38+
stringResource(R.string.security__bio_auth_with).replace("{biometricsName}", biometricsName)
39+
}
40+
)
41+
}
3642
}

0 commit comments

Comments
 (0)