diff --git a/CHANGELOG.md b/CHANGELOG.md index a0c8579..663cb94 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 The changes documented here do not include those from the original repository. +# [2.0.1] + +### 2025-10-02 + +- Fix crash `kotlin.UninitializedPropertyAccessException: lateinit property camera has not been initialized` (https://outsystemsrd.atlassian.net/browse/RMET-4530) + ## [2.0.0] ### 2025-08-22 diff --git a/docs/README.md b/docs/README.md index 8eb894c..cc338eb 100644 --- a/docs/README.md +++ b/docs/README.md @@ -46,7 +46,7 @@ In your app-level gradle file, import the OSBarcodeLib library like so: ```gradle dependencies { - implementation("com.github.outsystems:osbarcode-android:2.0.0@aar") + implementation("com.github.outsystems:osbarcode-android:2.0.1@aar") } ``` diff --git a/pom.xml b/pom.xml index 0de09bf..6859f68 100644 --- a/pom.xml +++ b/pom.xml @@ -7,5 +7,5 @@ 4.0.0 io.ionic.libs ionbarcode-android - 2.0.0 + 2.0.1 diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt index ee5f2b4..6d48d90 100644 --- a/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt +++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerActivity.kt @@ -135,7 +135,7 @@ import kotlin.math.roundToInt * implements the ImageAnalysis.Analyzer interface. */ class OSBARCScannerActivity : ComponentActivity() { - private lateinit var camera: Camera + private var camera: Camera? = null private lateinit var selector: CameraSelector private var permissionRequestCount = 0 private var showDialog by mutableStateOf(false) @@ -232,6 +232,7 @@ class OSBARCScannerActivity : ComponentActivity() { val lifecycleOwner = LocalLifecycleOwner.current val context = LocalContext.current var permissionGiven by remember { mutableStateOf(true) } + var uiState by remember { mutableStateOf(OSBARCScannerUiState.DEFAULT) } // permissions val requestPermissionLauncher = rememberLauncherForActivityResult( @@ -316,7 +317,13 @@ class OSBARCScannerActivity : ComponentActivity() { selector, preview, imageAnalysis - ) + ).also { + uiState = OSBARCScannerUiState( + hasFlashUnit = it.cameraInfo.hasFlashUnit(), + minZoomRatio = it.cameraInfo.zoomState.value?.minZoomRatio ?: 1f, + maxZoomRatio = it.cameraInfo.zoomState.value?.maxZoomRatio ?: 1f + ) + } } catch (e: Exception) { e.message?.let { Log.e(LOG_TAG, it) } setResult(OSBARCError.SCANNING_GENERAL_ERROR.code) @@ -327,7 +334,7 @@ class OSBARCScannerActivity : ComponentActivity() { modifier = Modifier.fillMaxSize() ) - ScanScreenUI(parameters, windowSizeClass) + ScanScreenUI(parameters, windowSizeClass, uiState) } } @@ -337,9 +344,14 @@ class OSBARCScannerActivity : ComponentActivity() { * should be rendered: portrait or landscape * @param parameters the scan parameters * @param windowSizeClass WindowSizeClass object to determine device type - phone or tablet + * @param uiState structure containing the state of the screen */ @Composable - fun ScanScreenUI(parameters: OSBARCScanParameters, windowSizeClass: WindowSizeClass) { + fun ScanScreenUI( + parameters: OSBARCScanParameters, + windowSizeClass: WindowSizeClass, + uiState: OSBARCScannerUiState + ) { // actual UI on top of the camera stream val configuration = LocalConfiguration.current val windowMetrics = @@ -356,19 +368,19 @@ class OSBARCScannerActivity : ComponentActivity() { // determine if device is phone or tablet val isPhone = windowSizeClass.widthSizeClass == WindowWidthSizeClass.Compact if (isPhone) { - ScanScreenUIPortrait(parameters, screenWidth, ScannerBorderPadding, true) + ScanScreenUIPortrait(parameters, screenWidth, ScannerBorderPadding, true, uiState = uiState) } else { - ScanScreenUILandscape(parameters, (screenWidth / 2), ScannerBorderPadding, TextToRectPadding, isPhone = false, isPortrait = true) + ScanScreenUILandscape(parameters, (screenWidth / 2), ScannerBorderPadding, TextToRectPadding, isPhone = false, isPortrait = true, uiState = uiState) } } else { // determine if device is phone or tablet val isPhone = windowSizeClass.heightSizeClass == WindowHeightSizeClass.Compact if (isPhone) { - ScanScreenUILandscape(parameters, screenHeight, ScannerBorderPadding, TextToRectPadding, isPhone = true, isPortrait = false) + ScanScreenUILandscape(parameters, screenHeight, ScannerBorderPadding, TextToRectPadding, isPhone = true, isPortrait = false, uiState = uiState) } else { - ScanScreenUILandscape(parameters, screenHeight / 2, ScannerBorderPadding, TextToRectPadding, isPhone = false, isPortrait = false) + ScanScreenUILandscape(parameters, screenHeight / 2, ScannerBorderPadding, TextToRectPadding, isPhone = false, isPortrait = false, uiState = uiState) } } } @@ -498,12 +510,14 @@ class OSBARCScannerActivity : ComponentActivity() { * @param parameters the scan parameters * @param screenHeight the screen height * @param borderPadding the value for the border padding + * @param uiState structure containing the state of the screen */ @Composable fun ScanScreenUIPortrait(parameters: OSBARCScanParameters, screenHeight: Dp, borderPadding: Dp, - isPhone: Boolean) { + isPhone: Boolean, + uiState: OSBARCScannerUiState) { Column( modifier = Modifier .fillMaxSize(), @@ -554,7 +568,7 @@ class OSBARCScannerActivity : ComponentActivity() { .weight(1f, fill = true) .safeDrawingPadding(), ) { - val showTorch = camera.cameraInfo.hasFlashUnit() + val showTorch = uiState.hasFlashUnit val showScan = parameters.scanButton Column( @@ -564,7 +578,7 @@ class OSBARCScannerActivity : ComponentActivity() { horizontalAlignment = Alignment.CenterHorizontally ) { - ZoomButtons() + ZoomButtons(uiState) // scan button to turn on scanning when used if (showScan) { @@ -595,6 +609,7 @@ class OSBARCScannerActivity : ComponentActivity() { * @param parameters the scan parameters * @param screenHeight the screen height * @param borderPadding the value for the border padding + * @param uiState structure containing the state of the screen */ @Composable fun ScanScreenUILandscape(parameters: OSBARCScanParameters, @@ -602,7 +617,8 @@ class OSBARCScannerActivity : ComponentActivity() { borderPadding: Dp, textToRectPadding: Dp, isPhone: Boolean, - isPortrait: Boolean) { + isPortrait: Boolean, + uiState: OSBARCScannerUiState) { var rightButtonsWidth by remember { mutableStateOf(0.dp) } val density = LocalDensity.current @@ -678,7 +694,7 @@ class OSBARCScannerActivity : ComponentActivity() { verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.End ) { - val showTorch = camera.cameraInfo.hasFlashUnit() + val showTorch = uiState.hasFlashUnit val showScan = parameters.scanButton // flashlight button @@ -690,7 +706,7 @@ class OSBARCScannerActivity : ComponentActivity() { Spacer(modifier = Modifier.height(16.dp)) } - ZoomButtons() + ZoomButtons(uiState) // scan button to turn on scanning when used if (showScan) { @@ -742,7 +758,7 @@ class OSBARCScannerActivity : ComponentActivity() { modifier = modifier .clickable { try { - camera.cameraControl.enableTorch(!isFlashlightOn) + camera?.cameraControl?.enableTorch(!isFlashlightOn) isFlashlightOn = !isFlashlightOn } catch (e: Exception) { e.message?.let { Log.e(LOG_TAG, it) } @@ -807,12 +823,13 @@ class OSBARCScannerActivity : ComponentActivity() { /** * Composable function, responsible for building the zoom buttons on the UI. + * @param uiState structure containing the state of the screen */ @Composable - fun ZoomButtons() { - val minZoomRatio = camera.cameraInfo.zoomState.value?.minZoomRatio ?: 1f + fun ZoomButtons(uiState: OSBARCScannerUiState) { + val minZoomRatio = uiState.minZoomRatio val roundedRatio = (minZoomRatio * 10).roundToInt() / 10f - val maxZoomRatio = camera.cameraInfo.zoomState.value?.maxZoomRatio ?: 1f + val maxZoomRatio = uiState.maxZoomRatio var selectedButton by remember { mutableStateOf(2) } Row( @@ -832,7 +849,7 @@ class OSBARCScannerActivity : ComponentActivity() { "$roundedRatio${getZoomButtonSuffix(selectedButton, 1)}", onClick = { selectedButton = 1 - camera.cameraControl.setZoomRatio(minZoomRatio) + camera?.cameraControl?.setZoomRatio(minZoomRatio) } ) } @@ -851,7 +868,7 @@ class OSBARCScannerActivity : ComponentActivity() { "1${getZoomButtonSuffix(selectedButton, 2)}", onClick = { selectedButton = 2 - camera.cameraControl.setZoomRatio(1f) + camera?.cameraControl?.setZoomRatio(1f) } ) } @@ -866,7 +883,7 @@ class OSBARCScannerActivity : ComponentActivity() { "2${getZoomButtonSuffix(selectedButton, 3)}", onClick = { selectedButton = 3 - camera.cameraControl.setZoomRatio(2f) + camera?.cameraControl?.setZoomRatio(2f) } ) } diff --git a/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerUiState.kt b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerUiState.kt new file mode 100644 index 0000000..3d4b719 --- /dev/null +++ b/src/main/kotlin/com/outsystems/plugins/barcode/view/OSBARCScannerUiState.kt @@ -0,0 +1,15 @@ +package com.outsystems.plugins.barcode.view + +data class OSBARCScannerUiState( + val hasFlashUnit: Boolean, + val minZoomRatio: Float, + val maxZoomRatio: Float +) { + companion object { + val DEFAULT = OSBARCScannerUiState( + hasFlashUnit = false, + minZoomRatio = 1f, + maxZoomRatio = 1f + ) + } +}