Skip to content

Commit 9885ecf

Browse files
committed
feat: add QR support to scanner to support scanning desktop QRs
Signed-off-by: Brandon McAnsh <git@bmcreations.dev>
1 parent d0e5b02 commit 9885ecf

18 files changed

Lines changed: 388 additions & 101 deletions

File tree

apps/flipcash/features/scanner/build.gradle.kts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,15 @@ dependencies {
5858
implementation(project(":apps:flipcash:shared:appupdates"))
5959
implementation(project(":apps:flipcash:shared:bills"))
6060
implementation(project(":apps:flipcash:shared:bill-customization"))
61+
implementation(project(":apps:flipcash:shared:router"))
6162
implementation(project(":apps:flipcash:shared:session"))
6263

64+
implementation(project(":libs:code-detection"))
6365
implementation(project(":libs:datetime"))
6466
implementation(project(":libs:logging"))
6567
implementation(project(":libs:messaging"))
6668
implementation(project(":libs:permissions:bindings"))
67-
implementation(project(":ui:analytics"))
69+
implementation(project(":libs:vibrator:bindings"))
6870
implementation(project(":ui:biometrics"))
6971
implementation(project(":ui:core"))
7072
implementation(project(":ui:components"))

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/Scanner.kt

Lines changed: 35 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,22 +16,29 @@ import androidx.lifecycle.Lifecycle
1616
import cafe.adriel.voyager.core.registry.ScreenRegistry
1717
import cafe.adriel.voyager.navigator.currentOrThrow
1818
import com.flipcash.app.core.navigation.DeeplinkType
19+
import com.flipcash.app.router.LocalRouter
1920
import com.flipcash.app.scanner.internal.bills.BillContainer
2021
import com.flipcash.app.session.LocalSessionController
2122
import com.flipcash.features.scanner.R
23+
import com.getcode.libs.code.detection.CodeScanResult
2224
import com.getcode.manager.BottomBarManager
2325
import com.getcode.navigation.core.LocalCodeNavigator
2426
import com.getcode.opencode.model.financial.orZero
2527
import com.getcode.ui.components.OnLifecycleEvent
2628
import com.getcode.ui.scanner.CodeScanner
2729
import com.getcode.ui.scanner.NoCamerasAvailableException
30+
import com.getcode.util.vibration.LocalVibrator
2831
import com.getcode.utils.ErrorUtils
32+
import com.kik.kikx.kikcodes.implementation.KikCodeResult
33+
import dev.theolm.rinku.DeepLink
34+
import kotlinx.coroutines.delay
2935
import timber.log.Timber
3036
import java.util.Timer
3137
import kotlin.concurrent.schedule
3238

3339
@Composable
3440
internal fun Scanner(deepLink: DeeplinkType?) {
41+
val router = LocalRouter.currentOrThrow
3542
val navigator = LocalCodeNavigator.current
3643
val session = LocalSessionController.currentOrThrow
3744
val state by session.state.collectAsState()
@@ -58,12 +65,20 @@ internal fun Scanner(deepLink: DeeplinkType?) {
5865

5966
val context = LocalContext.current
6067

68+
var deepLinkSaved by remember(deepLink) {
69+
mutableStateOf(deepLink)
70+
}
71+
72+
val vibrator = LocalVibrator.current
73+
6174
ScannerDeepLinkHandler(
62-
deepLink = deepLink,
75+
deepLink = deepLinkSaved,
6376
previewing = previewing,
6477
session = session,
6578
navigator = navigator
66-
)
79+
) {
80+
deepLinkSaved = null
81+
}
6782

6883
@SuppressLint("LocalContextGetResourceValueCall")
6984
BillContainer(
@@ -97,7 +112,24 @@ internal fun Scanner(deepLink: DeeplinkType?) {
97112
cameraAvailable = true
98113
previewing = it
99114
},
100-
onCodeScanned = session::onCodeScan,
115+
onCodeScanned = { result ->
116+
when (result) {
117+
is CodeScanResult.QrCode -> {
118+
val urls = result.results
119+
val deeplink = urls.firstNotNullOfOrNull { url ->
120+
router.processType(DeepLink(url))
121+
}
122+
println("deeplink type = $deeplink")
123+
if (deeplink != null) {
124+
vibrator.vibrate(duration = 50)
125+
deepLinkSaved = deeplink
126+
}
127+
}
128+
is KikCodeResult -> {
129+
session.onCodeScan(result.kikCode)
130+
}
131+
}
132+
},
101133
onError = {
102134
if (it is NoCamerasAvailableException) {
103135
cameraAvailable = false

apps/flipcash/features/scanner/src/main/kotlin/com/flipcash/app/scanner/internal/ScannerDeepLinkHandler.kt

Lines changed: 8 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -18,11 +18,9 @@ internal fun ScannerDeepLinkHandler(
1818
deepLink: DeeplinkType?,
1919
previewing: Boolean?,
2020
session: SessionController,
21-
navigator: CodeNavigator
21+
navigator: CodeNavigator,
22+
onDeeplinkHandled: () -> Unit,
2223
) {
23-
var deepLinkSaved by remember {
24-
mutableStateOf(deepLink)
25-
}
2624

2725
val focusManager = LocalFocusManager.current
2826
val biometricsState = LocalBiometricsState.current
@@ -36,7 +34,7 @@ internal fun ScannerDeepLinkHandler(
3634
LaunchedEffect(
3735
biometricsState,
3836
previewing,
39-
deepLinkSaved
37+
deepLink
4038
) {
4139
if (previewing == true) {
4240
focusManager.clearFocus()
@@ -48,19 +46,19 @@ internal fun ScannerDeepLinkHandler(
4846
session.onCameraScanning(previewing)
4947
}
5048

51-
val deeplink = deepLinkSaved ?: return@LaunchedEffect
49+
val link = deepLink ?: return@LaunchedEffect
5250

53-
when (deeplink) {
51+
when (link) {
5452
is DeeplinkType.CashLink -> {
55-
session.openCashLink(deeplink.entropy)
53+
session.openCashLink(link.entropy)
5654
}
5755

5856
is DeeplinkType.Login -> Unit
5957
is DeeplinkType.Navigatable -> {
60-
stateRestorer.restoreState(deeplink, animationScale)
58+
stateRestorer.restoreState(link, animationScale)
6159
}
6260
}
6361

64-
deepLinkSaved = null
62+
onDeeplinkHandled()
6563
}
6664
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.flipcash.app.scanner.internal
2+
3+
import androidx.compose.runtime.Composable
4+
5+
@Composable
6+
internal fun ScannerQrHandler() {
7+
8+
}

buildSrc/src/main/java/Dependencies.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ object Versions {
5454
const val crashlytics_gradle: String = "3.0.2"
5555
const val play_service_auth = "21.4.0"
5656
const val play_service_auth_phone = "18.3.0"
57+
const val play_service_ml_barcode = "18.3.1"
5758
const val google_play_billing = "8.0.0"
5859
const val google_play_updates = "2.1.0"
5960

@@ -286,6 +287,9 @@ object Libs {
286287
const val play_service_auth_phone =
287288
"com.google.android.gms:play-services-auth-api-phone:${Versions.play_service_auth_phone}"
288289

290+
const val play_service_ml_barcode =
291+
"com.google.android.gms:play-services-mlkit-barcode-scanning:${Versions.play_service_ml_barcode}"
292+
289293
const val google_play_billing_runtime =
290294
"com.android.billingclient:billing:${Versions.google_play_billing}"
291295
const val google_play_billing_ktx =

libs/code-detection/.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
build/
2+
.gradle/
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import org.jetbrains.kotlin.gradle.dsl.JvmTarget
2+
3+
buildscript {
4+
repositories {
5+
mavenCentral()
6+
}
7+
dependencies {
8+
classpath(Libs.kotlinx_serialization_json)
9+
}
10+
}
11+
12+
plugins {
13+
id(Plugins.android_library)
14+
id(Plugins.kotlin_android)
15+
id(Plugins.kotlin_kapt)
16+
id(Plugins.kotlin_serialization)
17+
}
18+
19+
android {
20+
namespace = "${Gradle.codeNamespace}.libs.code.detection"
21+
compileSdk = Android.compileSdkVersion
22+
defaultConfig {
23+
minSdk = Android.minSdkVersion
24+
testInstrumentationRunner = Android.testInstrumentationRunner
25+
}
26+
}
27+
28+
kotlin {
29+
jvmToolchain {
30+
languageVersion.set(JavaLanguageVersion.of(Versions.java))
31+
}
32+
33+
compilerOptions {
34+
jvmTarget.set(JvmTarget.fromTarget(Versions.java))
35+
optIn.addAll(
36+
"kotlin.time.ExperimentalTime",
37+
"kotlin.ExperimentalUnsignedTypes",
38+
"kotlin.RequiresOptIn"
39+
)
40+
}
41+
}
42+
43+
dependencies {
44+
implementation(Libs.inject)
45+
implementation(Libs.hilt)
46+
47+
api(Libs.androidx_camerax_core)
48+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package com.getcode.libs.code.detection
2+
3+
import androidx.camera.core.ImageProxy
4+
5+
interface CodeScanResult {
6+
7+
data class QrCode(
8+
val results: List<String>,
9+
) : CodeScanResult
10+
}
11+
12+
interface CodeDetector<T> {
13+
suspend fun detect(image: ImageProxy): CodeScanResult?
14+
}

libs/quickresponse/build.gradle.kts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -42,12 +42,14 @@ kotlin {
4242
}
4343

4444
dependencies {
45+
implementation(project(":libs:code-detection"))
46+
4547
//Jetpack compose
4648
implementation(platform(Libs.compose_bom))
4749
implementation(Libs.compose_ui)
4850

49-
api(Libs.zxing)
50-
51+
implementation(Libs.zxing)
52+
implementation(Libs.play_service_ml_barcode)
5153
implementation(Libs.inject)
5254

5355

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
2+
3+
<application>
4+
<meta-data
5+
android:name="com.google.mlkit.vision.DEPENDENCIES"
6+
android:value="barcode" />
7+
</application>
8+
9+
</manifest>

0 commit comments

Comments
 (0)