Skip to content
Permalink

Comparing changes

Choose two branches to see what’s changed or to start a new pull request. If you need to, you can also or learn more about diff comparisons.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also . Learn more about diff comparisons here.
base repository: juliansteenbakker/mobile_scanner
Failed to load repositories. Confirm that selected base ref is valid, then try again.
Loading
base: v6.0.1
Choose a base ref
...
head repository: juliansteenbakker/mobile_scanner
Failed to load repositories. Confirm that selected head ref is valid, then try again.
Loading
compare: master
Choose a head ref
Loading
Showing with 831 additions and 181 deletions.
  1. +1 −1 .github/workflows/auto-assign-pr.yml
  2. +1 −1 .github/workflows/flutter.yml
  3. +1 −1 .github/workflows/release-please.yml
  4. +30 −1 CHANGELOG.md
  5. +4 −0 README.md
  6. +3 −2 android/build.gradle
  7. +85 −24 android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScanner.kt
  8. +1 −0 android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerExceptions.kt
  9. +13 −0 android/src/main/kotlin/dev/steenbakker/mobile_scanner/MobileScannerHandler.kt
  10. +62 −41 android/src/main/kotlin/dev/steenbakker/mobile_scanner/utils/YuvToRgbConverter.kt
  11. +4 −4 example/android/app/build.gradle
  12. +1 −1 example/android/gradle/wrapper/gradle-wrapper.properties
  13. +2 −2 example/android/settings.gradle
  14. +1 −1 example/ios/Podfile
  15. +2 −1 example/lib/barcode_scanner_controller.dart
  16. +1 −1 example/lib/barcode_scanner_listview.dart
  17. +1 −1 example/lib/barcode_scanner_pageview.dart
  18. +1 −1 example/lib/barcode_scanner_returning_image.dart
  19. +1 −1 example/lib/barcode_scanner_simple.dart
  20. +6 −6 example/lib/barcode_scanner_window.dart
  21. +1 −1 example/lib/barcode_scanner_zoom.dart
  22. +6 −0 example/lib/main.dart
  23. +4 −4 example/lib/mobile_scanner_overlay.dart
  24. +117 −0 example/lib/picklist/barcode_scanner_picklist.dart
  25. +87 −0 example/lib/picklist/classes/barcode_at_center.dart
  26. +36 −0 example/lib/picklist/classes/fix_coordinate_space.dart
  27. +58 −0 example/lib/picklist/picklist_result.dart
  28. +26 −0 example/lib/picklist/widgets/crosshair.dart
  29. +27 −0 example/lib/scanner_button_widgets.dart
  30. +0 −1 example/lib/scanner_error_widget.dart
  31. +43 −12 ios/Classes/MobileScanner.swift
  32. +1 −0 ios/Classes/MobileScannerError.swift
  33. +10 −0 ios/Classes/MobileScannerPlugin.swift
  34. +3 −2 ios/mobile_scanner.podspec
  35. +16 −1 lib/src/method_channel/mobile_scanner_method_channel.dart
  36. +46 −23 lib/src/mobile_scanner_controller.dart
  37. +5 −0 lib/src/mobile_scanner_platform_interface.dart
  38. +11 −0 lib/src/web/barcode_reader.dart
  39. +21 −0 lib/src/web/mobile_scanner_web.dart
  40. +13 −1 lib/src/web/zxing/zxing_barcode_reader.dart
  41. +1 −1 macos/mobile_scanner.podspec
  42. +77 −44 macos/mobile_scanner/Sources/mobile_scanner/MobileScannerPlugin.swift
  43. +1 −1 pubspec.yaml
2 changes: 1 addition & 1 deletion .github/workflows/auto-assign-pr.yml
Original file line number Diff line number Diff line change
@@ -12,4 +12,4 @@ jobs:
assign-author:
runs-on: ubuntu-latest
steps:
- uses: toshimaru/auto-author-assign@v2.1.0
- uses: toshimaru/auto-author-assign@v2.1.1
2 changes: 1 addition & 1 deletion .github/workflows/flutter.yml
Original file line number Diff line number Diff line change
@@ -36,7 +36,7 @@ jobs:
- uses: subosito/flutter-action@v2.12.0
with:
cache: true
flutter-version: '3.22'
flutter-version: '3.24'
channel: 'stable'
- name: Version
run: flutter doctor -v
2 changes: 1 addition & 1 deletion .github/workflows/release-please.yml
Original file line number Diff line number Diff line change
@@ -7,7 +7,7 @@
release-please:
runs-on: ubuntu-latest
steps:
- uses: GoogleCloudPlatform/release-please-action@v4.1.0
- uses: GoogleCloudPlatform/release-please-action@v4.1.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
release-type: simple
31 changes: 30 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
## 6.0.1
## 6.0.7
Improvements:
* [Android] Updated bundled barcode scanning library to v17.3.0

## 6.0.6
Bugs fixed:
* [web] Fixed a bug that prevented color inverted barcodes from being scanned.

Improvements:
* [web] Bump ZXingJS from version 0.19.1 to 0.21.3.

## 6.0.5
Bugs fixed:
* [Android] Fixed crash due to imageProxy being closed too early.

## 6.0.4
Bugs fixed:
* [Android] Fixed UI stutter when `returnImage` is true.

## 6.0.3
New features:
* Adds pause function to pause the camera but keep textures in place.

## 6.0.2
Bugs fixed:
* Fixed a bug that prevented `analyzeImage` from actually accepting the configured formats.

Improvements:
* [iOS] Excluded the `arm64` architecture for Simulators, which is unsupported by MLKit 7.0.0.

## 6.0.1
Bugs fixed:
* Fixed a bug that would cause onDetect to not handle errors.

4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -60,6 +60,10 @@ dev.steenbakker.mobile_scanner.useUnbundled=true
```

### iOS

_iOS arm64 Simulators are currently not yet supported, until the migration to the Vision API is complete._
_See_ https://github.com/juliansteenbakker/mobile_scanner/issues/1225

**Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:**
NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor.

5 changes: 3 additions & 2 deletions android/build.gradle
Original file line number Diff line number Diff line change
@@ -70,7 +70,7 @@ dependencies {
implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.1'
} else {
// Bundled model in app
implementation 'com.google.mlkit:barcode-scanning:17.2.0'
implementation 'com.google.mlkit:barcode-scanning:17.3.0'
}

// org.jetbrains.kotlin:kotlin-bom artifact purpose is to align kotlin stdlib and related code versions.
@@ -79,7 +79,8 @@ dependencies {

implementation 'androidx.camera:camera-lifecycle:1.3.4'
implementation 'androidx.camera:camera-camera2:1.3.4'
implementation 'org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3'

testImplementation 'org.jetbrains.kotlin:kotlin-test'
testImplementation 'org.mockito:mockito-core:5.12.0'
testImplementation 'org.mockito:mockito-core:5.12.0'
}
Original file line number Diff line number Diff line change
@@ -35,6 +35,10 @@ import dev.steenbakker.mobile_scanner.objects.DetectionSpeed
import dev.steenbakker.mobile_scanner.objects.MobileScannerStartParameters
import dev.steenbakker.mobile_scanner.utils.YuvToRgbConverter
import io.flutter.view.TextureRegistry
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.launch
import java.io.ByteArrayOutputStream
import kotlin.math.roundToInt

@@ -49,6 +53,7 @@ class MobileScanner(
/// Internal variables
private var cameraProvider: ProcessCameraProvider? = null
private var camera: Camera? = null
private var cameraSelector: CameraSelector? = null
private var preview: Preview? = null
private var textureEntry: TextureRegistry.SurfaceTextureEntry? = null
private var scanner: BarcodeScanner? = null
@@ -61,6 +66,7 @@ class MobileScanner(
private var detectionSpeed: DetectionSpeed = DetectionSpeed.NO_DUPLICATES
private var detectionTimeout: Long = 250
private var returnImage = false
private var isPaused = false

companion object {
/**
@@ -95,6 +101,7 @@ class MobileScanner(

if (newScannedBarcodes == lastScanned) {
// New scanned is duplicate, returning
imageProxy.close()
return@addOnSuccessListener
}
if (newScannedBarcodes.isNotEmpty()) {
@@ -116,6 +123,7 @@ class MobileScanner(
}

if (barcodeMap.isEmpty()) {
imageProxy.close()
return@addOnSuccessListener
}

@@ -125,34 +133,41 @@ class MobileScanner(
null,
mediaImage.width,
mediaImage.height)
imageProxy.close()
return@addOnSuccessListener
}

val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888)
val imageFormat = YuvToRgbConverter(activity.applicationContext)
CoroutineScope(Dispatchers.IO).launch {
val bitmap = Bitmap.createBitmap(mediaImage.width, mediaImage.height, Bitmap.Config.ARGB_8888)
val imageFormat = YuvToRgbConverter(activity.applicationContext)

imageFormat.yuvToRgb(mediaImage, bitmap)
imageFormat.yuvToRgb(mediaImage, bitmap)

val bmResult = rotateBitmap(bitmap, camera?.cameraInfo?.sensorRotationDegrees?.toFloat() ?: 90f)
val bmResult = rotateBitmap(bitmap, camera?.cameraInfo?.sensorRotationDegrees?.toFloat() ?: 90f)

val stream = ByteArrayOutputStream()
bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray = stream.toByteArray()
val bmWidth = bmResult.width
val bmHeight = bmResult.height
bmResult.recycle()
val stream = ByteArrayOutputStream()
bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream)
val byteArray = stream.toByteArray()
val bmWidth = bmResult.width
val bmHeight = bmResult.height

mobileScannerCallback(
barcodeMap,
byteArray,
bmWidth,
bmHeight
)

bmResult.recycle()
imageProxy.close()
imageFormat.release()
}

mobileScannerCallback(
barcodeMap,
byteArray,
bmWidth,
bmHeight
)
}.addOnFailureListener { e ->
mobileScannerErrorCallback(
e.localizedMessage ?: e.toString()
)
}.addOnCompleteListener { imageProxy.close() }
}
}

if (detectionSpeed == DetectionSpeed.NORMAL) {
@@ -250,7 +265,22 @@ class MobileScanner(
this.detectionTimeout = detectionTimeout
this.returnImage = returnImage

if (camera?.cameraInfo != null && preview != null && textureEntry != null) {
if (camera?.cameraInfo != null && preview != null && textureEntry != null && !isPaused) {

// TODO: resume here for seamless transition
// if (isPaused) {
// resumeCamera()
// mobileScannerStartedCallback(
// MobileScannerStartParameters(
// if (portrait) width else height,
// if (portrait) height else width,
// currentTorchState,
// textureEntry!!.id(),
// numberOfCameras ?: 0
// )
// )
// return
// }
mobileScannerErrorCallback(AlreadyStarted())

return
@@ -273,7 +303,7 @@ class MobileScanner(
}

cameraProvider?.unbindAll()
textureEntry = textureRegistry.createSurfaceTexture()
textureEntry = textureEntry ?: textureRegistry.createSurfaceTexture()

// Preview
val surfaceProvider = Preview.SurfaceProvider { request ->
@@ -353,6 +383,7 @@ class MobileScanner(
preview,
analysis
)
cameraSelector = cameraPosition
} catch(exception: Exception) {
mobileScannerErrorCallback(NoCamera())

@@ -405,14 +436,47 @@ class MobileScanner(
}, executor)

}

/**
* Pause barcode scanning.
*/
fun pause() {
if (isPaused) {
throw AlreadyPaused()
} else if (isStopped()) {
throw AlreadyStopped()
}

pauseCamera()
}

/**
* Stop barcode scanning.
*/
fun stop() {
if (isStopped()) {
if (!isPaused && isStopped()) {
throw AlreadyStopped()
}

releaseCamera()
}

private fun pauseCamera() {
// Pause camera by unbinding all use cases
cameraProvider?.unbindAll()
isPaused = true
}

private fun resumeCamera() {
// Resume camera by rebinding use cases
cameraProvider?.let { provider ->
val owner = activity as LifecycleOwner
cameraSelector?.let { provider.bindToLifecycle(owner, it, preview) }
}
isPaused = false
}

private fun releaseCamera() {
if (displayListener != null) {
val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager

@@ -427,14 +491,11 @@ class MobileScanner(
it.zoomState.removeObservers(owner)
it.cameraState.removeObservers(owner)
}

// Unbind the camera use cases, the preview is a use case.
// The camera will be closed when the last use case is unbound.
cameraProvider?.unbindAll()
cameraProvider = null
camera = null
preview = null

// Release the texture for the preview.
textureEntry?.release()
textureEntry = null

Original file line number Diff line number Diff line change
@@ -3,6 +3,7 @@ package dev.steenbakker.mobile_scanner
class NoCamera : Exception()
class AlreadyStarted : Exception()
class AlreadyStopped : Exception()
class AlreadyPaused : Exception()
class CameraError : Exception()
class ZoomWhenStopped : Exception()
class ZoomNotInRange : Exception()
Original file line number Diff line number Diff line change
@@ -118,6 +118,7 @@ class MobileScannerHandler(
}
})
"start" -> start(call, result)
"pause" -> pause(result)
"stop" -> stop(result)
"toggleTorch" -> toggleTorch(result)
"analyzeImage" -> analyzeImage(call, result)
@@ -213,6 +214,18 @@ class MobileScannerHandler(
)
}

private fun pause(result: MethodChannel.Result) {
try {
mobileScanner!!.pause()
result.success(null)
} catch (e: Exception) {
when (e) {
is AlreadyPaused, is AlreadyStopped -> result.success(null)
else -> throw e
}
}
}

private fun stop(result: MethodChannel.Result) {
try {
mobileScanner!!.stop()
Loading