Skip to content

Commit 10db4e6

Browse files
authored
Merge pull request #15 from kalinjul/update-compose
update to compose 1.7
2 parents 9224524 + bb1e4d3 commit 10db4e6

6 files changed

Lines changed: 97 additions & 44 deletions

File tree

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,19 +12,19 @@ Supported Compose version:
1212

1313
| Compose version | EasyQRScan Version |
1414
|-----------------|--------------------|
15-
| 1.6.x | 0.1.x |
16-
| 1.7 | Not yet supported |
15+
| 1.6.x | 0.1.0+ |
16+
| 1.7 | 0.2.0+ |
1717

1818
# Dependency
1919
Add the dependency to your commonMain sourceSet (KMP) / Android dependencies (android only):
2020
```kotlin
21-
implementation("io.github.kalinjul.easyqrscan:scanner:0.1.6")
21+
implementation("io.github.kalinjul.easyqrscan:scanner:0.2.0")
2222
```
2323

2424
Or, for your libs.versions.toml:
2525
```toml
2626
[versions]
27-
easyqrscan = "0.1.6"
27+
easyqrscan = "0.2.0"
2828
[libraries]
2929
easyqrscan = { module = "io.github.kalinjul.easyqrscan:scanner", version.ref = "easyqrscan" }
3030
```

gradle/libs.versions.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -8,25 +8,25 @@ jvmTarget = "17"
88
agp = "8.4.2"
99

1010
#https://github.com/JetBrains/compose-multiplatform
11-
compose-multiplatform = "1.6.11"
12-
kotlin = "2.0.0"
11+
compose-multiplatform = "1.7.0"
12+
kotlin = "2.0.20"
1313
# https://github.com/google/ksp
14-
ksp = "2.0.0-1.0.21"
14+
ksp = "2.0.20-1.0.25"
1515

1616
#https://mvnrepository.com/artifact/org.jetbrains.compose.compiler/compiler
1717
#composeCompiler = "1.5.8.1"
1818

1919
# https://developer.android.com/jetpack/androidx/releases/activity
20-
androidxActivity = "1.9.0"
20+
androidxActivity = "1.9.3"
2121
# https://developer.android.com/jetpack/androidx/releases/appcompat
2222
androidxAppCompat = "1.7.0"
2323
coreKtx = "1.13.1"
2424

2525
dokka = "1.9.10"
2626
nexus-publish-plugin = "1.3.0"
2727
accompanist = "0.34.0"
28-
androidxCamera = "1.3.3"
29-
mlkit = "17.2.0"
28+
androidxCamera = "1.3.4"
29+
mlkit = "17.3.0"
3030

3131
[libraries]
3232
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
Lines changed: 34 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,34 +1,55 @@
11
package org.publicvalue.multiplatform.qrcode.sample
22

3+
import androidx.compose.foundation.layout.Box
34
import androidx.compose.foundation.layout.Column
45
import androidx.compose.foundation.layout.padding
56
import androidx.compose.material3.Button
7+
import androidx.compose.material3.SnackbarDuration
8+
import androidx.compose.material3.SnackbarHost
9+
import androidx.compose.material3.SnackbarHostState
610
import androidx.compose.material3.Text
711
import androidx.compose.runtime.Composable
812
import androidx.compose.runtime.getValue
913
import androidx.compose.runtime.mutableStateOf
1014
import androidx.compose.runtime.remember
15+
import androidx.compose.runtime.rememberCoroutineScope
1116
import androidx.compose.runtime.setValue
17+
import androidx.compose.ui.Alignment
1218
import androidx.compose.ui.Modifier
1319
import androidx.compose.ui.unit.dp
20+
import kotlinx.coroutines.launch
1421
import org.publicvalue.multiplatform.qrcode.CodeType
1522
import org.publicvalue.multiplatform.qrcode.ScannerWithPermissions
1623

1724
@Composable
1825
fun MainView() {
19-
Column() {
20-
Text("Scan QR-Code below")
21-
var scannerVisible by remember {mutableStateOf(false)}
22-
Button(onClick = {
23-
scannerVisible = !scannerVisible
24-
}) {
25-
Text("Toggle scanner (visible: $scannerVisible)")
26-
}
27-
if (scannerVisible) {
28-
ScannerWithPermissions(
29-
modifier = Modifier.padding(16.dp),
30-
onScanned = { println(it); true }, types = listOf(CodeType.QR)
31-
)
26+
Box() {
27+
val snackbarHostState = remember() { SnackbarHostState() }
28+
29+
Column() {
30+
Text("Scan QR-Code below")
31+
var scannerVisible by remember {mutableStateOf(false)}
32+
Button(onClick = {
33+
scannerVisible = !scannerVisible
34+
}) {
35+
Text("Toggle scanner (visible: $scannerVisible)")
36+
}
37+
if (scannerVisible) {
38+
val scope = rememberCoroutineScope()
39+
ScannerWithPermissions(
40+
modifier = Modifier.padding(16.dp),
41+
onScanned = {
42+
scope.launch {
43+
snackbarHostState.showSnackbar(it, duration = SnackbarDuration.Short)
44+
}
45+
false // continue scanning
46+
}, types = listOf(CodeType.QR)
47+
)
48+
}
3249
}
50+
SnackbarHost(
51+
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 20.dp),
52+
hostState = snackbarHostState
53+
)
3354
}
3455
}

scanner/src/commonMain/kotlin/Scanner.kt

Lines changed: 29 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,41 @@ import androidx.compose.ui.Modifier
1111
import androidx.compose.ui.draw.clipToBounds
1212
import androidx.compose.ui.unit.dp
1313

14+
/**
15+
* Code Scanner
16+
*
17+
* @param types Code types to scan.
18+
* @param onScanned Called when a code was scanned. The given lambda should return true
19+
* if scanning was successful and scanning should be aborted.
20+
* Return false if scanning should continue.
21+
*/
1422
@Composable
1523
expect fun Scanner(
1624
modifier: Modifier = Modifier,
1725
onScanned: (String) -> Boolean,
1826
types: List<CodeType>
1927
)
2028

29+
/**
30+
* Code Scanner with permission handling.
31+
*
32+
* @param types Code types to scan.
33+
* @param onScanned Called when a code was scanned. The given lambda should return true
34+
* if scanning was successful and scanning should be aborted.
35+
* Return false if scanning should continue.
36+
* @param permissionText Text to show if permission was denied.
37+
* @param openSettingsLabel Label to show on the "Go to settings" Button
38+
*/
2139
@Composable
2240
fun ScannerWithPermissions(
23-
modifier: Modifier = Modifier.clipToBounds(),
41+
modifier: Modifier = Modifier,
2442
onScanned: (String) -> Boolean,
2543
types: List<CodeType>,
2644
permissionText: String = "Camera is required for QR Code scanning",
2745
openSettingsLabel: String = "Open Settings",
2846
) {
2947
ScannerWithPermissions(
30-
modifier = modifier,
48+
modifier = modifier.clipToBounds(),
3149
onScanned = onScanned,
3250
types = types,
3351
permissionDeniedContent = { permissionState ->
@@ -44,6 +62,15 @@ fun ScannerWithPermissions(
4462
)
4563
}
4664

65+
/**
66+
* Code Scanner with permission handling.
67+
*
68+
* @param types Code types to scan.
69+
* @param onScanned Called when a code was scanned. The given lambda should return true
70+
* if scanning was successful and scanning should be aborted.
71+
* Return false if scanning should continue.
72+
* @param permissionDeniedContent Content to show if permission was denied.
73+
*/
4774
@Composable
4875
fun ScannerWithPermissions(
4976
modifier: Modifier = Modifier,

scanner/src/iosMain/kotlin/Scanner.ios.kt

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,6 @@ import platform.Foundation.NSURL
1515
import platform.UIKit.UIApplication
1616
import platform.UIKit.UIApplicationOpenSettingsURLString
1717

18-
1918
@Composable
2019
actual fun Scanner(
2120
modifier: Modifier,

scanner/src/iosMain/kotlin/ScannerView.kt

Lines changed: 24 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -5,13 +5,14 @@ import androidx.compose.runtime.Composable
55
import androidx.compose.runtime.DisposableEffect
66
import androidx.compose.runtime.remember
77
import androidx.compose.ui.Modifier
8-
import androidx.compose.ui.graphics.Color
9-
import androidx.compose.ui.interop.UIKitView
8+
import androidx.compose.ui.viewinterop.UIKitInteropProperties
9+
import androidx.compose.ui.viewinterop.UIKitView
1010
import kotlinx.cinterop.BetaInteropApi
1111
import kotlinx.cinterop.CValue
1212
import kotlinx.cinterop.ExperimentalForeignApi
1313
import kotlinx.cinterop.ObjCObjectVar
1414
import kotlinx.cinterop.alloc
15+
import kotlinx.cinterop.cValue
1516
import kotlinx.cinterop.memScoped
1617
import kotlinx.cinterop.ptr
1718
import kotlinx.cinterop.useContents
@@ -33,9 +34,9 @@ import platform.AVFoundation.AVLayerVideoGravityResizeAspectFill
3334
import platform.AVFoundation.AVMediaTypeVideo
3435
import platform.AVFoundation.AVMetadataMachineReadableCodeObject
3536
import platform.AVFoundation.AVMetadataObjectType
36-
import platform.AVFoundation.AVMetadataObjectTypeQRCode
3737
import platform.AudioToolbox.AudioServicesPlaySystemSound
3838
import platform.CoreGraphics.CGRect
39+
import platform.CoreGraphics.CGRectZero
3940
import platform.Foundation.NSError
4041
import platform.QuartzCore.CALayer
4142
import platform.QuartzCore.CATransaction
@@ -46,7 +47,6 @@ import platform.UIKit.UIView
4647
import platform.darwin.NSObject
4748
import platform.darwin.dispatch_get_main_queue
4849

49-
@OptIn(ExperimentalForeignApi::class)
5050
@Composable
5151
fun UiScannerView(
5252
modifier: Modifier = Modifier,
@@ -72,24 +72,18 @@ fun UiScannerView(
7272
}
7373
}
7474

75-
UIKitView(
75+
UIKitView<UIView>(
7676
modifier = modifier.fillMaxSize(),
77-
background = Color.Black,
7877
factory = {
79-
val previewContainer = UIView()
78+
val previewContainer = ScannerPreviewView(coordinator)
8079
println("Calling prepare")
8180
coordinator.prepare(previewContainer.layer, allowedMetadataTypes)
8281
previewContainer
8382
},
84-
update = {
85-
},
86-
onResize = { view, rect ->
87-
CATransaction.begin()
88-
CATransaction.setValue(true, kCATransactionDisableActions)
89-
view.layer.setFrame(rect)
90-
coordinator.setFrame(rect)
91-
CATransaction.commit()
92-
}
83+
properties = UIKitInteropProperties(
84+
isInteractive = true,
85+
isNativeAccessibilityEnabled = true,
86+
)
9387
)
9488

9589
// DisposableEffect(Unit) {
@@ -101,6 +95,20 @@ fun UiScannerView(
10195

10296
}
10397

98+
@OptIn(ExperimentalForeignApi::class)
99+
class ScannerPreviewView(private val coordinator: ScannerCameraCoordinator): UIView(frame = cValue { CGRectZero }) {
100+
@OptIn(ExperimentalForeignApi::class)
101+
override fun layoutSubviews() {
102+
super.layoutSubviews()
103+
CATransaction.begin()
104+
CATransaction.setValue(true, kCATransactionDisableActions)
105+
106+
layer.setFrame(frame)
107+
coordinator.setFrame(frame)
108+
CATransaction.commit()
109+
}
110+
}
111+
104112
@OptIn(ExperimentalForeignApi::class)
105113
class ScannerCameraCoordinator(
106114
val onScanned: (String) -> Boolean
@@ -195,8 +203,6 @@ class ScannerCameraCoordinator(
195203
}
196204

197205
fun onFound(code: String) {
198-
// kSystemSoundID_UserPreferredAlert = 0x00001000
199-
AudioServicesPlaySystemSound(0x1000u) // Mail-Sound 1108 wäre der Photo Sound
200206
captureSession.stopRunning()
201207
if (!onScanned(code)) {
202208
GlobalScope.launch(Dispatchers.Default) {

0 commit comments

Comments
 (0)