Skip to content

Commit 05796d9

Browse files
author
Jeff Yanta
committed
Merge branch 'develop'
2 parents 3530dc9 + c82bed3 commit 05796d9

11 files changed

Lines changed: 102 additions & 66 deletions

File tree

api/src/main/java/com/getcode/model/Limits.kt

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -49,9 +49,6 @@ data class Limits(
4949
val sends = sendLimits
5050
.mapNotNull { (k, v) ->
5151
val code = CurrencyCode.tryValueOf(k) ?: return@mapNotNull null
52-
if (code == CurrencyCode.USD) {
53-
println("limit=${v.maxPerDay.toDouble()}")
54-
}
5552
val limit = SendLimit(
5653
nextTransaction = v.nextTransaction.toDouble(),
5754
maxPerDay = v.maxPerDay.toDouble(),

app/src/main/java/com/getcode/CodeApp.kt

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ import com.getcode.ui.components.TopBarContainer
4646
import com.getcode.ui.utils.getActivity
4747
import com.getcode.ui.utils.getActivityScopedViewModel
4848
import com.getcode.ui.utils.measured
49+
import com.getcode.util.BiometricsError
4950
import com.getcode.view.main.home.components.BiometricsBlockingView
5051
import com.getcode.view.main.home.components.rememberBiometricsState
5152
import dev.bmcreations.tipkit.TipScaffold
@@ -58,7 +59,11 @@ fun CodeApp(tipsEngine: TipsEngine) {
5859
val activity = LocalContext.current.getActivity()
5960
val biometricsState = rememberBiometricsState(
6061
requireBiometrics = state.requireBiometrics,
61-
onBiometricsNotEnrolled = { tlvm.onMissingBiometrics() }
62+
onError = { error ->
63+
if (error == BiometricsError.NoBiometrics) {
64+
tlvm.onMissingBiometrics()
65+
}
66+
}
6267
)
6368

6469
OnLifecycleEvent { _, event ->

app/src/main/java/com/getcode/ui/tips/definitions/DownloadCodeTip.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,6 @@ class DownloadCodeTip @Inject constructor(
5252
{ homeOpenCount >= 1 },
5353
{
5454
val accountCreatedAtMillis = SessionManager.getOrganizer()?.createdAtMillis
55-
println("millis=$accountCreatedAtMillis")
5655
val millis = accountCreatedAtMillis ?: return@listOf false
5756
val createdAt = Instant.fromEpochMilliseconds(millis)
5857
createdAt.plus(1.days) < now

app/src/main/java/com/getcode/util/Biometrics.kt

Lines changed: 44 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,12 @@ package com.getcode.util
33
import android.content.Context
44
import androidx.biometric.BiometricManager
55
import androidx.biometric.BiometricPrompt
6+
import androidx.compose.runtime.getValue
7+
import androidx.compose.runtime.mutableStateOf
8+
import androidx.compose.runtime.setValue
69
import androidx.fragment.app.FragmentActivity
710
import com.getcode.R
11+
import kotlinx.coroutines.delay
812
import kotlinx.coroutines.suspendCancellableCoroutine
913
import timber.log.Timber
1014
import java.util.concurrent.Executors
@@ -44,38 +48,49 @@ enum class BiometricsError {
4448
}
4549

4650
object Biometrics {
47-
suspend fun prompt(context: Context): Result<Unit> = suspendCancellableCoroutine { cont ->
48-
val activity = context as FragmentActivity
49-
val executor = Executors.newSingleThreadExecutor()
50-
val biometricPrompt = BiometricPrompt(
51-
activity,
52-
executor,
53-
object : BiometricPrompt.AuthenticationCallback() {
54-
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
55-
Timber.d("Biometric Authentication successful")
56-
cont.resume(Result.success(Unit))
57-
}
51+
var promptActive by mutableStateOf(false)
52+
private set
5853

59-
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
60-
val error = BiometricsException(errorCode, errString)
61-
Timber.e(t = error.cause, message = "onAuthenticationErrorForBiometrics")
62-
cont.resume(Result.failure(error))
63-
}
54+
suspend fun prompt(
55+
context: Context,
56+
delay: Long = 0L,
57+
authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK
58+
or BiometricManager.Authenticators.DEVICE_CREDENTIAL,
59+
): Result<Unit> {
60+
promptActive = true
61+
delay(delay)
62+
return suspendCancellableCoroutine { cont ->
63+
val activity = context as FragmentActivity
64+
val executor = Executors.newSingleThreadExecutor()
65+
val biometricPrompt = BiometricPrompt(
66+
activity,
67+
executor,
68+
object : BiometricPrompt.AuthenticationCallback() {
69+
override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) {
70+
Timber.d("Biometric Authentication successful")
71+
promptActive = false
72+
cont.resume(Result.success(Unit))
73+
}
6474

65-
override fun onAuthenticationFailed() {
66-
Timber.e("onAuthenticationFailedForBiometrics")
67-
}
68-
})
75+
override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
76+
val error = BiometricsException(errorCode, errString)
77+
Timber.e(t = error.cause, message = "onAuthenticationErrorForBiometrics")
78+
promptActive = false
79+
cont.resume(Result.failure(error))
80+
}
6981

70-
val promptInfo = BiometricPrompt.PromptInfo.Builder()
71-
.setTitle(context.getString(R.string.title_biometricAuthentication))
72-
.setDescription(context.getString(R.string.description_biometricAuthentication))
73-
.setAllowedAuthenticators(
74-
BiometricManager.Authenticators.BIOMETRIC_WEAK
75-
or BiometricManager.Authenticators.DEVICE_CREDENTIAL
76-
)
77-
.build()
82+
override fun onAuthenticationFailed() {
83+
Timber.e("onAuthenticationFailedForBiometrics")
84+
}
85+
})
7886

79-
biometricPrompt.authenticate(promptInfo)
87+
val promptInfo = BiometricPrompt.PromptInfo.Builder()
88+
.setTitle(context.getString(R.string.title_biometricAuthentication))
89+
.setDescription(context.getString(R.string.description_biometricAuthentication))
90+
.setAllowedAuthenticators(authenticators)
91+
.build()
92+
93+
biometricPrompt.authenticate(promptInfo)
94+
}
8095
}
8196
}

app/src/main/java/com/getcode/view/main/account/AppSettingsScreen.kt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,15 +4,22 @@ import androidx.compose.foundation.ExperimentalFoundationApi
44
import androidx.compose.foundation.lazy.LazyColumn
55
import androidx.compose.foundation.lazy.items
66
import androidx.compose.runtime.Composable
7+
import androidx.compose.runtime.LaunchedEffect
78
import androidx.compose.runtime.collectAsState
89
import androidx.compose.runtime.getValue
10+
import androidx.compose.runtime.mutableStateOf
11+
import androidx.compose.runtime.remember
912
import androidx.compose.runtime.rememberCoroutineScope
13+
import androidx.compose.runtime.setValue
1014
import androidx.compose.ui.Modifier
1115
import androidx.compose.ui.platform.LocalContext
1216
import androidx.compose.ui.res.stringResource
17+
import com.getcode.LocalBiometricsState
18+
import com.getcode.model.AppSetting
1319
import com.getcode.model.PrefsBool
1420
import com.getcode.ui.components.SettingsRow
1521
import com.getcode.util.Biometrics
22+
import com.getcode.view.main.home.components.rememberBiometricsState
1623
import kotlinx.coroutines.launch
1724

1825
@OptIn(ExperimentalFoundationApi::class)
@@ -45,7 +52,7 @@ fun AppSettingsScreen(
4552
PrefsBool.CAMERA_START_BY_DEFAULT -> toggle()
4653
PrefsBool.REQUIRE_BIOMETRICS -> {
4754
scope.launch {
48-
Biometrics.prompt(context)
55+
Biometrics.prompt(context, delay = 300)
4956
.onSuccess { toggle() }
5057
}
5158
}

app/src/main/java/com/getcode/view/main/home/HomeScan.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,10 @@ private fun HomeScan(
157157
mutableStateOf(false)
158158
}
159159

160+
var cameraStarted by remember {
161+
mutableStateOf(dataState.autoStartCamera == true)
162+
}
163+
160164
val focusManager = LocalFocusManager.current
161165

162166
var deepLinkSaved by remember(deepLink) {
@@ -202,8 +206,10 @@ private fun HomeScan(
202206
navigator = navigator,
203207
isPaused = isPaused,
204208
isCameraReady = previewing,
209+
isCameraStarted = cameraStarted,
205210
dataState = dataState,
206211
homeViewModel = homeViewModel,
212+
onStartCamera = { cameraStarted = true },
207213
scannerView = {
208214
CodeScanner(
209215
scanningEnabled = previewing,
@@ -227,6 +233,9 @@ private fun HomeScan(
227233

228234
Lifecycle.Event.ON_STOP -> {
229235
Timber.d("onStop")
236+
if (dataState.autoStartCamera == false) {
237+
cameraStarted = false
238+
}
230239
}
231240

232241
Lifecycle.Event.ON_PAUSE -> {
@@ -272,10 +281,12 @@ private fun BillContainer(
272281
modifier: Modifier = Modifier,
273282
navigator: CodeNavigator,
274283
isCameraReady: Boolean,
284+
isCameraStarted: Boolean,
275285
isPaused: Boolean,
276286
dataState: HomeUiModel,
277287
homeViewModel: HomeViewModel,
278288
scannerView: @Composable () -> Unit,
289+
onStartCamera: () -> Unit,
279290
showBottomSheet: (HomeBottomSheet) -> Unit,
280291
) {
281292
val onPermissionResult =
@@ -308,9 +319,9 @@ private fun BillContainer(
308319
dataState.isCameraPermissionGranted == true || dataState.isCameraPermissionGranted == null -> {
309320
if (dataState.autoStartCamera == null) {
310321
// waiting for result
311-
} else if (!dataState.autoStartCamera) {
322+
} else if (!dataState.autoStartCamera && !isCameraStarted) {
312323
CameraDisabledView(modifier = Modifier.fillMaxSize()) {
313-
homeViewModel.onStartCamera()
324+
onStartCamera()
314325
}
315326
} else {
316327
scannerView()

app/src/main/java/com/getcode/view/main/home/HomeViewModel.kt

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -381,10 +381,6 @@ class HomeViewModel @Inject constructor(
381381
}
382382
}
383383

384-
fun onStartCamera() {
385-
uiFlow.update { it.copy(autoStartCamera = true) }
386-
}
387-
388384
fun onCameraPermissionChanged(isGranted: Boolean) {
389385
uiFlow.update { it.copy(isCameraPermissionGranted = isGranted) }
390386
}

app/src/main/java/com/getcode/view/main/home/components/BiometricsBlockingView.kt

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -64,13 +64,14 @@ data class BiometricsState(
6464
val passed: Boolean = false,
6565
val request: () -> Unit = { },
6666
) {
67-
val isAwaitingAuthentication = checking || !passed
67+
val isAwaitingAuthentication: Boolean
68+
get() = checking || !passed
6869
}
6970

7071
@Composable
7172
internal fun rememberBiometricsState(
7273
requireBiometrics: Boolean?,
73-
onBiometricsNotEnrolled: () -> Unit,
74+
onError: (BiometricsError) -> Unit = { },
7475
): BiometricsState {
7576
val context = LocalContext.current
7677

@@ -92,11 +93,9 @@ internal fun rememberBiometricsState(
9293
.onFailure {
9394
val error = it as? BiometricsException
9495
error?.let { exception ->
95-
if (exception.error == BiometricsError.NoBiometrics) {
96-
Timber.e("missing biometrics")
97-
onBiometricsNotEnrolled()
98-
}
96+
onError(exception.error)
9997
}
98+
biometricsPassed = false
10099
checkBiometrics = false
101100
}
102101
.onSuccess {

app/src/main/java/com/getcode/view/main/home/components/CodeScanner.kt

Lines changed: 24 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -28,9 +28,11 @@ import androidx.compose.ui.platform.LocalLifecycleOwner
2828
import androidx.compose.ui.viewinterop.AndroidView
2929
import androidx.lifecycle.Lifecycle
3030
import androidx.lifecycle.asFlow
31+
import com.getcode.LocalBiometricsState
3132
import com.getcode.theme.CodeTheme
3233
import com.getcode.ui.components.OnLifecycleEvent
3334
import com.getcode.ui.utils.AnimationUtils
35+
import com.getcode.util.Biometrics
3436
import com.getcode.utils.trace
3537
import com.kik.kikx.kikcodes.implementation.KikCodeAnalyzer
3638
import com.kik.kikx.kikcodes.implementation.KikCodeScannerImpl
@@ -86,12 +88,19 @@ fun CodeScanner(
8688
mutableStateOf(false)
8789
}
8890

91+
val biometricsState = LocalBiometricsState.current
92+
8993
val scope = rememberCoroutineScope()
90-
LaunchedEffect(scanner) {
94+
LaunchedEffect(scanner, biometricsState.isAwaitingAuthentication, Biometrics.promptActive) {
95+
val active = Biometrics.promptActive || biometricsState.isAwaitingAuthentication
9196
val cameraProvider = context.getCameraProvider()
92-
cameraProvider.unbindAll()
93-
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis)
94-
bound = true
97+
if (!active) {
98+
cameraProvider.unbindAll()
99+
cameraProvider.bindToLifecycle(lifecycleOwner, cameraSelector, preview, imageAnalysis)
100+
bound = true
101+
} else {
102+
cameraProvider.unbindAll()
103+
}
95104
}
96105

97106
OnLifecycleEvent { _, event ->
@@ -104,15 +113,17 @@ fun CodeScanner(
104113
} else if (event == Lifecycle.Event.ON_RESUME) {
105114
scope.launch {
106115
if (!bound) {
107-
val cameraProvider = context.getCameraProvider()
108-
cameraProvider.unbindAll()
109-
cameraProvider.bindToLifecycle(
110-
lifecycleOwner,
111-
cameraSelector,
112-
preview,
113-
imageAnalysis
114-
)
115-
bound = true
116+
if (!biometricsState.isAwaitingAuthentication) {
117+
val cameraProvider = context.getCameraProvider()
118+
cameraProvider.unbindAll()
119+
cameraProvider.bindToLifecycle(
120+
lifecycleOwner,
121+
cameraSelector,
122+
preview,
123+
imageAnalysis
124+
)
125+
bound = true
126+
}
116127
}
117128
}
118129
}

vendor/tipkit/tipkit/src/main/kotlin/dev/bmcreations/tipkit/Tip.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,6 @@ abstract class Tip(
7878
* for display.
7979
*/
8080
suspend fun show(): Boolean {
81-
println("show tip?")
8281
return criteria().all { it() }
8382
}
8483

0 commit comments

Comments
 (0)