Skip to content

Commit

Permalink
Merge pull request #15 from kalinjul/update-compose
Browse files Browse the repository at this point in the history
update to compose 1.7
  • Loading branch information
kalinjul authored Oct 24, 2024
2 parents 9224524 + bb1e4d3 commit 10db4e6
Show file tree
Hide file tree
Showing 6 changed files with 97 additions and 44 deletions.
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,19 +12,19 @@ Supported Compose version:

| Compose version | EasyQRScan Version |
|-----------------|--------------------|
| 1.6.x | 0.1.x |
| 1.7 | Not yet supported |
| 1.6.x | 0.1.0+ |
| 1.7 | 0.2.0+ |

# Dependency
Add the dependency to your commonMain sourceSet (KMP) / Android dependencies (android only):
```kotlin
implementation("io.github.kalinjul.easyqrscan:scanner:0.1.6")
implementation("io.github.kalinjul.easyqrscan:scanner:0.2.0")
```

Or, for your libs.versions.toml:
```toml
[versions]
easyqrscan = "0.1.6"
easyqrscan = "0.2.0"
[libraries]
easyqrscan = { module = "io.github.kalinjul.easyqrscan:scanner", version.ref = "easyqrscan" }
```
Expand Down
12 changes: 6 additions & 6 deletions gradle/libs.versions.toml
Original file line number Diff line number Diff line change
Expand Up @@ -8,25 +8,25 @@ jvmTarget = "17"
agp = "8.4.2"

#https://github.com/JetBrains/compose-multiplatform
compose-multiplatform = "1.6.11"
kotlin = "2.0.0"
compose-multiplatform = "1.7.0"
kotlin = "2.0.20"
# https://github.com/google/ksp
ksp = "2.0.0-1.0.21"
ksp = "2.0.20-1.0.25"

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

# https://developer.android.com/jetpack/androidx/releases/activity
androidxActivity = "1.9.0"
androidxActivity = "1.9.3"
# https://developer.android.com/jetpack/androidx/releases/appcompat
androidxAppCompat = "1.7.0"
coreKtx = "1.13.1"

dokka = "1.9.10"
nexus-publish-plugin = "1.3.0"
accompanist = "0.34.0"
androidxCamera = "1.3.3"
mlkit = "17.2.0"
androidxCamera = "1.3.4"
mlkit = "17.3.0"

[libraries]
androidx-activity-compose = { module = "androidx.activity:activity-compose", version.ref = "androidxActivity" }
Expand Down
47 changes: 34 additions & 13 deletions sample-app/shared/src/commonMain/kotlin/MainView.kt
Original file line number Diff line number Diff line change
@@ -1,34 +1,55 @@
package org.publicvalue.multiplatform.qrcode.sample

import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHost
import androidx.compose.material3.SnackbarHostState
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import kotlinx.coroutines.launch
import org.publicvalue.multiplatform.qrcode.CodeType
import org.publicvalue.multiplatform.qrcode.ScannerWithPermissions

@Composable
fun MainView() {
Column() {
Text("Scan QR-Code below")
var scannerVisible by remember {mutableStateOf(false)}
Button(onClick = {
scannerVisible = !scannerVisible
}) {
Text("Toggle scanner (visible: $scannerVisible)")
}
if (scannerVisible) {
ScannerWithPermissions(
modifier = Modifier.padding(16.dp),
onScanned = { println(it); true }, types = listOf(CodeType.QR)
)
Box() {
val snackbarHostState = remember() { SnackbarHostState() }

Column() {
Text("Scan QR-Code below")
var scannerVisible by remember {mutableStateOf(false)}
Button(onClick = {
scannerVisible = !scannerVisible
}) {
Text("Toggle scanner (visible: $scannerVisible)")
}
if (scannerVisible) {
val scope = rememberCoroutineScope()
ScannerWithPermissions(
modifier = Modifier.padding(16.dp),
onScanned = {
scope.launch {
snackbarHostState.showSnackbar(it, duration = SnackbarDuration.Short)
}
false // continue scanning
}, types = listOf(CodeType.QR)
)
}
}
SnackbarHost(
modifier = Modifier.align(Alignment.BottomCenter).padding(bottom = 20.dp),
hostState = snackbarHostState
)
}
}
31 changes: 29 additions & 2 deletions scanner/src/commonMain/kotlin/Scanner.kt
Original file line number Diff line number Diff line change
Expand Up @@ -11,23 +11,41 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.unit.dp

/**
* Code Scanner
*
* @param types Code types to scan.
* @param onScanned Called when a code was scanned. The given lambda should return true
* if scanning was successful and scanning should be aborted.
* Return false if scanning should continue.
*/
@Composable
expect fun Scanner(
modifier: Modifier = Modifier,
onScanned: (String) -> Boolean,
types: List<CodeType>
)

/**
* Code Scanner with permission handling.
*
* @param types Code types to scan.
* @param onScanned Called when a code was scanned. The given lambda should return true
* if scanning was successful and scanning should be aborted.
* Return false if scanning should continue.
* @param permissionText Text to show if permission was denied.
* @param openSettingsLabel Label to show on the "Go to settings" Button
*/
@Composable
fun ScannerWithPermissions(
modifier: Modifier = Modifier.clipToBounds(),
modifier: Modifier = Modifier,
onScanned: (String) -> Boolean,
types: List<CodeType>,
permissionText: String = "Camera is required for QR Code scanning",
openSettingsLabel: String = "Open Settings",
) {
ScannerWithPermissions(
modifier = modifier,
modifier = modifier.clipToBounds(),
onScanned = onScanned,
types = types,
permissionDeniedContent = { permissionState ->
Expand All @@ -44,6 +62,15 @@ fun ScannerWithPermissions(
)
}

/**
* Code Scanner with permission handling.
*
* @param types Code types to scan.
* @param onScanned Called when a code was scanned. The given lambda should return true
* if scanning was successful and scanning should be aborted.
* Return false if scanning should continue.
* @param permissionDeniedContent Content to show if permission was denied.
*/
@Composable
fun ScannerWithPermissions(
modifier: Modifier = Modifier,
Expand Down
1 change: 0 additions & 1 deletion scanner/src/iosMain/kotlin/Scanner.ios.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ import platform.Foundation.NSURL
import platform.UIKit.UIApplication
import platform.UIKit.UIApplicationOpenSettingsURLString


@Composable
actual fun Scanner(
modifier: Modifier,
Expand Down
42 changes: 24 additions & 18 deletions scanner/src/iosMain/kotlin/ScannerView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -5,13 +5,14 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.interop.UIKitView
import androidx.compose.ui.viewinterop.UIKitInteropProperties
import androidx.compose.ui.viewinterop.UIKitView
import kotlinx.cinterop.BetaInteropApi
import kotlinx.cinterop.CValue
import kotlinx.cinterop.ExperimentalForeignApi
import kotlinx.cinterop.ObjCObjectVar
import kotlinx.cinterop.alloc
import kotlinx.cinterop.cValue
import kotlinx.cinterop.memScoped
import kotlinx.cinterop.ptr
import kotlinx.cinterop.useContents
Expand All @@ -33,9 +34,9 @@ import platform.AVFoundation.AVLayerVideoGravityResizeAspectFill
import platform.AVFoundation.AVMediaTypeVideo
import platform.AVFoundation.AVMetadataMachineReadableCodeObject
import platform.AVFoundation.AVMetadataObjectType
import platform.AVFoundation.AVMetadataObjectTypeQRCode
import platform.AudioToolbox.AudioServicesPlaySystemSound
import platform.CoreGraphics.CGRect
import platform.CoreGraphics.CGRectZero
import platform.Foundation.NSError
import platform.QuartzCore.CALayer
import platform.QuartzCore.CATransaction
Expand All @@ -46,7 +47,6 @@ import platform.UIKit.UIView
import platform.darwin.NSObject
import platform.darwin.dispatch_get_main_queue

@OptIn(ExperimentalForeignApi::class)
@Composable
fun UiScannerView(
modifier: Modifier = Modifier,
Expand All @@ -72,24 +72,18 @@ fun UiScannerView(
}
}

UIKitView(
UIKitView<UIView>(
modifier = modifier.fillMaxSize(),
background = Color.Black,
factory = {
val previewContainer = UIView()
val previewContainer = ScannerPreviewView(coordinator)
println("Calling prepare")
coordinator.prepare(previewContainer.layer, allowedMetadataTypes)
previewContainer
},
update = {
},
onResize = { view, rect ->
CATransaction.begin()
CATransaction.setValue(true, kCATransactionDisableActions)
view.layer.setFrame(rect)
coordinator.setFrame(rect)
CATransaction.commit()
}
properties = UIKitInteropProperties(
isInteractive = true,
isNativeAccessibilityEnabled = true,
)
)

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

}

@OptIn(ExperimentalForeignApi::class)
class ScannerPreviewView(private val coordinator: ScannerCameraCoordinator): UIView(frame = cValue { CGRectZero }) {
@OptIn(ExperimentalForeignApi::class)
override fun layoutSubviews() {
super.layoutSubviews()
CATransaction.begin()
CATransaction.setValue(true, kCATransactionDisableActions)

layer.setFrame(frame)
coordinator.setFrame(frame)
CATransaction.commit()
}
}

@OptIn(ExperimentalForeignApi::class)
class ScannerCameraCoordinator(
val onScanned: (String) -> Boolean
Expand Down Expand Up @@ -195,8 +203,6 @@ class ScannerCameraCoordinator(
}

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

0 comments on commit 10db4e6

Please sign in to comment.