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
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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")
}
```

Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
<modelVersion>4.0.0</modelVersion>
<groupId>io.ionic.libs</groupId>
<artifactId>ionbarcode-android</artifactId>
<version>2.0.0</version>
<version>2.0.1</version>
</project>
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -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)
Expand All @@ -327,7 +334,7 @@ class OSBARCScannerActivity : ComponentActivity() {
modifier = Modifier.fillMaxSize()
)

ScanScreenUI(parameters, windowSizeClass)
ScanScreenUI(parameters, windowSizeClass, uiState)

}
}
Expand All @@ -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 =
Expand All @@ -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)
}
}
}
Expand Down Expand Up @@ -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(),
Expand Down Expand Up @@ -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(
Expand All @@ -564,7 +578,7 @@ class OSBARCScannerActivity : ComponentActivity() {
horizontalAlignment = Alignment.CenterHorizontally
) {

ZoomButtons()
ZoomButtons(uiState)

// scan button to turn on scanning when used
if (showScan) {
Expand Down Expand Up @@ -595,14 +609,16 @@ 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,
screenHeight:Dp,
borderPadding: Dp,
textToRectPadding: Dp,
isPhone: Boolean,
isPortrait: Boolean) {
isPortrait: Boolean,
uiState: OSBARCScannerUiState) {
var rightButtonsWidth by remember { mutableStateOf(0.dp) }
val density = LocalDensity.current

Expand Down Expand Up @@ -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
Expand All @@ -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) {
Expand Down Expand Up @@ -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) }
Expand Down Expand Up @@ -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(
Expand All @@ -832,7 +849,7 @@ class OSBARCScannerActivity : ComponentActivity() {
"$roundedRatio${getZoomButtonSuffix(selectedButton, 1)}",
onClick = {
selectedButton = 1
camera.cameraControl.setZoomRatio(minZoomRatio)
camera?.cameraControl?.setZoomRatio(minZoomRatio)
}
)
}
Expand All @@ -851,7 +868,7 @@ class OSBARCScannerActivity : ComponentActivity() {
"1${getZoomButtonSuffix(selectedButton, 2)}",
onClick = {
selectedButton = 2
camera.cameraControl.setZoomRatio(1f)
camera?.cameraControl?.setZoomRatio(1f)
}
)
}
Expand All @@ -866,7 +883,7 @@ class OSBARCScannerActivity : ComponentActivity() {
"2${getZoomButtonSuffix(selectedButton, 3)}",
onClick = {
selectedButton = 3
camera.cameraControl.setZoomRatio(2f)
camera?.cameraControl?.setZoomRatio(2f)
}
)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -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
)
}
}
Loading