Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,7 +118,7 @@ The library uses Kotlin Multiplatform with the following source sets:

### Published Library
- Available on Maven Central repository as `io.github.tillhub:scan-engine:x.x.x`
- Current version: 2.0.1 (see `gradle/libs.versions.toml`)
- Current version: 2.0.4 (see `gradle/libs.versions.toml`)
- Namespace: `de.tillhub.scanengine`

### Framework Generation
Expand Down
2 changes: 1 addition & 1 deletion gradle/libs.versions.toml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
[versions]
scan-engine = "2.0.3"
scan-engine = "2.0.4"
kotlin = "2.2.0"
agp = "8.11.1"
compose = "1.8.1"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48">
<path
android:pathData="M0,8C0,3.582 3.582,0 8,0H40C44.418,0 48,3.582 48,8V40C48,44.418 44.418,48 40,48H8C3.582,48 0,44.418 0,40V8Z"
android:fillColor="#EAEBEC"/>
<path
android:pathData="M30.714,17H29.434L29.114,16C28.906,15.413 28.522,14.905 28.013,14.547C27.504,14.189 26.896,13.998 26.274,14H21.154C20.525,14.001 19.913,14.2 19.404,14.567C18.894,14.935 18.513,15.454 18.314,16.05L17.994,17.05H16.714C15.918,17.05 15.155,17.366 14.593,17.929C14.03,18.491 13.714,19.254 13.714,20.05V28.05C13.714,28.846 14.03,29.609 14.593,30.171C15.155,30.734 15.918,31.05 16.714,31.05H30.714C31.51,31.05 32.273,30.734 32.835,30.171C33.398,29.609 33.714,28.846 33.714,28.05V20.05C33.721,19.652 33.648,19.256 33.5,18.887C33.352,18.517 33.132,18.18 32.853,17.896C32.574,17.612 32.241,17.387 31.874,17.233C31.506,17.079 31.112,17 30.714,17ZM31.714,28C31.714,28.265 31.609,28.52 31.421,28.707C31.233,28.895 30.979,29 30.714,29H16.714C16.449,29 16.194,28.895 16.007,28.707C15.819,28.52 15.714,28.265 15.714,28V20C15.714,19.735 15.819,19.48 16.007,19.293C16.194,19.105 16.449,19 16.714,19H18.714C18.932,19.011 19.148,18.951 19.328,18.828C19.509,18.705 19.644,18.527 19.714,18.32L20.254,16.68C20.321,16.481 20.449,16.309 20.619,16.187C20.79,16.065 20.994,15.999 21.204,16H26.324C26.534,15.999 26.738,16.065 26.909,16.187C27.079,16.309 27.207,16.481 27.274,16.68L27.814,18.32C27.878,18.511 27.998,18.678 28.159,18.799C28.319,18.92 28.513,18.99 28.714,19H30.714C30.979,19 31.233,19.105 31.421,19.293C31.609,19.48 31.714,19.735 31.714,20V28ZM23.714,19C22.923,19 22.149,19.235 21.492,19.674C20.834,20.114 20.321,20.738 20.018,21.469C19.716,22.2 19.636,23.004 19.791,23.78C19.945,24.556 20.326,25.269 20.885,25.828C21.445,26.388 22.158,26.769 22.934,26.923C23.709,27.077 24.514,26.998 25.245,26.695C25.976,26.393 26.6,25.88 27.04,25.222C27.479,24.564 27.714,23.791 27.714,23C27.714,21.939 27.292,20.922 26.542,20.172C25.792,19.421 24.775,19 23.714,19ZM23.714,25C23.318,25 22.932,24.883 22.603,24.663C22.274,24.443 22.018,24.131 21.866,23.765C21.715,23.4 21.675,22.998 21.752,22.61C21.83,22.222 22.02,21.865 22.3,21.586C22.579,21.306 22.936,21.116 23.324,21.038C23.712,20.961 24.114,21.001 24.479,21.152C24.845,21.304 25.157,21.56 25.377,21.889C25.597,22.218 25.714,22.604 25.714,23C25.714,23.53 25.503,24.039 25.128,24.414C24.753,24.789 24.244,25 23.714,25Z"
android:fillColor="#5D6476"/>
</vector>
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,13 @@

<string name="camera_title">Barcode Scannen</string>
<string name="permission_required_title">Berechtigungen</string>
<string name="permission_required_message">Um den Scanner nutzen zu können, müssen Sie den Zugriff im nächsten Berechtigungsdialog gewähren.\n\nMöchten Sie fortfahren?</string>
<string name="camera_access_required">Kamerazugang erforderlich</string>
<string name="permission_required_message">Erlauben Sie den Kamerazugang, um Barcodes zu scannen und sich mit Ihrem Kartenleser zu verbinden. Im nächsten Schritt werden Sie um Erlaubnis gebeten.</string>
<string name="permission_camera_request">Kameraberechtigungen</string>

<string name="camera_error">Es gab ein Problem bei der Anforderung einer Kamera-Schnittstelle auf Ihrem Gerät.</string>

<string name="navigate_back">Zurück navigieren</string>
<string name="ok">OK</string>
<string name="loading">Laden</string>
<string name="pairing_title">Barcode-Kopplung</string>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,11 +5,13 @@

<string name="camera_title">Scan barcode</string>
<string name="permission_required_title">Grant permissions</string>
<string name="permission_required_message">In order to use barcode reader you need to grant access by accepting the next permission dialog.\n\nWould you like to continue?</string>
<string name="camera_access_required">Camera access required</string>
<string name="permission_required_message">Allow camera access to scan barcodes and connect to your card reader. You'll be asked for permission in the next step.</string>
<string name="permission_camera_request">Request camera permission</string>

<string name="camera_error">There was an issue requesting a camera interface on your device.</string>

<string name="navigate_back">Navigate back</string>
<string name="ok">OK</string>
<string name="loading">Loading</string>
<string name="pairing_title">Barcode pairing</string>
Expand Down
Original file line number Diff line number Diff line change
@@ -1,33 +1,42 @@
package de.tillhub.scanengine.camera.ui

import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
import de.tillhub.scanengine.camera.PermissionHandler
import de.tillhub.scanengine.camera.getPermissionHandler
import de.tillhub.scanengine.resources.Res
import de.tillhub.scanengine.resources.camera_access_required
import de.tillhub.scanengine.resources.camera_error
import de.tillhub.scanengine.resources.camera_title
import de.tillhub.scanengine.resources.ic_camera
import de.tillhub.scanengine.resources.permission_camera_request
import de.tillhub.scanengine.resources.permission_required_message
import de.tillhub.scanengine.resources.permission_required_title
import de.tillhub.scanengine.ui.components.BottomButton
import de.tillhub.scanengine.ui.components.Toolbar
import de.tillhub.scanengine.ui.theme.AppTheme
import org.jetbrains.compose.resources.painterResource
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview

/**
* A Composable function that displays a camera screen for barcode scanning.
Expand All @@ -44,7 +53,6 @@ import org.jetbrains.compose.ui.tooling.preview.Preview
* @param onDismiss A callback function that is invoked when the user dismisses the screen
* (e.g., by clicking the back button in the toolbar).
*/
@Preview
@Composable
internal fun CameraScreen(
permissions: PermissionHandler = getPermissionHandler(),
Expand All @@ -58,13 +66,15 @@ internal fun CameraScreen(
AppTheme {
Scaffold(
modifier = Modifier,
containerColor = Color.White,
topBar = {
val title = if (!hasPermission.value && !cameraError.value) {
stringResource(Res.string.permission_required_title)
} else {
stringResource(Res.string.camera_title)
}
Toolbar(
title = if (hasPermission.value) {
stringResource(Res.string.camera_title)
} else {
stringResource(Res.string.permission_required_title)
},
title = title,
onClick = { onDismiss() },
)
},
Expand Down Expand Up @@ -96,7 +106,7 @@ internal fun CameraScreen(
.padding(top = 16.dp)
.semantics { contentDescription = "Camera preview" },
barcodeScanned = onResult,
onCameraError = { error ->
onCameraError = { _ ->
cameraError.value = true
},
)
Expand All @@ -111,18 +121,42 @@ internal fun CameraScreen(
.fillMaxHeight(),
verticalArrangement = Arrangement.SpaceBetween,
) {
Text(
style = MaterialTheme.typography.labelLarge,
Column(
modifier = Modifier
.padding(
vertical = 16.dp,
horizontal = 16.dp,
)
.semantics { contentDescription = "Permission explanation" },
text = stringResource(Res.string.permission_required_message),
)
.weight(1f)
.fillMaxWidth()
.padding(horizontal = 40.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Image(
painter = painterResource(Res.drawable.ic_camera),
contentDescription = "Camera icon",
modifier = Modifier.size(48.dp),
)
Spacer(modifier = Modifier.height(16.dp))
Text(
text = stringResource(Res.string.camera_access_required),
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.semantics {
contentDescription = "Permission title"
},
)
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(Res.string.permission_required_message),
style = MaterialTheme.typography.bodyMedium,
modifier = Modifier
.semantics {
contentDescription = "Permission explanation"
},
)
}
BottomButton(
modifier = Modifier.testTag("submitButton"),
modifier = Modifier
.padding(horizontal = 16.dp, vertical = 16.dp)
.testTag("submitButton"),
text = stringResource(Res.string.permission_camera_request),
onClick = { askForPermission.value = true },
)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,13 @@ package de.tillhub.scanengine.ui.components

import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Button
import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.semantics.contentDescription
import androidx.compose.ui.semantics.semantics
import androidx.compose.ui.unit.dp
Expand All @@ -28,14 +28,14 @@ internal fun BottomButton(
enabled = isEnable,
modifier = modifier
.fillMaxWidth(),
shape = RectangleShape,
shape = RoundedCornerShape(8.dp),
onClick = onClick,
colors = ButtonDefaults.buttonColors(
containerColor = if (isEnable) OrbitalBlue else MagneticGrey,
),
) {
Text(
style = MaterialTheme.typography.headlineSmall,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier
.padding(vertical = 8.dp)
.semantics { contentDescription = "Bottom button label" },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,9 @@

package de.tillhub.scanengine.ui.components

import androidx.compose.foundation.layout.Column
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Close
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
Expand All @@ -17,38 +15,38 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.testTag
import de.tillhub.scanengine.resources.Res
import de.tillhub.scanengine.resources.navigate_back
import org.jetbrains.compose.resources.stringResource
import org.jetbrains.compose.ui.tooling.preview.Preview

@Composable
internal fun Toolbar(
title: String,
onClick: () -> Unit,
) {
Column {
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors()
.copy(containerColor = Color.White),
title = {
Text(
title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.testTag("toolbarTitle"),
TopAppBar(
colors = TopAppBarDefaults.topAppBarColors()
.copy(containerColor = Color.White),
title = {
Text(
title,
style = MaterialTheme.typography.titleLarge,
modifier = Modifier.testTag("toolbarTitle"),
)
},
navigationIcon = {
IconButton(
onClick = onClick,
modifier = Modifier.testTag("toolbarIcon"),
) {
Icon(
imageVector = Icons.AutoMirrored.Filled.ArrowBack,
contentDescription = stringResource(Res.string.navigate_back),
)
},
navigationIcon = {
IconButton(
onClick = onClick,
modifier = Modifier.testTag("toolbarIcon"),
) {
Icon(
imageVector = Icons.Filled.Close,
contentDescription = "close",
)
}
},
)
HorizontalDivider()
}
}
},
)
}

@Preview
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class ToolbarTest {
}
}

onNodeWithContentDescription("close").assertExists()
onNodeWithContentDescription("Navigate back").assertExists()
onNodeWithTag("toolbarTitle").assertTextEquals(testTitle)

onNodeWithTag("toolbarIcon").performClick()
Expand Down
Loading