Showing
100 changed files
with
1926 additions
and
1056 deletions
Too many changes to show.
To preserve performance only 100 of 100+ files are displayed.
| @@ -11,12 +11,12 @@ jobs: | @@ -11,12 +11,12 @@ jobs: | ||
| 11 | analysis: | 11 | analysis: |
| 12 | runs-on: ubuntu-latest | 12 | runs-on: ubuntu-latest |
| 13 | steps: | 13 | steps: |
| 14 | - - uses: actions/checkout@v4.0.0 | ||
| 15 | - - uses: actions/setup-java@v3.12.0 | 14 | + - uses: actions/checkout@v4.1.1 |
| 15 | + - uses: actions/setup-java@v3.13.0 | ||
| 16 | with: | 16 | with: |
| 17 | - java-version: 11 | 17 | + java-version: 17 |
| 18 | distribution: temurin | 18 | distribution: temurin |
| 19 | - - uses: subosito/flutter-action@v2.10.0 | 19 | + - uses: subosito/flutter-action@v2.12.0 |
| 20 | with: | 20 | with: |
| 21 | cache: true | 21 | cache: true |
| 22 | - name: Version | 22 | - name: Version |
| @@ -28,12 +28,12 @@ jobs: | @@ -28,12 +28,12 @@ jobs: | ||
| 28 | formatting: | 28 | formatting: |
| 29 | runs-on: ubuntu-latest | 29 | runs-on: ubuntu-latest |
| 30 | steps: | 30 | steps: |
| 31 | - - uses: actions/checkout@v4.0.0 | ||
| 32 | - - uses: actions/setup-java@v3.12.0 | 31 | + - uses: actions/checkout@v4.1.1 |
| 32 | + - uses: actions/setup-java@v3.13.0 | ||
| 33 | with: | 33 | with: |
| 34 | - java-version: 11 | 34 | + java-version: 17 |
| 35 | distribution: temurin | 35 | distribution: temurin |
| 36 | - - uses: subosito/flutter-action@v2.10.0 | 36 | + - uses: subosito/flutter-action@v2.12.0 |
| 37 | with: | 37 | with: |
| 38 | cache: true | 38 | cache: true |
| 39 | - name: Format | 39 | - name: Format |
| @@ -7,7 +7,7 @@ | @@ -7,7 +7,7 @@ | ||
| 7 | release-please: | 7 | release-please: |
| 8 | runs-on: ubuntu-latest | 8 | runs-on: ubuntu-latest |
| 9 | steps: | 9 | steps: |
| 10 | - - uses: GoogleCloudPlatform/release-please-action@v3.7.11 | 10 | + - uses: GoogleCloudPlatform/release-please-action@v3.7.13 |
| 11 | with: | 11 | with: |
| 12 | token: ${{ secrets.GITHUB_TOKEN }} | 12 | token: ${{ secrets.GITHUB_TOKEN }} |
| 13 | release-type: simple | 13 | release-type: simple |
| 1 | +## 3.5.5 | ||
| 2 | +Bugs fixed: | ||
| 3 | +* Fixed a bug where the scanner would get stuck after denying permissions on Android. (thanks @navaronbracke !) | ||
| 4 | + | ||
| 5 | +## 3.5.4 | ||
| 6 | +Bugs fixed: | ||
| 7 | +* Fixed a bug with an implicit conversion to integer for the scan timeout for iOS. (thanks @EArminjon !) | ||
| 8 | + | ||
| 9 | +## 3.5.2 | ||
| 10 | +Improvements: | ||
| 11 | +* Updated to `play-services-mlkit-barcode-scanning` version 18.3.0 | ||
| 12 | + | ||
| 13 | +Bugs fixed: | ||
| 14 | +* Fixed the `updateScanWindow()` function not completing on Android and MacOS. (thanks @navaronbracke !) | ||
| 15 | +* Fixed some camera access issues, when the camera could have been null on Android. (thanks @navaronbracke !) | ||
| 16 | +* Fixed a crash on Android when there is no camera. (thanks @navaronbracke !) | ||
| 17 | +* Fixed a bug with the `noDuplicates` detection speed. (thanks @pgeof !) | ||
| 18 | +* Fixed a synchronization issue for the torch state. (thanks @navaronbracke !) | ||
| 19 | + | ||
| 20 | +## 3.5.1 | ||
| 21 | +Improvements: | ||
| 22 | +* The `type` of an `Address` is now non-null. | ||
| 23 | +* The `type` of an `Email` is now non-null. | ||
| 24 | +* The `phoneNumber` of an `SMS` is now non-null. | ||
| 25 | +* The `latitude` and `longitude` of a `GeoPoint` are now non-null. | ||
| 26 | +* The `phones` and `urls` of `ContactInfo` are now non-null. | ||
| 27 | +* The `url` of a `UrlBookmark` is now non-null. | ||
| 28 | +* The `type` of `Phone` is now non-null. | ||
| 29 | +* The `width` and `height` of `BarcodeCapture` are now non-null. | ||
| 30 | +* The `BarcodeCapture` class now exposes a `size`. | ||
| 31 | +* The list of `corners` of a `Barcode` is now non-null. | ||
| 32 | + | ||
| 33 | +Bugs fixed: | ||
| 34 | +* Fixed the default values for the `format` and `type` arguments of the Barcode constructor. | ||
| 35 | + These now use `BarcodeFormat.unknown` and `BarcodeType.unknown`, rather than `BarcodeFormat.ean13` and `BarcodeType.text`. | ||
| 36 | + (thanks @navaronbracke !) | ||
| 37 | +* Fixed messages not being sent on the main thread for Android, iOS and MacOS. (thanks @navaronbracke !) | ||
| 38 | + | ||
| 39 | +## 3.5.0 | ||
| 40 | +New Features: | ||
| 41 | +* Added the option to switch between bundled and unbundled MLKit for Android. (thanks @woolfred !) | ||
| 42 | +* Added the option to specify the camera resolution for Android. (thanks @EArminjon !) | ||
| 43 | +* Added a sample with a scanner overlay. (thanks @Spyy004 !) | ||
| 44 | + | ||
| 45 | +Bugs fixed: | ||
| 46 | +* Fixed the scan window calculation taking into account the widget coordinates, instead of the screen coordinates. (thanks @jlin5 !) | ||
| 47 | +* Fixed the scan window calculation returning wrong results. (thanks @MBulli !) | ||
| 48 | +* Fixed the BarcodeCapture format on MacOS. (thanks @ryanduffyne !) | ||
| 49 | +* Fixed the timeout for scanning on MacOS. (thanks @ryanduffyne !) | ||
| 50 | +* Fixed Android builds failing by downgrading from Kotlin 1.9.10 to 1.7.22. (thanks @vbuberen !) | ||
| 51 | +* Fixed images on iOS being rotated, resulting in bad detection rates. (thanks @EArminjon !) | ||
| 52 | +* Fixed scan timeout not working on iOS. (thanks @navaronbracke !) | ||
| 53 | +* Fixed a crash on iOS when the device is nil. (thanks @navaronbracke !) | ||
| 54 | +* Fixed a case of an unhandled exception when starting the scanner. (thanks @navaronbracke !) | ||
| 55 | + | ||
| 56 | +Improvements: | ||
| 57 | +* Improved MacOS memory footprint by using a background queue. (thanks @ryanduffyne !) | ||
| 58 | + | ||
| 1 | ## 3.4.1 | 59 | ## 3.4.1 |
| 2 | -Change MediaQuery.sizeOf(context) to of(context).size for backwards compatibility | 60 | +* Changed MediaQuery.sizeOf(context) to of(context).size for compatibility with older Flutter versions. |
| 3 | 61 | ||
| 4 | ## 3.4.0 | 62 | ## 3.4.0 |
| 5 | New Features: | 63 | New Features: |
| @@ -27,11 +27,17 @@ See the example app for detailed implementation information. | @@ -27,11 +27,17 @@ See the example app for detailed implementation information. | ||
| 27 | 27 | ||
| 28 | ## Platform specific setup | 28 | ## Platform specific setup |
| 29 | ### Android | 29 | ### Android |
| 30 | -This packages uses the **bundled version** of MLKit Barcode-scanning for Android. This version is more accurate and immediately available to devices. However, this version will increase the size of the app with approximately 3 to 10 MB. The alternative for this is to use the **unbundled version** of MLKit Barcode-scanning for Android. This version is older than the bundled version however this only increases the size by around 600KB. | ||
| 31 | -To use this version you must alter the mobile_scanner gradle file to replace `com.google.mlkit:barcode-scanning:17.0.2` with `com.google.android.gms:play-services-mlkit-barcode-scanning:18.0.0`. Keep in mind that if you alter the gradle files directly in your project it can be overriden when you update your pubspec.yaml. I am still searching for a way to properly replace the module in gradle but have yet to find one. | 30 | +This package uses by default the **bundled version** of MLKit Barcode-scanning for Android. This version is immediately available to the device. But it will increase the size of the app by approximately 3 to 10 MB. |
| 31 | + | ||
| 32 | +The alternative is to use the **unbundled version** of MLKit Barcode-scanning for Android. This version is downloaded on first use via Google Play Services. It increases the app size by around 600KB. | ||
| 32 | 33 | ||
| 33 | [You can read more about the difference between the two versions here.](https://developers.google.com/ml-kit/vision/barcode-scanning/android) | 34 | [You can read more about the difference between the two versions here.](https://developers.google.com/ml-kit/vision/barcode-scanning/android) |
| 34 | 35 | ||
| 36 | +To use the **unbundled version** of the MLKit Barcode-scanning, add the following line to your `/android/gradle.properties` file: | ||
| 37 | +``` | ||
| 38 | +dev.steenbakker.mobile_scanner.useUnbundled=true | ||
| 39 | +``` | ||
| 40 | + | ||
| 35 | ### iOS | 41 | ### iOS |
| 36 | **Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:** | 42 | **Add the following keys to your Info.plist file, located in <project root>/ios/Runner/Info.plist:** |
| 37 | NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor. | 43 | NSCameraUsageDescription - describe why your app needs access to the camera. This is called Privacy - Camera Usage Description in the visual editor. |
| @@ -2,19 +2,19 @@ group 'dev.steenbakker.mobile_scanner' | @@ -2,19 +2,19 @@ group 'dev.steenbakker.mobile_scanner' | ||
| 2 | version '1.0-SNAPSHOT' | 2 | version '1.0-SNAPSHOT' |
| 3 | 3 | ||
| 4 | buildscript { | 4 | buildscript { |
| 5 | - ext.kotlin_version = '1.9.10' | 5 | + ext.kotlin_version = '1.7.22' |
| 6 | repositories { | 6 | repositories { |
| 7 | google() | 7 | google() |
| 8 | mavenCentral() | 8 | mavenCentral() |
| 9 | } | 9 | } |
| 10 | 10 | ||
| 11 | dependencies { | 11 | dependencies { |
| 12 | - classpath 'com.android.tools.build:gradle:8.1.1' | 12 | + classpath 'com.android.tools.build:gradle:8.2.0' |
| 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | 13 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" |
| 14 | } | 14 | } |
| 15 | } | 15 | } |
| 16 | 16 | ||
| 17 | -rootProject.allprojects { | 17 | +allprojects { |
| 18 | repositories { | 18 | repositories { |
| 19 | google() | 19 | google() |
| 20 | mavenCentral() | 20 | mavenCentral() |
| @@ -25,37 +25,60 @@ apply plugin: 'com.android.library' | @@ -25,37 +25,60 @@ apply plugin: 'com.android.library' | ||
| 25 | apply plugin: 'kotlin-android' | 25 | apply plugin: 'kotlin-android' |
| 26 | 26 | ||
| 27 | android { | 27 | android { |
| 28 | - compileSdkVersion 33 | 28 | + if (project.android.hasProperty("namespace")) { |
| 29 | + namespace 'dev.steenbakker.mobile_scanner' | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + compileSdk 34 | ||
| 29 | 33 | ||
| 30 | compileOptions { | 34 | compileOptions { |
| 31 | - sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 32 | - targetCompatibility JavaVersion.VERSION_1_8 | 35 | + sourceCompatibility JavaVersion.VERSION_17 |
| 36 | + targetCompatibility JavaVersion.VERSION_17 | ||
| 33 | } | 37 | } |
| 34 | 38 | ||
| 35 | kotlinOptions { | 39 | kotlinOptions { |
| 36 | - jvmTarget = '1.8' | 40 | + jvmTarget = '17' |
| 37 | } | 41 | } |
| 38 | 42 | ||
| 39 | sourceSets { | 43 | sourceSets { |
| 40 | main.java.srcDirs += 'src/main/kotlin' | 44 | main.java.srcDirs += 'src/main/kotlin' |
| 45 | + test.java.srcDirs += 'src/test/kotlin' | ||
| 41 | } | 46 | } |
| 42 | 47 | ||
| 43 | defaultConfig { | 48 | defaultConfig { |
| 44 | minSdkVersion 21 | 49 | minSdkVersion 21 |
| 50 | + consumerProguardFiles 'proguard-rules.pro' | ||
| 45 | } | 51 | } |
| 46 | 52 | ||
| 47 | - namespace 'dev.steenbakker.mobile_scanner' | 53 | + testOptions { |
| 54 | + unitTests.all { | ||
| 55 | + useJUnitPlatform() | ||
| 56 | + | ||
| 57 | + testLogging { | ||
| 58 | + events "passed", "skipped", "failed", "standardOut", "standardError" | ||
| 59 | + outputs.upToDateWhen {false} | ||
| 60 | + showStandardStreams = true | ||
| 61 | + } | ||
| 62 | + } | ||
| 63 | + } | ||
| 48 | } | 64 | } |
| 49 | 65 | ||
| 50 | dependencies { | 66 | dependencies { |
| 51 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" | 67 | implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" |
| 52 | 68 | ||
| 53 | - // Use this dependency to bundle the model with your app | 69 | + def useUnbundled = project.findProperty('dev.steenbakker.mobile_scanner.useUnbundled') ?: false |
| 70 | + if (useUnbundled.toBoolean()) { | ||
| 71 | + // Dynamically downloaded model via Google Play Services | ||
| 72 | + implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.3.0' | ||
| 73 | + } else { | ||
| 74 | + // Bundled model in app | ||
| 54 | implementation 'com.google.mlkit:barcode-scanning:17.2.0' | 75 | implementation 'com.google.mlkit:barcode-scanning:17.2.0' |
| 55 | - // Use this dependency to use the dynamically downloaded model in Google Play Services | ||
| 56 | -// implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0' | 76 | + } |
| 77 | + | ||
| 78 | + implementation 'androidx.camera:camera-camera2:1.3.0' | ||
| 79 | + implementation 'androidx.camera:camera-lifecycle:1.3.0' | ||
| 80 | + implementation 'androidx.camera:camera-camera2:1.3.0' | ||
| 57 | 81 | ||
| 58 | - implementation 'androidx.camera:camera-camera2:1.2.3' | ||
| 59 | - implementation 'androidx.camera:camera-lifecycle:1.2.3' | ||
| 60 | - implementation 'androidx.camera:camera-camera2:1.2.3' | 82 | + testImplementation 'org.jetbrains.kotlin:kotlin-test' |
| 83 | + testImplementation 'org.mockito:mockito-core:5.8.0' | ||
| 61 | } | 84 | } |
android/proguard-rules.pro
0 → 100644
| @@ -2,4 +2,5 @@ | @@ -2,4 +2,5 @@ | ||
| 2 | package="dev.steenbakker.mobile_scanner"> | 2 | package="dev.steenbakker.mobile_scanner"> |
| 3 | 3 | ||
| 4 | <uses-permission android:name="android.permission.CAMERA" /> | 4 | <uses-permission android:name="android.permission.CAMERA" /> |
| 5 | + <uses-feature android:name="android.hardware.camera" android:required="false" /> | ||
| 5 | </manifest> | 6 | </manifest> |
| 1 | package dev.steenbakker.mobile_scanner | 1 | package dev.steenbakker.mobile_scanner |
| 2 | 2 | ||
| 3 | import android.app.Activity | 3 | import android.app.Activity |
| 4 | +import android.content.Context | ||
| 4 | import android.graphics.Bitmap | 5 | import android.graphics.Bitmap |
| 5 | import android.graphics.Matrix | 6 | import android.graphics.Matrix |
| 6 | import android.graphics.Rect | 7 | import android.graphics.Rect |
| 8 | +import android.hardware.display.DisplayManager | ||
| 7 | import android.net.Uri | 9 | import android.net.Uri |
| 10 | +import android.os.Build | ||
| 8 | import android.os.Handler | 11 | import android.os.Handler |
| 9 | import android.os.Looper | 12 | import android.os.Looper |
| 13 | +import android.util.Size | ||
| 10 | import android.view.Surface | 14 | import android.view.Surface |
| 11 | -import androidx.camera.core.* | 15 | +import android.view.WindowManager |
| 16 | +import androidx.camera.core.Camera | ||
| 17 | +import androidx.camera.core.CameraSelector | ||
| 18 | +import androidx.camera.core.ExperimentalGetImage | ||
| 19 | +import androidx.camera.core.ImageAnalysis | ||
| 20 | +import androidx.camera.core.ImageProxy | ||
| 21 | +import androidx.camera.core.Preview | ||
| 22 | +import androidx.camera.core.resolutionselector.AspectRatioStrategy | ||
| 23 | +import androidx.camera.core.resolutionselector.ResolutionSelector | ||
| 24 | +import androidx.camera.core.resolutionselector.ResolutionStrategy | ||
| 12 | import androidx.camera.lifecycle.ProcessCameraProvider | 25 | import androidx.camera.lifecycle.ProcessCameraProvider |
| 13 | import androidx.core.content.ContextCompat | 26 | import androidx.core.content.ContextCompat |
| 14 | import androidx.lifecycle.LifecycleOwner | 27 | import androidx.lifecycle.LifecycleOwner |
| @@ -39,6 +52,7 @@ class MobileScanner( | @@ -39,6 +52,7 @@ class MobileScanner( | ||
| 39 | private var scanner = BarcodeScanning.getClient() | 52 | private var scanner = BarcodeScanning.getClient() |
| 40 | private var lastScanned: List<String?>? = null | 53 | private var lastScanned: List<String?>? = null |
| 41 | private var scannerTimeout = false | 54 | private var scannerTimeout = false |
| 55 | + private var displayListener: DisplayManager.DisplayListener? = null | ||
| 42 | 56 | ||
| 43 | /// Configurable variables | 57 | /// Configurable variables |
| 44 | var scanWindow: List<Float>? = null | 58 | var scanWindow: List<Float>? = null |
| @@ -64,7 +78,7 @@ class MobileScanner( | @@ -64,7 +78,7 @@ class MobileScanner( | ||
| 64 | scanner.process(inputImage) | 78 | scanner.process(inputImage) |
| 65 | .addOnSuccessListener { barcodes -> | 79 | .addOnSuccessListener { barcodes -> |
| 66 | if (detectionSpeed == DetectionSpeed.NO_DUPLICATES) { | 80 | if (detectionSpeed == DetectionSpeed.NO_DUPLICATES) { |
| 67 | - val newScannedBarcodes = barcodes.map { barcode -> barcode.rawValue } | 81 | + val newScannedBarcodes = barcodes.mapNotNull({ barcode -> barcode.rawValue }).sorted() |
| 68 | if (newScannedBarcodes == lastScanned) { | 82 | if (newScannedBarcodes == lastScanned) { |
| 69 | // New scanned is duplicate, returning | 83 | // New scanned is duplicate, returning |
| 70 | return@addOnSuccessListener | 84 | return@addOnSuccessListener |
| @@ -102,14 +116,16 @@ class MobileScanner( | @@ -102,14 +116,16 @@ class MobileScanner( | ||
| 102 | val stream = ByteArrayOutputStream() | 116 | val stream = ByteArrayOutputStream() |
| 103 | bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream) | 117 | bmResult.compress(Bitmap.CompressFormat.PNG, 100, stream) |
| 104 | val byteArray = stream.toByteArray() | 118 | val byteArray = stream.toByteArray() |
| 119 | + val bmWidth = bmResult.width | ||
| 120 | + val bmHeight = bmResult.height | ||
| 105 | bmResult.recycle() | 121 | bmResult.recycle() |
| 106 | 122 | ||
| 107 | 123 | ||
| 108 | mobileScannerCallback( | 124 | mobileScannerCallback( |
| 109 | barcodeMap, | 125 | barcodeMap, |
| 110 | byteArray, | 126 | byteArray, |
| 111 | - bmResult.width, | ||
| 112 | - bmResult.height | 127 | + bmWidth, |
| 128 | + bmHeight | ||
| 113 | ) | 129 | ) |
| 114 | 130 | ||
| 115 | } else { | 131 | } else { |
| @@ -138,7 +154,7 @@ class MobileScanner( | @@ -138,7 +154,7 @@ class MobileScanner( | ||
| 138 | } | 154 | } |
| 139 | } | 155 | } |
| 140 | 156 | ||
| 141 | - fun rotateBitmap(bitmap: Bitmap, degrees: Float): Bitmap { | 157 | + private fun rotateBitmap(bitmap: Bitmap, degrees: Float): Bitmap { |
| 142 | val matrix = Matrix() | 158 | val matrix = Matrix() |
| 143 | matrix.postRotate(degrees) | 159 | matrix.postRotate(degrees) |
| 144 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) | 160 | return Bitmap.createBitmap(bitmap, 0, 0, bitmap.width, bitmap.height, matrix, true) |
| @@ -166,6 +182,34 @@ class MobileScanner( | @@ -166,6 +182,34 @@ class MobileScanner( | ||
| 166 | return scaledScanWindow.contains(barcodeBoundingBox) | 182 | return scaledScanWindow.contains(barcodeBoundingBox) |
| 167 | } | 183 | } |
| 168 | 184 | ||
| 185 | + // Return the best resolution for the actual device orientation. | ||
| 186 | + // | ||
| 187 | + // By default the resolution is 480x640, which is too low for ML Kit. | ||
| 188 | + // If the given resolution is not supported by the display, | ||
| 189 | + // the closest available resolution is used. | ||
| 190 | + // | ||
| 191 | + // The resolution should be adjusted for the display rotation, to preserve the aspect ratio. | ||
| 192 | + @Suppress("deprecation") | ||
| 193 | + private fun getResolution(cameraResolution: Size): Size { | ||
| 194 | + val rotation = if (Build.VERSION.SDK_INT >= 30) { | ||
| 195 | + activity.display!!.rotation | ||
| 196 | + } else { | ||
| 197 | + val windowManager = activity.applicationContext.getSystemService(Context.WINDOW_SERVICE) as WindowManager | ||
| 198 | + | ||
| 199 | + windowManager.defaultDisplay.rotation | ||
| 200 | + } | ||
| 201 | + | ||
| 202 | + val widthMaxRes = cameraResolution.width | ||
| 203 | + val heightMaxRes = cameraResolution.height | ||
| 204 | + | ||
| 205 | + val targetResolution = if (rotation == Surface.ROTATION_0 || rotation == Surface.ROTATION_180) { | ||
| 206 | + Size(widthMaxRes, heightMaxRes) // Portrait mode | ||
| 207 | + } else { | ||
| 208 | + Size(heightMaxRes, widthMaxRes) // Landscape mode | ||
| 209 | + } | ||
| 210 | + return targetResolution | ||
| 211 | + } | ||
| 212 | + | ||
| 169 | /** | 213 | /** |
| 170 | * Start barcode scanning by initializing the camera and barcode scanner. | 214 | * Start barcode scanning by initializing the camera and barcode scanner. |
| 171 | */ | 215 | */ |
| @@ -179,16 +223,22 @@ class MobileScanner( | @@ -179,16 +223,22 @@ class MobileScanner( | ||
| 179 | torchStateCallback: TorchStateCallback, | 223 | torchStateCallback: TorchStateCallback, |
| 180 | zoomScaleStateCallback: ZoomScaleStateCallback, | 224 | zoomScaleStateCallback: ZoomScaleStateCallback, |
| 181 | mobileScannerStartedCallback: MobileScannerStartedCallback, | 225 | mobileScannerStartedCallback: MobileScannerStartedCallback, |
| 182 | - detectionTimeout: Long | 226 | + mobileScannerErrorCallback: (exception: Exception) -> Unit, |
| 227 | + detectionTimeout: Long, | ||
| 228 | + cameraResolution: Size?, | ||
| 229 | + newCameraResolutionSelector: Boolean | ||
| 183 | ) { | 230 | ) { |
| 184 | this.detectionSpeed = detectionSpeed | 231 | this.detectionSpeed = detectionSpeed |
| 185 | this.detectionTimeout = detectionTimeout | 232 | this.detectionTimeout = detectionTimeout |
| 186 | this.returnImage = returnImage | 233 | this.returnImage = returnImage |
| 187 | 234 | ||
| 188 | if (camera?.cameraInfo != null && preview != null && textureEntry != null) { | 235 | if (camera?.cameraInfo != null && preview != null && textureEntry != null) { |
| 189 | - throw AlreadyStarted() | 236 | + mobileScannerErrorCallback(AlreadyStarted()) |
| 237 | + | ||
| 238 | + return | ||
| 190 | } | 239 | } |
| 191 | 240 | ||
| 241 | + lastScanned = null | ||
| 192 | scanner = if (barcodeScannerOptions != null) { | 242 | scanner = if (barcodeScannerOptions != null) { |
| 193 | BarcodeScanning.getClient(barcodeScannerOptions) | 243 | BarcodeScanning.getClient(barcodeScannerOptions) |
| 194 | } else { | 244 | } else { |
| @@ -200,10 +250,15 @@ class MobileScanner( | @@ -200,10 +250,15 @@ class MobileScanner( | ||
| 200 | 250 | ||
| 201 | cameraProviderFuture.addListener({ | 251 | cameraProviderFuture.addListener({ |
| 202 | cameraProvider = cameraProviderFuture.get() | 252 | cameraProvider = cameraProviderFuture.get() |
| 253 | + val nrOfCameras = cameraProvider?.availableCameraInfos?.size | ||
| 254 | + | ||
| 203 | if (cameraProvider == null) { | 255 | if (cameraProvider == null) { |
| 204 | - throw CameraError() | 256 | + mobileScannerErrorCallback(CameraError()) |
| 257 | + | ||
| 258 | + return@addListener | ||
| 205 | } | 259 | } |
| 206 | - cameraProvider!!.unbindAll() | 260 | + |
| 261 | + cameraProvider?.unbindAll() | ||
| 207 | textureEntry = textureRegistry.createSurfaceTexture() | 262 | textureEntry = textureRegistry.createSurfaceTexture() |
| 208 | 263 | ||
| 209 | // Preview | 264 | // Preview |
| @@ -229,42 +284,97 @@ class MobileScanner( | @@ -229,42 +284,97 @@ class MobileScanner( | ||
| 229 | // Build the analyzer to be passed on to MLKit | 284 | // Build the analyzer to be passed on to MLKit |
| 230 | val analysisBuilder = ImageAnalysis.Builder() | 285 | val analysisBuilder = ImageAnalysis.Builder() |
| 231 | .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) | 286 | .setBackpressureStrategy(ImageAnalysis.STRATEGY_KEEP_ONLY_LATEST) |
| 232 | -// analysisBuilder.setTargetResolution(Size(1440, 1920)) | 287 | + val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager |
| 288 | + | ||
| 289 | + if (cameraResolution != null) { | ||
| 290 | + if (newCameraResolutionSelector) { | ||
| 291 | + val selectorBuilder = ResolutionSelector.Builder() | ||
| 292 | + selectorBuilder.setResolutionStrategy( | ||
| 293 | + ResolutionStrategy( | ||
| 294 | + cameraResolution, | ||
| 295 | + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER | ||
| 296 | + ) | ||
| 297 | + ) | ||
| 298 | + analysisBuilder.setResolutionSelector(selectorBuilder.build()).build() | ||
| 299 | + } else { | ||
| 300 | + @Suppress("DEPRECATION") | ||
| 301 | + analysisBuilder.setTargetResolution(getResolution(cameraResolution)) | ||
| 302 | + } | ||
| 303 | + | ||
| 304 | + if (displayListener == null) { | ||
| 305 | + displayListener = object : DisplayManager.DisplayListener { | ||
| 306 | + override fun onDisplayAdded(displayId: Int) {} | ||
| 307 | + | ||
| 308 | + override fun onDisplayRemoved(displayId: Int) {} | ||
| 309 | + | ||
| 310 | + override fun onDisplayChanged(displayId: Int) { | ||
| 311 | + if (newCameraResolutionSelector) { | ||
| 312 | + val selectorBuilder = ResolutionSelector.Builder() | ||
| 313 | + selectorBuilder.setResolutionStrategy( | ||
| 314 | + ResolutionStrategy( | ||
| 315 | + cameraResolution, | ||
| 316 | + ResolutionStrategy.FALLBACK_RULE_CLOSEST_HIGHER_THEN_LOWER | ||
| 317 | + ) | ||
| 318 | + ) | ||
| 319 | + analysisBuilder.setResolutionSelector(selectorBuilder.build()).build() | ||
| 320 | + } else { | ||
| 321 | + @Suppress("DEPRECATION") | ||
| 322 | + analysisBuilder.setTargetResolution(getResolution(cameraResolution)) | ||
| 323 | + } | ||
| 324 | + } | ||
| 325 | + } | ||
| 326 | + | ||
| 327 | + displayManager.registerDisplayListener( | ||
| 328 | + displayListener, null, | ||
| 329 | + ) | ||
| 330 | + } | ||
| 331 | + } | ||
| 332 | + | ||
| 233 | val analysis = analysisBuilder.build().apply { setAnalyzer(executor, captureOutput) } | 333 | val analysis = analysisBuilder.build().apply { setAnalyzer(executor, captureOutput) } |
| 234 | 334 | ||
| 235 | - camera = cameraProvider!!.bindToLifecycle( | 335 | + try { |
| 336 | + camera = cameraProvider?.bindToLifecycle( | ||
| 236 | activity as LifecycleOwner, | 337 | activity as LifecycleOwner, |
| 237 | cameraPosition, | 338 | cameraPosition, |
| 238 | preview, | 339 | preview, |
| 239 | analysis | 340 | analysis |
| 240 | ) | 341 | ) |
| 342 | + } catch(exception: Exception) { | ||
| 343 | + mobileScannerErrorCallback(NoCamera()) | ||
| 344 | + | ||
| 345 | + return@addListener | ||
| 346 | + } | ||
| 241 | 347 | ||
| 348 | + camera?.let { | ||
| 242 | // Register the torch listener | 349 | // Register the torch listener |
| 243 | - camera!!.cameraInfo.torchState.observe(activity) { state -> | 350 | + it.cameraInfo.torchState.observe(activity as LifecycleOwner) { state -> |
| 244 | // TorchState.OFF = 0; TorchState.ON = 1 | 351 | // TorchState.OFF = 0; TorchState.ON = 1 |
| 245 | torchStateCallback(state) | 352 | torchStateCallback(state) |
| 246 | } | 353 | } |
| 247 | 354 | ||
| 248 | // Register the zoom scale listener | 355 | // Register the zoom scale listener |
| 249 | - camera!!.cameraInfo.zoomState.observe(activity) { state -> | 356 | + it.cameraInfo.zoomState.observe(activity) { state -> |
| 250 | zoomScaleStateCallback(state.linearZoom.toDouble()) | 357 | zoomScaleStateCallback(state.linearZoom.toDouble()) |
| 251 | } | 358 | } |
| 252 | 359 | ||
| 253 | - | ||
| 254 | // Enable torch if provided | 360 | // Enable torch if provided |
| 255 | - camera!!.cameraControl.enableTorch(torch) | 361 | + if (it.cameraInfo.hasFlashUnit()) { |
| 362 | + it.cameraControl.enableTorch(torch) | ||
| 363 | + } | ||
| 364 | + } | ||
| 256 | 365 | ||
| 257 | val resolution = analysis.resolutionInfo!!.resolution | 366 | val resolution = analysis.resolutionInfo!!.resolution |
| 258 | - val portrait = camera!!.cameraInfo.sensorRotationDegrees % 180 == 0 | ||
| 259 | val width = resolution.width.toDouble() | 367 | val width = resolution.width.toDouble() |
| 260 | val height = resolution.height.toDouble() | 368 | val height = resolution.height.toDouble() |
| 369 | + val portrait = (camera?.cameraInfo?.sensorRotationDegrees ?: 0) % 180 == 0 | ||
| 261 | 370 | ||
| 262 | mobileScannerStartedCallback( | 371 | mobileScannerStartedCallback( |
| 263 | MobileScannerStartParameters( | 372 | MobileScannerStartParameters( |
| 264 | if (portrait) width else height, | 373 | if (portrait) width else height, |
| 265 | if (portrait) height else width, | 374 | if (portrait) height else width, |
| 266 | - camera!!.cameraInfo.hasFlashUnit(), | ||
| 267 | - textureEntry!!.id() | 375 | + camera?.cameraInfo?.hasFlashUnit() ?: false, |
| 376 | + textureEntry!!.id(), | ||
| 377 | + nrOfCameras ?: 0 | ||
| 268 | ) | 378 | ) |
| 269 | ) | 379 | ) |
| 270 | }, executor) | 380 | }, executor) |
| @@ -278,6 +388,13 @@ class MobileScanner( | @@ -278,6 +388,13 @@ class MobileScanner( | ||
| 278 | throw AlreadyStopped() | 388 | throw AlreadyStopped() |
| 279 | } | 389 | } |
| 280 | 390 | ||
| 391 | + if (displayListener != null) { | ||
| 392 | + val displayManager = activity.applicationContext.getSystemService(Context.DISPLAY_SERVICE) as DisplayManager | ||
| 393 | + | ||
| 394 | + displayManager.unregisterDisplayListener(displayListener) | ||
| 395 | + displayListener = null | ||
| 396 | + } | ||
| 397 | + | ||
| 281 | val owner = activity as LifecycleOwner | 398 | val owner = activity as LifecycleOwner |
| 282 | camera?.cameraInfo?.torchState?.removeObservers(owner) | 399 | camera?.cameraInfo?.torchState?.removeObservers(owner) |
| 283 | cameraProvider?.unbindAll() | 400 | cameraProvider?.unbindAll() |
| @@ -296,9 +413,12 @@ class MobileScanner( | @@ -296,9 +413,12 @@ class MobileScanner( | ||
| 296 | */ | 413 | */ |
| 297 | fun toggleTorch(enableTorch: Boolean) { | 414 | fun toggleTorch(enableTorch: Boolean) { |
| 298 | if (camera == null) { | 415 | if (camera == null) { |
| 299 | - throw TorchWhenStopped() | 416 | + return |
| 417 | + } | ||
| 418 | + | ||
| 419 | + if (camera?.cameraInfo?.hasFlashUnit() == true) { | ||
| 420 | + camera?.cameraControl?.enableTorch(enableTorch) | ||
| 300 | } | 421 | } |
| 301 | - camera!!.cameraControl.enableTorch(enableTorch) | ||
| 302 | } | 422 | } |
| 303 | 423 | ||
| 304 | /** | 424 | /** |
| @@ -328,9 +448,9 @@ class MobileScanner( | @@ -328,9 +448,9 @@ class MobileScanner( | ||
| 328 | * Set the zoom rate of the camera. | 448 | * Set the zoom rate of the camera. |
| 329 | */ | 449 | */ |
| 330 | fun setScale(scale: Double) { | 450 | fun setScale(scale: Double) { |
| 331 | - if (camera == null) throw ZoomWhenStopped() | ||
| 332 | if (scale > 1.0 || scale < 0) throw ZoomNotInRange() | 451 | if (scale > 1.0 || scale < 0) throw ZoomNotInRange() |
| 333 | - camera!!.cameraControl.setLinearZoom(scale.toFloat()) | 452 | + if (camera == null) throw ZoomWhenStopped() |
| 453 | + camera?.cameraControl?.setLinearZoom(scale.toFloat()) | ||
| 334 | } | 454 | } |
| 335 | 455 | ||
| 336 | /** | 456 | /** |
| @@ -338,7 +458,7 @@ class MobileScanner( | @@ -338,7 +458,7 @@ class MobileScanner( | ||
| 338 | */ | 458 | */ |
| 339 | fun resetScale() { | 459 | fun resetScale() { |
| 340 | if (camera == null) throw ZoomWhenStopped() | 460 | if (camera == null) throw ZoomWhenStopped() |
| 341 | - camera!!.cameraControl.setZoomRatio(1f) | 461 | + camera?.cameraControl?.setZoomRatio(1f) |
| 342 | } | 462 | } |
| 343 | 463 | ||
| 344 | } | 464 | } |
| @@ -3,8 +3,6 @@ package dev.steenbakker.mobile_scanner | @@ -3,8 +3,6 @@ package dev.steenbakker.mobile_scanner | ||
| 3 | class NoCamera : Exception() | 3 | class NoCamera : Exception() |
| 4 | class AlreadyStarted : Exception() | 4 | class AlreadyStarted : Exception() |
| 5 | class AlreadyStopped : Exception() | 5 | class AlreadyStopped : Exception() |
| 6 | -class TorchError : Exception() | ||
| 7 | class CameraError : Exception() | 6 | class CameraError : Exception() |
| 8 | -class TorchWhenStopped : Exception() | ||
| 9 | class ZoomWhenStopped : Exception() | 7 | class ZoomWhenStopped : Exception() |
| 10 | class ZoomNotInRange : Exception() | 8 | class ZoomNotInRange : Exception() |
| @@ -2,6 +2,9 @@ package dev.steenbakker.mobile_scanner | @@ -2,6 +2,9 @@ package dev.steenbakker.mobile_scanner | ||
| 2 | 2 | ||
| 3 | import android.app.Activity | 3 | import android.app.Activity |
| 4 | import android.net.Uri | 4 | import android.net.Uri |
| 5 | +import android.os.Handler | ||
| 6 | +import android.os.Looper | ||
| 7 | +import android.util.Size | ||
| 5 | import androidx.camera.core.CameraSelector | 8 | import androidx.camera.core.CameraSelector |
| 6 | import androidx.camera.core.ExperimentalGetImage | 9 | import androidx.camera.core.ExperimentalGetImage |
| 7 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions | 10 | import com.google.mlkit.vision.barcode.BarcodeScannerOptions |
| @@ -29,12 +32,13 @@ class MobileScannerHandler( | @@ -29,12 +32,13 @@ class MobileScannerHandler( | ||
| 29 | "name" to "barcode", | 32 | "name" to "barcode", |
| 30 | "data" to barcodes | 33 | "data" to barcodes |
| 31 | )) | 34 | )) |
| 32 | - analyzerResult?.success(true) | ||
| 33 | - } else { | ||
| 34 | - analyzerResult?.success(false) | ||
| 35 | } | 35 | } |
| 36 | + | ||
| 37 | + Handler(Looper.getMainLooper()).post { | ||
| 38 | + analyzerResult?.success(barcodes != null) | ||
| 36 | analyzerResult = null | 39 | analyzerResult = null |
| 37 | } | 40 | } |
| 41 | + } | ||
| 38 | 42 | ||
| 39 | private var analyzerResult: MethodChannel.Result? = null | 43 | private var analyzerResult: MethodChannel.Result? = null |
| 40 | 44 | ||
| @@ -91,7 +95,6 @@ class MobileScannerHandler( | @@ -91,7 +95,6 @@ class MobileScannerHandler( | ||
| 91 | if(listener != null) { | 95 | if(listener != null) { |
| 92 | activityPluginBinding.removeRequestPermissionsResultListener(listener) | 96 | activityPluginBinding.removeRequestPermissionsResultListener(listener) |
| 93 | } | 97 | } |
| 94 | - | ||
| 95 | } | 98 | } |
| 96 | 99 | ||
| 97 | @ExperimentalGetImage | 100 | @ExperimentalGetImage |
| @@ -119,8 +122,8 @@ class MobileScannerHandler( | @@ -119,8 +122,8 @@ class MobileScannerHandler( | ||
| 119 | "stop" -> stop(result) | 122 | "stop" -> stop(result) |
| 120 | "analyzeImage" -> analyzeImage(call, result) | 123 | "analyzeImage" -> analyzeImage(call, result) |
| 121 | "setScale" -> setScale(call, result) | 124 | "setScale" -> setScale(call, result) |
| 122 | - "resetScale" -> resetScale(call, result) | ||
| 123 | - "updateScanWindow" -> updateScanWindow(call) | 125 | + "resetScale" -> resetScale(result) |
| 126 | + "updateScanWindow" -> updateScanWindow(call, result) | ||
| 124 | else -> result.notImplemented() | 127 | else -> result.notImplemented() |
| 125 | } | 128 | } |
| 126 | } | 129 | } |
| @@ -133,12 +136,19 @@ class MobileScannerHandler( | @@ -133,12 +136,19 @@ class MobileScannerHandler( | ||
| 133 | val returnImage: Boolean = call.argument<Boolean>("returnImage") ?: false | 136 | val returnImage: Boolean = call.argument<Boolean>("returnImage") ?: false |
| 134 | val speed: Int = call.argument<Int>("speed") ?: 1 | 137 | val speed: Int = call.argument<Int>("speed") ?: 1 |
| 135 | val timeout: Int = call.argument<Int>("timeout") ?: 250 | 138 | val timeout: Int = call.argument<Int>("timeout") ?: 250 |
| 139 | + val cameraResolutionValues: List<Int>? = call.argument<List<Int>>("cameraResolution") | ||
| 140 | + val useNewCameraSelector: Boolean = call.argument<Boolean>("useNewCameraSelector") ?: false | ||
| 141 | + val cameraResolution: Size? = if (cameraResolutionValues != null) { | ||
| 142 | + Size(cameraResolutionValues[0], cameraResolutionValues[1]) | ||
| 143 | + } else { | ||
| 144 | + null | ||
| 145 | + } | ||
| 136 | 146 | ||
| 137 | var barcodeScannerOptions: BarcodeScannerOptions? = null | 147 | var barcodeScannerOptions: BarcodeScannerOptions? = null |
| 138 | if (formats != null) { | 148 | if (formats != null) { |
| 139 | val formatsList: MutableList<Int> = mutableListOf() | 149 | val formatsList: MutableList<Int> = mutableListOf() |
| 140 | - for (index in formats) { | ||
| 141 | - formatsList.add(BarcodeFormats.values()[index].intValue) | 150 | + for (formatValue in formats) { |
| 151 | + formatsList.add(BarcodeFormats.fromRawValue(formatValue).intValue) | ||
| 142 | } | 152 | } |
| 143 | barcodeScannerOptions = if (formatsList.size == 1) { | 153 | barcodeScannerOptions = if (formatsList.size == 1) { |
| 144 | BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first()) | 154 | BarcodeScannerOptions.Builder().setBarcodeFormats(formatsList.first()) |
| @@ -156,48 +166,63 @@ class MobileScannerHandler( | @@ -156,48 +166,63 @@ class MobileScannerHandler( | ||
| 156 | 166 | ||
| 157 | val detectionSpeed: DetectionSpeed = DetectionSpeed.values().first { it.intValue == speed} | 167 | val detectionSpeed: DetectionSpeed = DetectionSpeed.values().first { it.intValue == speed} |
| 158 | 168 | ||
| 159 | - try { | ||
| 160 | - mobileScanner!!.start(barcodeScannerOptions, returnImage, position, torch, detectionSpeed, torchStateCallback, zoomScaleStateCallback, mobileScannerStartedCallback = { | 169 | + mobileScanner!!.start( |
| 170 | + barcodeScannerOptions, | ||
| 171 | + returnImage, | ||
| 172 | + position, | ||
| 173 | + torch, | ||
| 174 | + detectionSpeed, | ||
| 175 | + torchStateCallback, | ||
| 176 | + zoomScaleStateCallback, | ||
| 177 | + mobileScannerStartedCallback = { | ||
| 178 | + Handler(Looper.getMainLooper()).post { | ||
| 161 | result.success(mapOf( | 179 | result.success(mapOf( |
| 162 | "textureId" to it.id, | 180 | "textureId" to it.id, |
| 163 | "size" to mapOf("width" to it.width, "height" to it.height), | 181 | "size" to mapOf("width" to it.width, "height" to it.height), |
| 164 | - "torchable" to it.hasFlashUnit | 182 | + "torchable" to it.hasFlashUnit, |
| 183 | + "nrOfCameras" to it.nrOfCameras | ||
| 165 | )) | 184 | )) |
| 185 | + } | ||
| 166 | }, | 186 | }, |
| 167 | - timeout.toLong()) | ||
| 168 | - | ||
| 169 | - } catch (e: AlreadyStarted) { | 187 | + mobileScannerErrorCallback = { |
| 188 | + Handler(Looper.getMainLooper()).post { | ||
| 189 | + when (it) { | ||
| 190 | + is AlreadyStarted -> { | ||
| 170 | result.error( | 191 | result.error( |
| 171 | "MobileScanner", | 192 | "MobileScanner", |
| 172 | "Called start() while already started", | 193 | "Called start() while already started", |
| 173 | null | 194 | null |
| 174 | ) | 195 | ) |
| 175 | - } catch (e: NoCamera) { | ||
| 176 | - result.error( | ||
| 177 | - "MobileScanner", | ||
| 178 | - "No camera found or failed to open camera!", | ||
| 179 | - null | ||
| 180 | - ) | ||
| 181 | - } catch (e: TorchError) { | 196 | + } |
| 197 | + is CameraError -> { | ||
| 182 | result.error( | 198 | result.error( |
| 183 | "MobileScanner", | 199 | "MobileScanner", |
| 184 | - "Error occurred when setting torch!", | 200 | + "Error occurred when setting up camera!", |
| 185 | null | 201 | null |
| 186 | ) | 202 | ) |
| 187 | - } catch (e: CameraError) { | 203 | + } |
| 204 | + is NoCamera -> { | ||
| 188 | result.error( | 205 | result.error( |
| 189 | "MobileScanner", | 206 | "MobileScanner", |
| 190 | - "Error occurred when setting up camera!", | 207 | + "No camera found or failed to open camera!", |
| 191 | null | 208 | null |
| 192 | ) | 209 | ) |
| 193 | - } catch (e: Exception) { | 210 | + } |
| 211 | + else -> { | ||
| 194 | result.error( | 212 | result.error( |
| 195 | "MobileScanner", | 213 | "MobileScanner", |
| 196 | - "Unknown error occurred..", | 214 | + "Unknown error occurred.", |
| 197 | null | 215 | null |
| 198 | ) | 216 | ) |
| 199 | } | 217 | } |
| 200 | } | 218 | } |
| 219 | + } | ||
| 220 | + }, | ||
| 221 | + timeout.toLong(), | ||
| 222 | + cameraResolution, | ||
| 223 | + useNewCameraSelector | ||
| 224 | + ) | ||
| 225 | + } | ||
| 201 | 226 | ||
| 202 | private fun stop(result: MethodChannel.Result) { | 227 | private fun stop(result: MethodChannel.Result) { |
| 203 | try { | 228 | try { |
| @@ -215,12 +240,8 @@ class MobileScannerHandler( | @@ -215,12 +240,8 @@ class MobileScannerHandler( | ||
| 215 | } | 240 | } |
| 216 | 241 | ||
| 217 | private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) { | 242 | private fun toggleTorch(call: MethodCall, result: MethodChannel.Result) { |
| 218 | - try { | ||
| 219 | mobileScanner!!.toggleTorch(call.arguments == 1) | 243 | mobileScanner!!.toggleTorch(call.arguments == 1) |
| 220 | result.success(null) | 244 | result.success(null) |
| 221 | - } catch (e: AlreadyStopped) { | ||
| 222 | - result.error("MobileScanner", "Called toggleTorch() while stopped!", null) | ||
| 223 | - } | ||
| 224 | } | 245 | } |
| 225 | 246 | ||
| 226 | private fun setScale(call: MethodCall, result: MethodChannel.Result) { | 247 | private fun setScale(call: MethodCall, result: MethodChannel.Result) { |
| @@ -234,7 +255,7 @@ class MobileScannerHandler( | @@ -234,7 +255,7 @@ class MobileScannerHandler( | ||
| 234 | } | 255 | } |
| 235 | } | 256 | } |
| 236 | 257 | ||
| 237 | - private fun resetScale(call: MethodCall, result: MethodChannel.Result) { | 258 | + private fun resetScale(result: MethodChannel.Result) { |
| 238 | try { | 259 | try { |
| 239 | mobileScanner!!.resetScale() | 260 | mobileScanner!!.resetScale() |
| 240 | result.success(null) | 261 | result.success(null) |
| @@ -243,7 +264,9 @@ class MobileScannerHandler( | @@ -243,7 +264,9 @@ class MobileScannerHandler( | ||
| 243 | } | 264 | } |
| 244 | } | 265 | } |
| 245 | 266 | ||
| 246 | - private fun updateScanWindow(call: MethodCall) { | 267 | + private fun updateScanWindow(call: MethodCall, result: MethodChannel.Result) { |
| 247 | mobileScanner!!.scanWindow = call.argument<List<Float>?>("rect") | 268 | mobileScanner!!.scanWindow = call.argument<List<Float>?>("rect") |
| 269 | + | ||
| 270 | + result.success(null) | ||
| 248 | } | 271 | } |
| 249 | } | 272 | } |
| @@ -45,7 +45,7 @@ class MobileScannerPermissions { | @@ -45,7 +45,7 @@ class MobileScannerPermissions { | ||
| 45 | return if (hasPermission) { | 45 | return if (hasPermission) { |
| 46 | 1 | 46 | 1 |
| 47 | } else { | 47 | } else { |
| 48 | - 0 | 48 | + 2 |
| 49 | } | 49 | } |
| 50 | } | 50 | } |
| 51 | 51 | ||
| @@ -70,6 +70,7 @@ class MobileScannerPermissions { | @@ -70,6 +70,7 @@ class MobileScannerPermissions { | ||
| 70 | object: ResultCallback { | 70 | object: ResultCallback { |
| 71 | override fun onResult(errorCode: String?, errorDescription: String?) { | 71 | override fun onResult(errorCode: String?, errorDescription: String?) { |
| 72 | ongoing = false | 72 | ongoing = false |
| 73 | + listener = null | ||
| 73 | callback.onResult(errorCode, errorDescription) | 74 | callback.onResult(errorCode, errorDescription) |
| 74 | } | 75 | } |
| 75 | } | 76 | } |
| @@ -2,17 +2,41 @@ package dev.steenbakker.mobile_scanner.objects | @@ -2,17 +2,41 @@ package dev.steenbakker.mobile_scanner.objects | ||
| 2 | 2 | ||
| 3 | enum class BarcodeFormats(val intValue: Int) { | 3 | enum class BarcodeFormats(val intValue: Int) { |
| 4 | UNKNOWN(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UNKNOWN), | 4 | UNKNOWN(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UNKNOWN), |
| 5 | - ALL_FORMATS(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ALL_FORMATS), CODE_128(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_128), CODE_39( | ||
| 6 | - com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_39 | ||
| 7 | - ), | ||
| 8 | - CODE_93(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_93), CODABAR(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODABAR), DATA_MATRIX( | ||
| 9 | - com.google.mlkit.vision.barcode.common.Barcode.FORMAT_DATA_MATRIX | ||
| 10 | - ), | ||
| 11 | - EAN_13(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_13), EAN_8(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_8), ITF( | ||
| 12 | - com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ITF | ||
| 13 | - ), | ||
| 14 | - QR_CODE(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_QR_CODE), UPC_A(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_A), UPC_E( | ||
| 15 | - com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_E | ||
| 16 | - ), | ||
| 17 | - PDF417(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_PDF417), AZTEC(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_AZTEC); | 5 | + ALL_FORMATS(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ALL_FORMATS), |
| 6 | + CODE_128(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_128), | ||
| 7 | + CODE_39(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_39), | ||
| 8 | + CODE_93(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODE_93), | ||
| 9 | + CODABAR(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_CODABAR), | ||
| 10 | + DATA_MATRIX(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_DATA_MATRIX), | ||
| 11 | + EAN_13(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_13), | ||
| 12 | + EAN_8(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_EAN_8), | ||
| 13 | + ITF(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_ITF), | ||
| 14 | + QR_CODE(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_QR_CODE), | ||
| 15 | + UPC_A(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_A), | ||
| 16 | + UPC_E(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_UPC_E), | ||
| 17 | + PDF417(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_PDF417), | ||
| 18 | + AZTEC(com.google.mlkit.vision.barcode.common.Barcode.FORMAT_AZTEC); | ||
| 19 | + | ||
| 20 | + companion object { | ||
| 21 | + fun fromRawValue(rawValue: Int): BarcodeFormats { | ||
| 22 | + return when(rawValue) { | ||
| 23 | + -1 -> UNKNOWN | ||
| 24 | + 0 -> ALL_FORMATS | ||
| 25 | + 1 -> CODE_128 | ||
| 26 | + 2 -> CODE_39 | ||
| 27 | + 4 -> CODE_93 | ||
| 28 | + 8 -> CODABAR | ||
| 29 | + 16 -> DATA_MATRIX | ||
| 30 | + 32 -> EAN_13 | ||
| 31 | + 64 -> EAN_8 | ||
| 32 | + 128 -> ITF | ||
| 33 | + 256 -> QR_CODE | ||
| 34 | + 512 -> UPC_A | ||
| 35 | + 1024 -> UPC_E | ||
| 36 | + 2048 -> PDF417 | ||
| 37 | + 4096 -> AZTEC | ||
| 38 | + else -> UNKNOWN | ||
| 39 | + } | ||
| 40 | + } | ||
| 41 | + } | ||
| 18 | } | 42 | } |
| @@ -4,5 +4,6 @@ class MobileScannerStartParameters( | @@ -4,5 +4,6 @@ class MobileScannerStartParameters( | ||
| 4 | val width: Double = 0.0, | 4 | val width: Double = 0.0, |
| 5 | val height: Double, | 5 | val height: Double, |
| 6 | val hasFlashUnit: Boolean, | 6 | val hasFlashUnit: Boolean, |
| 7 | - val id: Long | 7 | + val id: Long, |
| 8 | + val nrOfCameras: Int | ||
| 8 | ) | 9 | ) |
| @@ -8,6 +8,7 @@ | @@ -8,6 +8,7 @@ | ||
| 8 | .buildlog/ | 8 | .buildlog/ |
| 9 | .history | 9 | .history |
| 10 | .svn/ | 10 | .svn/ |
| 11 | +migrate_working_dir/ | ||
| 11 | 12 | ||
| 12 | # IntelliJ related | 13 | # IntelliJ related |
| 13 | *.iml | 14 | *.iml |
| @@ -31,8 +32,6 @@ | @@ -31,8 +32,6 @@ | ||
| 31 | .pub/ | 32 | .pub/ |
| 32 | /build/ | 33 | /build/ |
| 33 | 34 | ||
| 34 | -# Web related | ||
| 35 | - | ||
| 36 | # Symbolication related | 35 | # Symbolication related |
| 37 | app.*.symbols | 36 | app.*.symbols |
| 38 | 37 |
example/.metadata
deleted
100644 → 0
| 1 | -# This file tracks properties of this Flutter project. | ||
| 2 | -# Used by Flutter tool to assess capabilities and perform upgrades etc. | ||
| 3 | -# | ||
| 4 | -# This file should be version controlled and should not be manually edited. | ||
| 5 | - | ||
| 6 | -version: | ||
| 7 | - revision: 5f105a6ca7a5ac7b8bc9b241f4c2d86f4188cf5c | ||
| 8 | - channel: stable | ||
| 9 | - | ||
| 10 | -project_type: app |
| @@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. | @@ -8,9 +8,9 @@ This project is a starting point for a Flutter application. | ||
| 8 | 8 | ||
| 9 | A few resources to get you started if this is your first Flutter project: | 9 | A few resources to get you started if this is your first Flutter project: |
| 10 | 10 | ||
| 11 | -- [Lab: Write your first Flutter app](https://flutter.dev/docs/get-started/codelab) | ||
| 12 | -- [Cookbook: Useful Flutter samples](https://flutter.dev/docs/cookbook) | 11 | +- [Lab: Write your first Flutter app](https://docs.flutter.dev/get-started/codelab) |
| 12 | +- [Cookbook: Useful Flutter samples](https://docs.flutter.dev/cookbook) | ||
| 13 | 13 | ||
| 14 | -For help getting started with Flutter, view our | ||
| 15 | -[online documentation](https://flutter.dev/docs), which offers tutorials, | 14 | +For help getting started with Flutter development, view the |
| 15 | +[online documentation](https://docs.flutter.dev/), which offers tutorials, | ||
| 16 | samples, guidance on mobile development, and a full API reference. | 16 | samples, guidance on mobile development, and a full API reference. |
| 1 | +plugins { | ||
| 2 | + id "com.android.application" | ||
| 3 | + id "kotlin-android" | ||
| 4 | + id "dev.flutter.flutter-gradle-plugin" | ||
| 5 | +} | ||
| 6 | + | ||
| 1 | def localProperties = new Properties() | 7 | def localProperties = new Properties() |
| 2 | def localPropertiesFile = rootProject.file('local.properties') | 8 | def localPropertiesFile = rootProject.file('local.properties') |
| 3 | if (localPropertiesFile.exists()) { | 9 | if (localPropertiesFile.exists()) { |
| @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { | @@ -6,11 +12,6 @@ if (localPropertiesFile.exists()) { | ||
| 6 | } | 12 | } |
| 7 | } | 13 | } |
| 8 | 14 | ||
| 9 | -def flutterRoot = localProperties.getProperty('flutter.sdk') | ||
| 10 | -if (flutterRoot == null) { | ||
| 11 | - throw new GradleException("Flutter SDK not found. Define location with flutter.sdk in the local.properties file.") | ||
| 12 | -} | ||
| 13 | - | ||
| 14 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') | 15 | def flutterVersionCode = localProperties.getProperty('flutter.versionCode') |
| 15 | if (flutterVersionCode == null) { | 16 | if (flutterVersionCode == null) { |
| 16 | flutterVersionCode = '1' | 17 | flutterVersionCode = '1' |
| @@ -21,20 +22,19 @@ if (flutterVersionName == null) { | @@ -21,20 +22,19 @@ if (flutterVersionName == null) { | ||
| 21 | flutterVersionName = '1.0' | 22 | flutterVersionName = '1.0' |
| 22 | } | 23 | } |
| 23 | 24 | ||
| 24 | -apply plugin: 'com.android.application' | ||
| 25 | -apply plugin: 'kotlin-android' | ||
| 26 | -apply from: "$flutterRoot/packages/flutter_tools/gradle/flutter.gradle" | ||
| 27 | - | ||
| 28 | android { | 25 | android { |
| 29 | - compileSdkVersion 33 | 26 | + namespace "dev.steenbakker.mobile_scanner_example" |
| 27 | + compileSdk 34 | ||
| 28 | + ndkVersion "25.1.8937393" | ||
| 29 | +// ndkVersion flutter.ndkVersion | ||
| 30 | 30 | ||
| 31 | compileOptions { | 31 | compileOptions { |
| 32 | - sourceCompatibility JavaVersion.VERSION_1_8 | ||
| 33 | - targetCompatibility JavaVersion.VERSION_1_8 | 32 | + sourceCompatibility JavaVersion.VERSION_17 |
| 33 | + targetCompatibility JavaVersion.VERSION_17 | ||
| 34 | } | 34 | } |
| 35 | 35 | ||
| 36 | kotlinOptions { | 36 | kotlinOptions { |
| 37 | - jvmTarget = '1.8' | 37 | + jvmTarget = '17' |
| 38 | } | 38 | } |
| 39 | 39 | ||
| 40 | sourceSets { | 40 | sourceSets { |
| @@ -45,7 +45,7 @@ android { | @@ -45,7 +45,7 @@ android { | ||
| 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). | 45 | // TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html). |
| 46 | applicationId "dev.steenbakker.mobile_scanner_example" | 46 | applicationId "dev.steenbakker.mobile_scanner_example" |
| 47 | minSdkVersion 21 | 47 | minSdkVersion 21 |
| 48 | - targetSdkVersion 33 | 48 | + targetSdkVersion 34 |
| 49 | versionCode flutterVersionCode.toInteger() | 49 | versionCode flutterVersionCode.toInteger() |
| 50 | versionName flutterVersionName | 50 | versionName flutterVersionName |
| 51 | } | 51 | } |
| @@ -57,7 +57,6 @@ android { | @@ -57,7 +57,6 @@ android { | ||
| 57 | signingConfig signingConfigs.debug | 57 | signingConfig signingConfigs.debug |
| 58 | } | 58 | } |
| 59 | } | 59 | } |
| 60 | - namespace 'dev.steenbakker.mobile_scanner_example' | ||
| 61 | } | 60 | } |
| 62 | 61 | ||
| 63 | flutter { | 62 | flutter { |
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| 2 | - <!-- Flutter needs it to communicate with the running application | 2 | + <!-- The INTERNET permission is required for development. Specifically, |
| 3 | + the Flutter tool needs it to communicate with the running application | ||
| 3 | to allow setting breakpoints, to provide hot reload, etc. | 4 | to allow setting breakpoints, to provide hot reload, etc. |
| 4 | --> | 5 | --> |
| 5 | <uses-permission android:name="android.permission.INTERNET"/> | 6 | <uses-permission android:name="android.permission.INTERNET"/> |
| 1 | -<manifest xmlns:android="http://schemas.android.com/apk/res/android" | ||
| 2 | - package="dev.steenbakker.mobile_scanner_example"> | ||
| 3 | - | 1 | +<manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| 4 | <application | 2 | <application |
| 5 | android:label="mobile_scanner_example" | 3 | android:label="mobile_scanner_example" |
| 4 | + android:name="${applicationName}" | ||
| 6 | android:icon="@mipmap/ic_launcher"> | 5 | android:icon="@mipmap/ic_launcher"> |
| 7 | <activity | 6 | <activity |
| 8 | android:name=".MainActivity" | 7 | android:name=".MainActivity" |
| 1 | package dev.steenbakker.mobile_scanner_example | 1 | package dev.steenbakker.mobile_scanner_example |
| 2 | 2 | ||
| 3 | -import io.flutter.embedding.android.FlutterFragmentActivity | 3 | +import io.flutter.embedding.android.FlutterActivity |
| 4 | 4 | ||
| 5 | -class MainActivity : FlutterFragmentActivity() { | 5 | +class MainActivity: FlutterActivity() { |
| 6 | } | 6 | } |
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> | 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is on --> |
| 4 | <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> | 4 | <style name="LaunchTheme" parent="@android:style/Theme.Black.NoTitleBar"> |
| 5 | <!-- Show a splash screen on the activity. Automatically removed when | 5 | <!-- Show a splash screen on the activity. Automatically removed when |
| 6 | - Flutter draws its first frame --> | 6 | + the Flutter engine draws its first frame --> |
| 7 | <item name="android:windowBackground">@drawable/launch_background</item> | 7 | <item name="android:windowBackground">@drawable/launch_background</item> |
| 8 | </style> | 8 | </style> |
| 9 | <!-- Theme applied to the Android Window as soon as the process has started. | 9 | <!-- Theme applied to the Android Window as soon as the process has started. |
| @@ -3,7 +3,7 @@ | @@ -3,7 +3,7 @@ | ||
| 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> | 3 | <!-- Theme applied to the Android Window while the process is starting when the OS's Dark Mode setting is off --> |
| 4 | <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> | 4 | <style name="LaunchTheme" parent="@android:style/Theme.Light.NoTitleBar"> |
| 5 | <!-- Show a splash screen on the activity. Automatically removed when | 5 | <!-- Show a splash screen on the activity. Automatically removed when |
| 6 | - Flutter draws its first frame --> | 6 | + the Flutter engine draws its first frame --> |
| 7 | <item name="android:windowBackground">@drawable/launch_background</item> | 7 | <item name="android:windowBackground">@drawable/launch_background</item> |
| 8 | </style> | 8 | </style> |
| 9 | <!-- Theme applied to the Android Window as soon as the process has started. | 9 | <!-- Theme applied to the Android Window as soon as the process has started. |
| 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> | 1 | <manifest xmlns:android="http://schemas.android.com/apk/res/android"> |
| 2 | - <!-- Flutter needs it to communicate with the running application | 2 | + <!-- The INTERNET permission is required for development. Specifically, |
| 3 | + the Flutter tool needs it to communicate with the running application | ||
| 3 | to allow setting breakpoints, to provide hot reload, etc. | 4 | to allow setting breakpoints, to provide hot reload, etc. |
| 4 | --> | 5 | --> |
| 5 | <uses-permission android:name="android.permission.INTERNET"/> | 6 | <uses-permission android:name="android.permission.INTERNET"/> |
| @@ -6,7 +6,7 @@ buildscript { | @@ -6,7 +6,7 @@ buildscript { | ||
| 6 | } | 6 | } |
| 7 | 7 | ||
| 8 | dependencies { | 8 | dependencies { |
| 9 | - classpath 'com.android.tools.build:gradle:8.1.1' | 9 | + classpath 'com.android.tools.build:gradle:8.2.0' |
| 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" | 10 | classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version" |
| 11 | } | 11 | } |
| 12 | } | 12 | } |
| 1 | -#Tue Jun 27 18:47:05 CEST 2023 | ||
| 2 | distributionBase=GRADLE_USER_HOME | 1 | distributionBase=GRADLE_USER_HOME |
| 3 | distributionPath=wrapper/dists | 2 | distributionPath=wrapper/dists |
| 4 | -distributionUrl=https\://services.gradle.org/distributions/gradle-8.1.1-bin.zip | 3 | +distributionUrl=https\://services.gradle.org/distributions/gradle-8.2-bin.zip |
| 5 | zipStoreBase=GRADLE_USER_HOME | 4 | zipStoreBase=GRADLE_USER_HOME |
| 6 | zipStorePath=wrapper/dists | 5 | zipStorePath=wrapper/dists |
| 1 | -include ':app' | 1 | +pluginManagement { |
| 2 | + def flutterSdkPath = { | ||
| 3 | + def properties = new Properties() | ||
| 4 | + file("local.properties").withInputStream { properties.load(it) } | ||
| 5 | + def flutterSdkPath = properties.getProperty("flutter.sdk") | ||
| 6 | + assert flutterSdkPath != null, "flutter.sdk not set in local.properties" | ||
| 7 | + return flutterSdkPath | ||
| 8 | + } | ||
| 9 | + settings.ext.flutterSdkPath = flutterSdkPath() | ||
| 2 | 10 | ||
| 3 | -def localPropertiesFile = new File(rootProject.projectDir, "local.properties") | ||
| 4 | -def properties = new Properties() | 11 | + includeBuild("${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle") |
| 12 | +} | ||
| 5 | 13 | ||
| 6 | -assert localPropertiesFile.exists() | ||
| 7 | -localPropertiesFile.withReader("UTF-8") { reader -> properties.load(reader) } | 14 | +include ":app" |
| 8 | 15 | ||
| 9 | -def flutterSdkPath = properties.getProperty("flutter.sdk") | ||
| 10 | -assert flutterSdkPath != null, "flutter.sdk not set in local.properties" | ||
| 11 | -apply from: "$flutterSdkPath/packages/flutter_tools/gradle/app_plugin_loader.gradle" | 16 | +apply from: "${settings.ext.flutterSdkPath}/packages/flutter_tools/gradle/app_plugin_loader.gradle" |
| 1 | # Uncomment this line to define a global platform for your project | 1 | # Uncomment this line to define a global platform for your project |
| 2 | -platform :ios, '11.0' | 2 | +# platform :ios, '11.0' |
| 3 | 3 | ||
| 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. | 4 | # CocoaPods analytics sends network stats synchronously affecting flutter build latency. |
| 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' | 5 | ENV['COCOAPODS_DISABLE_STATS'] = 'true' |
| @@ -32,13 +32,15 @@ target 'Runner' do | @@ -32,13 +32,15 @@ target 'Runner' do | ||
| 32 | use_modular_headers! | 32 | use_modular_headers! |
| 33 | 33 | ||
| 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) | 34 | flutter_install_all_ios_pods File.dirname(File.realpath(__FILE__)) |
| 35 | + target 'RunnerTests' do | ||
| 36 | + inherit! :search_paths | ||
| 37 | + end | ||
| 35 | end | 38 | end |
| 36 | 39 | ||
| 37 | post_install do |installer| | 40 | post_install do |installer| |
| 38 | installer.pods_project.targets.each do |target| | 41 | installer.pods_project.targets.each do |target| |
| 39 | flutter_additional_ios_build_settings(target) | 42 | flutter_additional_ios_build_settings(target) |
| 40 | target.build_configurations.each do |config| | 43 | target.build_configurations.each do |config| |
| 41 | - config.build_settings['ENABLE_BITCODE'] = 'NO' | ||
| 42 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' | 44 | config.build_settings['IPHONEOS_DEPLOYMENT_TARGET'] = '11.0' |
| 43 | end | 45 | end |
| 44 | end | 46 | end |
| @@ -8,14 +8,26 @@ | @@ -8,14 +8,26 @@ | ||
| 8 | 8 | ||
| 9 | /* Begin PBXBuildFile section */ | 9 | /* Begin PBXBuildFile section */ |
| 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; | 10 | 1498D2341E8E89220040F4C2 /* GeneratedPluginRegistrant.m in Sources */ = {isa = PBXBuildFile; fileRef = 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */; }; |
| 11 | + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C807B294A618700263BE5 /* RunnerTests.swift */; }; | ||
| 11 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; | 12 | 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; |
| 12 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; | 13 | 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; |
| 13 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; | 14 | 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; |
| 14 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; | 15 | 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; |
| 15 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; | 16 | 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; |
| 16 | - A5A2C2B73A9F26060DE9FB22 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */; }; | 17 | + A277671F4E603C31E191FCBF /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = FB50C06655B1959BAA8EAF64 /* Pods_Runner.framework */; }; |
| 18 | + F8C489EFA0FC3A131EBA1838 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 9F20A7A668B7797BBB5443C1 /* Pods_RunnerTests.framework */; }; | ||
| 17 | /* End PBXBuildFile section */ | 19 | /* End PBXBuildFile section */ |
| 18 | 20 | ||
| 21 | +/* Begin PBXContainerItemProxy section */ | ||
| 22 | + 331C8085294A63A400263BE5 /* PBXContainerItemProxy */ = { | ||
| 23 | + isa = PBXContainerItemProxy; | ||
| 24 | + containerPortal = 97C146E61CF9000F007C117D /* Project object */; | ||
| 25 | + proxyType = 1; | ||
| 26 | + remoteGlobalIDString = 97C146ED1CF9000F007C117D; | ||
| 27 | + remoteInfo = Runner; | ||
| 28 | + }; | ||
| 29 | +/* End PBXContainerItemProxy section */ | ||
| 30 | + | ||
| 19 | /* Begin PBXCopyFilesBuildPhase section */ | 31 | /* Begin PBXCopyFilesBuildPhase section */ |
| 20 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { | 32 | 9705A1C41CF9048500538489 /* Embed Frameworks */ = { |
| 21 | isa = PBXCopyFilesBuildPhase; | 33 | isa = PBXCopyFilesBuildPhase; |
| @@ -32,12 +44,14 @@ | @@ -32,12 +44,14 @@ | ||
| 32 | /* Begin PBXFileReference section */ | 44 | /* Begin PBXFileReference section */ |
| 33 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; | 45 | 1498D2321E8E86230040F4C2 /* GeneratedPluginRegistrant.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = GeneratedPluginRegistrant.h; sourceTree = "<group>"; }; |
| 34 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; | 46 | 1498D2331E8E89220040F4C2 /* GeneratedPluginRegistrant.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = GeneratedPluginRegistrant.m; sourceTree = "<group>"; }; |
| 35 | - 32FD382A786B3A0080FE63FD /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | 47 | + 331C807B294A618700263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; |
| 48 | + 331C8081294A63A400263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 49 | + 337C24E5CFDB233CDBA51D9D /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | ||
| 36 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; | 50 | 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.xml; name = AppFrameworkInfo.plist; path = Flutter/AppFrameworkInfo.plist; sourceTree = "<group>"; }; |
| 37 | - 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 38 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; | 51 | 74858FAD1ED2DC5600515810 /* Runner-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "Runner-Bridging-Header.h"; sourceTree = "<group>"; }; |
| 39 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; | 52 | 74858FAE1ED2DC5600515810 /* AppDelegate.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = "<group>"; }; |
| 40 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; | 53 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = "<group>"; }; |
| 54 | + 7F6B8553738879C25774B609 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; | ||
| 41 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; | 55 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = "<group>"; }; |
| 42 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; | 56 | 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = "<group>"; }; |
| 43 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; | 57 | 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
| @@ -45,30 +59,40 @@ | @@ -45,30 +59,40 @@ | ||
| 45 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; | 59 | 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; |
| 46 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; | 60 | 97C147001CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = "<group>"; }; |
| 47 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; | 61 | 97C147021CF9000F007C117D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; }; |
| 48 | - D5B36FCD262B39F867CFDEEE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 49 | - F0D5742F0690BE32D07B033A /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | 62 | + 9F20A7A668B7797BBB5443C1 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 63 | + A5A24D61913C510FB38A434E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 64 | + ADC5B74E7E0BCF65C9CD3AA9 /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 65 | + CB190BADFE09EBDD6E5B44BE /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 66 | + F3732C1D66ACA2383B88CDC2 /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 67 | + FB50C06655B1959BAA8EAF64 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 50 | /* End PBXFileReference section */ | 68 | /* End PBXFileReference section */ |
| 51 | 69 | ||
| 52 | /* Begin PBXFrameworksBuildPhase section */ | 70 | /* Begin PBXFrameworksBuildPhase section */ |
| 71 | + 0F3F7D03C80D20F6F18A5A17 /* Frameworks */ = { | ||
| 72 | + isa = PBXFrameworksBuildPhase; | ||
| 73 | + buildActionMask = 2147483647; | ||
| 74 | + files = ( | ||
| 75 | + F8C489EFA0FC3A131EBA1838 /* Pods_RunnerTests.framework in Frameworks */, | ||
| 76 | + ); | ||
| 77 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 78 | + }; | ||
| 53 | 97C146EB1CF9000F007C117D /* Frameworks */ = { | 79 | 97C146EB1CF9000F007C117D /* Frameworks */ = { |
| 54 | isa = PBXFrameworksBuildPhase; | 80 | isa = PBXFrameworksBuildPhase; |
| 55 | buildActionMask = 2147483647; | 81 | buildActionMask = 2147483647; |
| 56 | files = ( | 82 | files = ( |
| 57 | - A5A2C2B73A9F26060DE9FB22 /* Pods_Runner.framework in Frameworks */, | 83 | + A277671F4E603C31E191FCBF /* Pods_Runner.framework in Frameworks */, |
| 58 | ); | 84 | ); |
| 59 | runOnlyForDeploymentPostprocessing = 0; | 85 | runOnlyForDeploymentPostprocessing = 0; |
| 60 | }; | 86 | }; |
| 61 | /* End PBXFrameworksBuildPhase section */ | 87 | /* End PBXFrameworksBuildPhase section */ |
| 62 | 88 | ||
| 63 | /* Begin PBXGroup section */ | 89 | /* Begin PBXGroup section */ |
| 64 | - 203D5C95A734778D93D18369 /* Pods */ = { | 90 | + 331C8082294A63A400263BE5 /* RunnerTests */ = { |
| 65 | isa = PBXGroup; | 91 | isa = PBXGroup; |
| 66 | children = ( | 92 | children = ( |
| 67 | - D5B36FCD262B39F867CFDEEE /* Pods-Runner.debug.xcconfig */, | ||
| 68 | - 32FD382A786B3A0080FE63FD /* Pods-Runner.release.xcconfig */, | ||
| 69 | - F0D5742F0690BE32D07B033A /* Pods-Runner.profile.xcconfig */, | 93 | + 331C807B294A618700263BE5 /* RunnerTests.swift */, |
| 70 | ); | 94 | ); |
| 71 | - path = Pods; | 95 | + path = RunnerTests; |
| 72 | sourceTree = "<group>"; | 96 | sourceTree = "<group>"; |
| 73 | }; | 97 | }; |
| 74 | 9740EEB11CF90186004384FC /* Flutter */ = { | 98 | 9740EEB11CF90186004384FC /* Flutter */ = { |
| @@ -88,8 +112,9 @@ | @@ -88,8 +112,9 @@ | ||
| 88 | 9740EEB11CF90186004384FC /* Flutter */, | 112 | 9740EEB11CF90186004384FC /* Flutter */, |
| 89 | 97C146F01CF9000F007C117D /* Runner */, | 113 | 97C146F01CF9000F007C117D /* Runner */, |
| 90 | 97C146EF1CF9000F007C117D /* Products */, | 114 | 97C146EF1CF9000F007C117D /* Products */, |
| 91 | - 203D5C95A734778D93D18369 /* Pods */, | ||
| 92 | - FF36E403CAC9E06A5A96BB9F /* Frameworks */, | 115 | + 331C8082294A63A400263BE5 /* RunnerTests */, |
| 116 | + C3326B04728E979C0CAEA680 /* Pods */, | ||
| 117 | + E31F04DB28A3D0F29210F29D /* Frameworks */, | ||
| 93 | ); | 118 | ); |
| 94 | sourceTree = "<group>"; | 119 | sourceTree = "<group>"; |
| 95 | }; | 120 | }; |
| @@ -97,6 +122,7 @@ | @@ -97,6 +122,7 @@ | ||
| 97 | isa = PBXGroup; | 122 | isa = PBXGroup; |
| 98 | children = ( | 123 | children = ( |
| 99 | 97C146EE1CF9000F007C117D /* Runner.app */, | 124 | 97C146EE1CF9000F007C117D /* Runner.app */, |
| 125 | + 331C8081294A63A400263BE5 /* RunnerTests.xctest */, | ||
| 100 | ); | 126 | ); |
| 101 | name = Products; | 127 | name = Products; |
| 102 | sourceTree = "<group>"; | 128 | sourceTree = "<group>"; |
| @@ -116,10 +142,24 @@ | @@ -116,10 +142,24 @@ | ||
| 116 | path = Runner; | 142 | path = Runner; |
| 117 | sourceTree = "<group>"; | 143 | sourceTree = "<group>"; |
| 118 | }; | 144 | }; |
| 119 | - FF36E403CAC9E06A5A96BB9F /* Frameworks */ = { | 145 | + C3326B04728E979C0CAEA680 /* Pods */ = { |
| 120 | isa = PBXGroup; | 146 | isa = PBXGroup; |
| 121 | children = ( | 147 | children = ( |
| 122 | - 54E006799E73DEAB41FD3623 /* Pods_Runner.framework */, | 148 | + CB190BADFE09EBDD6E5B44BE /* Pods-Runner.debug.xcconfig */, |
| 149 | + F3732C1D66ACA2383B88CDC2 /* Pods-Runner.release.xcconfig */, | ||
| 150 | + 337C24E5CFDB233CDBA51D9D /* Pods-Runner.profile.xcconfig */, | ||
| 151 | + A5A24D61913C510FB38A434E /* Pods-RunnerTests.debug.xcconfig */, | ||
| 152 | + ADC5B74E7E0BCF65C9CD3AA9 /* Pods-RunnerTests.release.xcconfig */, | ||
| 153 | + 7F6B8553738879C25774B609 /* Pods-RunnerTests.profile.xcconfig */, | ||
| 154 | + ); | ||
| 155 | + path = Pods; | ||
| 156 | + sourceTree = "<group>"; | ||
| 157 | + }; | ||
| 158 | + E31F04DB28A3D0F29210F29D /* Frameworks */ = { | ||
| 159 | + isa = PBXGroup; | ||
| 160 | + children = ( | ||
| 161 | + FB50C06655B1959BAA8EAF64 /* Pods_Runner.framework */, | ||
| 162 | + 9F20A7A668B7797BBB5443C1 /* Pods_RunnerTests.framework */, | ||
| 123 | ); | 163 | ); |
| 124 | name = Frameworks; | 164 | name = Frameworks; |
| 125 | sourceTree = "<group>"; | 165 | sourceTree = "<group>"; |
| @@ -127,18 +167,37 @@ | @@ -127,18 +167,37 @@ | ||
| 127 | /* End PBXGroup section */ | 167 | /* End PBXGroup section */ |
| 128 | 168 | ||
| 129 | /* Begin PBXNativeTarget section */ | 169 | /* Begin PBXNativeTarget section */ |
| 170 | + 331C8080294A63A400263BE5 /* RunnerTests */ = { | ||
| 171 | + isa = PBXNativeTarget; | ||
| 172 | + buildConfigurationList = 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | ||
| 173 | + buildPhases = ( | ||
| 174 | + C7DE006A696F551C4E067E41 /* [CP] Check Pods Manifest.lock */, | ||
| 175 | + 331C807D294A63A400263BE5 /* Sources */, | ||
| 176 | + 331C807F294A63A400263BE5 /* Resources */, | ||
| 177 | + 0F3F7D03C80D20F6F18A5A17 /* Frameworks */, | ||
| 178 | + ); | ||
| 179 | + buildRules = ( | ||
| 180 | + ); | ||
| 181 | + dependencies = ( | ||
| 182 | + 331C8086294A63A400263BE5 /* PBXTargetDependency */, | ||
| 183 | + ); | ||
| 184 | + name = RunnerTests; | ||
| 185 | + productName = RunnerTests; | ||
| 186 | + productReference = 331C8081294A63A400263BE5 /* RunnerTests.xctest */; | ||
| 187 | + productType = "com.apple.product-type.bundle.unit-test"; | ||
| 188 | + }; | ||
| 130 | 97C146ED1CF9000F007C117D /* Runner */ = { | 189 | 97C146ED1CF9000F007C117D /* Runner */ = { |
| 131 | isa = PBXNativeTarget; | 190 | isa = PBXNativeTarget; |
| 132 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; | 191 | buildConfigurationList = 97C147051CF9000F007C117D /* Build configuration list for PBXNativeTarget "Runner" */; |
| 133 | buildPhases = ( | 192 | buildPhases = ( |
| 134 | - B086C54F5791A4E759CB6822 /* [CP] Check Pods Manifest.lock */, | 193 | + C8221F629E52D01B6ABC23B2 /* [CP] Check Pods Manifest.lock */, |
| 135 | 9740EEB61CF901F6004384FC /* Run Script */, | 194 | 9740EEB61CF901F6004384FC /* Run Script */, |
| 136 | 97C146EA1CF9000F007C117D /* Sources */, | 195 | 97C146EA1CF9000F007C117D /* Sources */, |
| 137 | 97C146EB1CF9000F007C117D /* Frameworks */, | 196 | 97C146EB1CF9000F007C117D /* Frameworks */, |
| 138 | 97C146EC1CF9000F007C117D /* Resources */, | 197 | 97C146EC1CF9000F007C117D /* Resources */, |
| 139 | 9705A1C41CF9048500538489 /* Embed Frameworks */, | 198 | 9705A1C41CF9048500538489 /* Embed Frameworks */, |
| 140 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, | 199 | 3B06AD1E1E4923F5004D2608 /* Thin Binary */, |
| 141 | - F825A499E8C466DB9DC6247D /* [CP] Embed Pods Frameworks */, | 200 | + 3DBCC0215D7BED1D9A756EA3 /* [CP] Embed Pods Frameworks */, |
| 142 | ); | 201 | ); |
| 143 | buildRules = ( | 202 | buildRules = ( |
| 144 | ); | 203 | ); |
| @@ -155,9 +214,14 @@ | @@ -155,9 +214,14 @@ | ||
| 155 | 97C146E61CF9000F007C117D /* Project object */ = { | 214 | 97C146E61CF9000F007C117D /* Project object */ = { |
| 156 | isa = PBXProject; | 215 | isa = PBXProject; |
| 157 | attributes = { | 216 | attributes = { |
| 158 | - LastUpgradeCheck = 1300; | 217 | + BuildIndependentTargetsInParallel = YES; |
| 218 | + LastUpgradeCheck = 1430; | ||
| 159 | ORGANIZATIONNAME = ""; | 219 | ORGANIZATIONNAME = ""; |
| 160 | TargetAttributes = { | 220 | TargetAttributes = { |
| 221 | + 331C8080294A63A400263BE5 = { | ||
| 222 | + CreatedOnToolsVersion = 14.0; | ||
| 223 | + TestTargetID = 97C146ED1CF9000F007C117D; | ||
| 224 | + }; | ||
| 161 | 97C146ED1CF9000F007C117D = { | 225 | 97C146ED1CF9000F007C117D = { |
| 162 | CreatedOnToolsVersion = 7.3.1; | 226 | CreatedOnToolsVersion = 7.3.1; |
| 163 | LastSwiftMigration = 1100; | 227 | LastSwiftMigration = 1100; |
| @@ -178,11 +242,19 @@ | @@ -178,11 +242,19 @@ | ||
| 178 | projectRoot = ""; | 242 | projectRoot = ""; |
| 179 | targets = ( | 243 | targets = ( |
| 180 | 97C146ED1CF9000F007C117D /* Runner */, | 244 | 97C146ED1CF9000F007C117D /* Runner */, |
| 245 | + 331C8080294A63A400263BE5 /* RunnerTests */, | ||
| 181 | ); | 246 | ); |
| 182 | }; | 247 | }; |
| 183 | /* End PBXProject section */ | 248 | /* End PBXProject section */ |
| 184 | 249 | ||
| 185 | /* Begin PBXResourcesBuildPhase section */ | 250 | /* Begin PBXResourcesBuildPhase section */ |
| 251 | + 331C807F294A63A400263BE5 /* Resources */ = { | ||
| 252 | + isa = PBXResourcesBuildPhase; | ||
| 253 | + buildActionMask = 2147483647; | ||
| 254 | + files = ( | ||
| 255 | + ); | ||
| 256 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 257 | + }; | ||
| 186 | 97C146EC1CF9000F007C117D /* Resources */ = { | 258 | 97C146EC1CF9000F007C117D /* Resources */ = { |
| 187 | isa = PBXResourcesBuildPhase; | 259 | isa = PBXResourcesBuildPhase; |
| 188 | buildActionMask = 2147483647; | 260 | buildActionMask = 2147483647; |
| @@ -213,6 +285,23 @@ | @@ -213,6 +285,23 @@ | ||
| 213 | shellPath = /bin/sh; | 285 | shellPath = /bin/sh; |
| 214 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; | 286 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" embed_and_thin"; |
| 215 | }; | 287 | }; |
| 288 | + 3DBCC0215D7BED1D9A756EA3 /* [CP] Embed Pods Frameworks */ = { | ||
| 289 | + isa = PBXShellScriptBuildPhase; | ||
| 290 | + buildActionMask = 2147483647; | ||
| 291 | + files = ( | ||
| 292 | + ); | ||
| 293 | + inputFileListPaths = ( | ||
| 294 | + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||
| 295 | + ); | ||
| 296 | + name = "[CP] Embed Pods Frameworks"; | ||
| 297 | + outputFileListPaths = ( | ||
| 298 | + "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | ||
| 299 | + ); | ||
| 300 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 301 | + shellPath = /bin/sh; | ||
| 302 | + shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | ||
| 303 | + showEnvVarsInLog = 0; | ||
| 304 | + }; | ||
| 216 | 9740EEB61CF901F6004384FC /* Run Script */ = { | 305 | 9740EEB61CF901F6004384FC /* Run Script */ = { |
| 217 | isa = PBXShellScriptBuildPhase; | 306 | isa = PBXShellScriptBuildPhase; |
| 218 | alwaysOutOfDate = 1; | 307 | alwaysOutOfDate = 1; |
| @@ -228,7 +317,7 @@ | @@ -228,7 +317,7 @@ | ||
| 228 | shellPath = /bin/sh; | 317 | shellPath = /bin/sh; |
| 229 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; | 318 | shellScript = "/bin/sh \"$FLUTTER_ROOT/packages/flutter_tools/bin/xcode_backend.sh\" build"; |
| 230 | }; | 319 | }; |
| 231 | - B086C54F5791A4E759CB6822 /* [CP] Check Pods Manifest.lock */ = { | 320 | + C7DE006A696F551C4E067E41 /* [CP] Check Pods Manifest.lock */ = { |
| 232 | isa = PBXShellScriptBuildPhase; | 321 | isa = PBXShellScriptBuildPhase; |
| 233 | buildActionMask = 2147483647; | 322 | buildActionMask = 2147483647; |
| 234 | files = ( | 323 | files = ( |
| @@ -243,33 +332,46 @@ | @@ -243,33 +332,46 @@ | ||
| 243 | outputFileListPaths = ( | 332 | outputFileListPaths = ( |
| 244 | ); | 333 | ); |
| 245 | outputPaths = ( | 334 | outputPaths = ( |
| 246 | - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | 335 | + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", |
| 247 | ); | 336 | ); |
| 248 | runOnlyForDeploymentPostprocessing = 0; | 337 | runOnlyForDeploymentPostprocessing = 0; |
| 249 | shellPath = /bin/sh; | 338 | shellPath = /bin/sh; |
| 250 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | 339 | shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; |
| 251 | showEnvVarsInLog = 0; | 340 | showEnvVarsInLog = 0; |
| 252 | }; | 341 | }; |
| 253 | - F825A499E8C466DB9DC6247D /* [CP] Embed Pods Frameworks */ = { | 342 | + C8221F629E52D01B6ABC23B2 /* [CP] Check Pods Manifest.lock */ = { |
| 254 | isa = PBXShellScriptBuildPhase; | 343 | isa = PBXShellScriptBuildPhase; |
| 255 | buildActionMask = 2147483647; | 344 | buildActionMask = 2147483647; |
| 256 | files = ( | 345 | files = ( |
| 257 | ); | 346 | ); |
| 258 | inputFileListPaths = ( | 347 | inputFileListPaths = ( |
| 259 | - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-input-files.xcfilelist", | ||
| 260 | ); | 348 | ); |
| 261 | - name = "[CP] Embed Pods Frameworks"; | 349 | + inputPaths = ( |
| 350 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| 351 | + "${PODS_ROOT}/Manifest.lock", | ||
| 352 | + ); | ||
| 353 | + name = "[CP] Check Pods Manifest.lock"; | ||
| 262 | outputFileListPaths = ( | 354 | outputFileListPaths = ( |
| 263 | - "${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks-${CONFIGURATION}-output-files.xcfilelist", | 355 | + ); |
| 356 | + outputPaths = ( | ||
| 357 | + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | ||
| 264 | ); | 358 | ); |
| 265 | runOnlyForDeploymentPostprocessing = 0; | 359 | runOnlyForDeploymentPostprocessing = 0; |
| 266 | shellPath = /bin/sh; | 360 | shellPath = /bin/sh; |
| 267 | - shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | 361 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; |
| 268 | showEnvVarsInLog = 0; | 362 | showEnvVarsInLog = 0; |
| 269 | }; | 363 | }; |
| 270 | /* End PBXShellScriptBuildPhase section */ | 364 | /* End PBXShellScriptBuildPhase section */ |
| 271 | 365 | ||
| 272 | /* Begin PBXSourcesBuildPhase section */ | 366 | /* Begin PBXSourcesBuildPhase section */ |
| 367 | + 331C807D294A63A400263BE5 /* Sources */ = { | ||
| 368 | + isa = PBXSourcesBuildPhase; | ||
| 369 | + buildActionMask = 2147483647; | ||
| 370 | + files = ( | ||
| 371 | + 331C808B294A63AB00263BE5 /* RunnerTests.swift in Sources */, | ||
| 372 | + ); | ||
| 373 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 374 | + }; | ||
| 273 | 97C146EA1CF9000F007C117D /* Sources */ = { | 375 | 97C146EA1CF9000F007C117D /* Sources */ = { |
| 274 | isa = PBXSourcesBuildPhase; | 376 | isa = PBXSourcesBuildPhase; |
| 275 | buildActionMask = 2147483647; | 377 | buildActionMask = 2147483647; |
| @@ -281,6 +383,14 @@ | @@ -281,6 +383,14 @@ | ||
| 281 | }; | 383 | }; |
| 282 | /* End PBXSourcesBuildPhase section */ | 384 | /* End PBXSourcesBuildPhase section */ |
| 283 | 385 | ||
| 386 | +/* Begin PBXTargetDependency section */ | ||
| 387 | + 331C8086294A63A400263BE5 /* PBXTargetDependency */ = { | ||
| 388 | + isa = PBXTargetDependency; | ||
| 389 | + target = 97C146ED1CF9000F007C117D /* Runner */; | ||
| 390 | + targetProxy = 331C8085294A63A400263BE5 /* PBXContainerItemProxy */; | ||
| 391 | + }; | ||
| 392 | +/* End PBXTargetDependency section */ | ||
| 393 | + | ||
| 284 | /* Begin PBXVariantGroup section */ | 394 | /* Begin PBXVariantGroup section */ |
| 285 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { | 395 | 97C146FA1CF9000F007C117D /* Main.storyboard */ = { |
| 286 | isa = PBXVariantGroup; | 396 | isa = PBXVariantGroup; |
| @@ -367,7 +477,7 @@ | @@ -367,7 +477,7 @@ | ||
| 367 | "$(inherited)", | 477 | "$(inherited)", |
| 368 | "@executable_path/Frameworks", | 478 | "@executable_path/Frameworks", |
| 369 | ); | 479 | ); |
| 370 | - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; | 480 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; |
| 371 | PRODUCT_NAME = "$(TARGET_NAME)"; | 481 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 372 | PROVISIONING_PROFILE_SPECIFIER = ""; | 482 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 373 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 483 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -376,6 +486,56 @@ | @@ -376,6 +486,56 @@ | ||
| 376 | }; | 486 | }; |
| 377 | name = Profile; | 487 | name = Profile; |
| 378 | }; | 488 | }; |
| 489 | + 331C8088294A63A400263BE5 /* Debug */ = { | ||
| 490 | + isa = XCBuildConfiguration; | ||
| 491 | + baseConfigurationReference = A5A24D61913C510FB38A434E /* Pods-RunnerTests.debug.xcconfig */; | ||
| 492 | + buildSettings = { | ||
| 493 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 494 | + CODE_SIGN_STYLE = Automatic; | ||
| 495 | + CURRENT_PROJECT_VERSION = 1; | ||
| 496 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 497 | + MARKETING_VERSION = 1.0; | ||
| 498 | + PRODUCT_BUNDLE_IDENTIFIER = com.example.mobile-scanner.RunnerTests; | ||
| 499 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 500 | + SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; | ||
| 501 | + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; | ||
| 502 | + SWIFT_VERSION = 5.0; | ||
| 503 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
| 504 | + }; | ||
| 505 | + name = Debug; | ||
| 506 | + }; | ||
| 507 | + 331C8089294A63A400263BE5 /* Release */ = { | ||
| 508 | + isa = XCBuildConfiguration; | ||
| 509 | + baseConfigurationReference = ADC5B74E7E0BCF65C9CD3AA9 /* Pods-RunnerTests.release.xcconfig */; | ||
| 510 | + buildSettings = { | ||
| 511 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 512 | + CODE_SIGN_STYLE = Automatic; | ||
| 513 | + CURRENT_PROJECT_VERSION = 1; | ||
| 514 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 515 | + MARKETING_VERSION = 1.0; | ||
| 516 | + PRODUCT_BUNDLE_IDENTIFIER = com.example.mobile-scanner.RunnerTests; | ||
| 517 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 518 | + SWIFT_VERSION = 5.0; | ||
| 519 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
| 520 | + }; | ||
| 521 | + name = Release; | ||
| 522 | + }; | ||
| 523 | + 331C808A294A63A400263BE5 /* Profile */ = { | ||
| 524 | + isa = XCBuildConfiguration; | ||
| 525 | + baseConfigurationReference = 7F6B8553738879C25774B609 /* Pods-RunnerTests.profile.xcconfig */; | ||
| 526 | + buildSettings = { | ||
| 527 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 528 | + CODE_SIGN_STYLE = Automatic; | ||
| 529 | + CURRENT_PROJECT_VERSION = 1; | ||
| 530 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 531 | + MARKETING_VERSION = 1.0; | ||
| 532 | + PRODUCT_BUNDLE_IDENTIFIER = com.example.mobile-scanner.RunnerTests; | ||
| 533 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 534 | + SWIFT_VERSION = 5.0; | ||
| 535 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/Runner.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/Runner"; | ||
| 536 | + }; | ||
| 537 | + name = Profile; | ||
| 538 | + }; | ||
| 379 | 97C147031CF9000F007C117D /* Debug */ = { | 539 | 97C147031CF9000F007C117D /* Debug */ = { |
| 380 | isa = XCBuildConfiguration; | 540 | isa = XCBuildConfiguration; |
| 381 | buildSettings = { | 541 | buildSettings = { |
| @@ -499,7 +659,7 @@ | @@ -499,7 +659,7 @@ | ||
| 499 | "$(inherited)", | 659 | "$(inherited)", |
| 500 | "@executable_path/Frameworks", | 660 | "@executable_path/Frameworks", |
| 501 | ); | 661 | ); |
| 502 | - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; | 662 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; |
| 503 | PRODUCT_NAME = "$(TARGET_NAME)"; | 663 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 504 | PROVISIONING_PROFILE_SPECIFIER = ""; | 664 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 505 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 665 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -525,7 +685,7 @@ | @@ -525,7 +685,7 @@ | ||
| 525 | "$(inherited)", | 685 | "$(inherited)", |
| 526 | "@executable_path/Frameworks", | 686 | "@executable_path/Frameworks", |
| 527 | ); | 687 | ); |
| 528 | - PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample; | 688 | + PRODUCT_BUNDLE_IDENTIFIER = "com.example.mobile-scanner"; |
| 529 | PRODUCT_NAME = "$(TARGET_NAME)"; | 689 | PRODUCT_NAME = "$(TARGET_NAME)"; |
| 530 | PROVISIONING_PROFILE_SPECIFIER = ""; | 690 | PROVISIONING_PROFILE_SPECIFIER = ""; |
| 531 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; | 691 | SWIFT_OBJC_BRIDGING_HEADER = "Runner/Runner-Bridging-Header.h"; |
| @@ -537,6 +697,16 @@ | @@ -537,6 +697,16 @@ | ||
| 537 | /* End XCBuildConfiguration section */ | 697 | /* End XCBuildConfiguration section */ |
| 538 | 698 | ||
| 539 | /* Begin XCConfigurationList section */ | 699 | /* Begin XCConfigurationList section */ |
| 700 | + 331C8087294A63A400263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | ||
| 701 | + isa = XCConfigurationList; | ||
| 702 | + buildConfigurations = ( | ||
| 703 | + 331C8088294A63A400263BE5 /* Debug */, | ||
| 704 | + 331C8089294A63A400263BE5 /* Release */, | ||
| 705 | + 331C808A294A63A400263BE5 /* Profile */, | ||
| 706 | + ); | ||
| 707 | + defaultConfigurationIsVisible = 0; | ||
| 708 | + defaultConfigurationName = Release; | ||
| 709 | + }; | ||
| 540 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { | 710 | 97C146E91CF9000F007C117D /* Build configuration list for PBXProject "Runner" */ = { |
| 541 | isa = XCConfigurationList; | 711 | isa = XCConfigurationList; |
| 542 | buildConfigurations = ( | 712 | buildConfigurations = ( |
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <Scheme | 2 | <Scheme |
| 3 | - LastUpgradeVersion = "1300" | 3 | + LastUpgradeVersion = "1430" |
| 4 | version = "1.3"> | 4 | version = "1.3"> |
| 5 | <BuildAction | 5 | <BuildAction |
| 6 | parallelizeBuildables = "YES" | 6 | parallelizeBuildables = "YES" |
| @@ -37,6 +37,17 @@ | @@ -37,6 +37,17 @@ | ||
| 37 | </BuildableReference> | 37 | </BuildableReference> |
| 38 | </MacroExpansion> | 38 | </MacroExpansion> |
| 39 | <Testables> | 39 | <Testables> |
| 40 | + <TestableReference | ||
| 41 | + skipped = "NO" | ||
| 42 | + parallelizable = "YES"> | ||
| 43 | + <BuildableReference | ||
| 44 | + BuildableIdentifier = "primary" | ||
| 45 | + BlueprintIdentifier = "331C8080294A63A400263BE5" | ||
| 46 | + BuildableName = "RunnerTests.xctest" | ||
| 47 | + BlueprintName = "RunnerTests" | ||
| 48 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
| 49 | + </BuildableReference> | ||
| 50 | + </TestableReference> | ||
| 40 | </Testables> | 51 | </Testables> |
| 41 | </TestAction> | 52 | </TestAction> |
| 42 | <LaunchAction | 53 | <LaunchAction |
| @@ -2,8 +2,6 @@ | @@ -2,8 +2,6 @@ | ||
| 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> | 2 | <!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd"> |
| 3 | <plist version="1.0"> | 3 | <plist version="1.0"> |
| 4 | <dict> | 4 | <dict> |
| 5 | - <key>CADisableMinimumFrameDurationOnPhone</key> | ||
| 6 | - <true/> | ||
| 7 | <key>CFBundleDevelopmentRegion</key> | 5 | <key>CFBundleDevelopmentRegion</key> |
| 8 | <string>$(DEVELOPMENT_LANGUAGE)</string> | 6 | <string>$(DEVELOPMENT_LANGUAGE)</string> |
| 9 | <key>CFBundleDisplayName</key> | 7 | <key>CFBundleDisplayName</key> |
| @@ -27,11 +25,9 @@ | @@ -27,11 +25,9 @@ | ||
| 27 | <key>LSRequiresIPhoneOS</key> | 25 | <key>LSRequiresIPhoneOS</key> |
| 28 | <true/> | 26 | <true/> |
| 29 | <key>NSCameraUsageDescription</key> | 27 | <key>NSCameraUsageDescription</key> |
| 30 | - <string>We use the camera to scan barcodes</string> | 28 | + <string>This app needs camera access to scan QR codes</string> |
| 31 | <key>NSPhotoLibraryUsageDescription</key> | 29 | <key>NSPhotoLibraryUsageDescription</key> |
| 32 | - <string>We need access in order to open photos of barcodes</string> | ||
| 33 | - <key>UIApplicationSupportsIndirectInputEvents</key> | ||
| 34 | - <true/> | 30 | + <string>This app needs photos access to get QR code from photo library</string> |
| 35 | <key>UILaunchStoryboardName</key> | 31 | <key>UILaunchStoryboardName</key> |
| 36 | <string>LaunchScreen</string> | 32 | <string>LaunchScreen</string> |
| 37 | <key>UIMainStoryboardFile</key> | 33 | <key>UIMainStoryboardFile</key> |
| @@ -51,5 +47,9 @@ | @@ -51,5 +47,9 @@ | ||
| 51 | </array> | 47 | </array> |
| 52 | <key>UIViewControllerBasedStatusBarAppearance</key> | 48 | <key>UIViewControllerBasedStatusBarAppearance</key> |
| 53 | <false/> | 49 | <false/> |
| 50 | + <key>CADisableMinimumFrameDurationOnPhone</key> | ||
| 51 | + <true/> | ||
| 52 | + <key>UIApplicationSupportsIndirectInputEvents</key> | ||
| 53 | + <true/> | ||
| 54 | </dict> | 54 | </dict> |
| 55 | </plist> | 55 | </plist> |
example/ios/RunnerTests/RunnerTests.swift
0 → 100644
| 1 | +import Flutter | ||
| 2 | +import UIKit | ||
| 3 | +import XCTest | ||
| 4 | + | ||
| 5 | +@testable import mobile_scanner | ||
| 6 | + | ||
| 7 | +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. | ||
| 8 | +// | ||
| 9 | +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. | ||
| 10 | + | ||
| 11 | +class RunnerTests: XCTestCase { | ||
| 12 | + | ||
| 13 | + // TODO: this test was left as-is from the template, but it obviuosly fails for now. | ||
| 14 | + // Add new tests later. | ||
| 15 | + /* | ||
| 16 | + func testGetPlatformVersion() { | ||
| 17 | + let plugin = MobileScannerPlugin() | ||
| 18 | + | ||
| 19 | + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) | ||
| 20 | + | ||
| 21 | + let resultExpectation = expectation(description: "result block must be called.") | ||
| 22 | + plugin.handle(call) { result in | ||
| 23 | + XCTAssertEqual(result as! String, "iOS " + UIDevice.current.systemVersion) | ||
| 24 | + resultExpectation.fulfill() | ||
| 25 | + } | ||
| 26 | + waitForExpectations(timeout: 1) | ||
| 27 | + }*/ | ||
| 28 | + | ||
| 29 | +} |
| @@ -4,10 +4,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -4,10 +4,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 4 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 4 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 5 | 5 | ||
| 6 | class BarcodeListScannerWithController extends StatefulWidget { | 6 | class BarcodeListScannerWithController extends StatefulWidget { |
| 7 | - const BarcodeListScannerWithController({Key? key}) : super(key: key); | 7 | + const BarcodeListScannerWithController({super.key}); |
| 8 | 8 | ||
| 9 | @override | 9 | @override |
| 10 | - _BarcodeListScannerWithControllerState createState() => | 10 | + State<BarcodeListScannerWithController> createState() => |
| 11 | _BarcodeListScannerWithControllerState(); | 11 | _BarcodeListScannerWithControllerState(); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| @@ -82,16 +82,10 @@ class _BarcodeListScannerWithControllerState | @@ -82,16 +82,10 @@ class _BarcodeListScannerWithControllerState | ||
| 82 | children: [ | 82 | children: [ |
| 83 | IconButton( | 83 | IconButton( |
| 84 | color: Colors.white, | 84 | color: Colors.white, |
| 85 | - icon: ValueListenableBuilder( | 85 | + icon: ValueListenableBuilder<TorchState>( |
| 86 | valueListenable: controller.torchState, | 86 | valueListenable: controller.torchState, |
| 87 | builder: (context, state, child) { | 87 | builder: (context, state, child) { |
| 88 | - if (state == null) { | ||
| 89 | - return const Icon( | ||
| 90 | - Icons.flash_off, | ||
| 91 | - color: Colors.grey, | ||
| 92 | - ); | ||
| 93 | - } | ||
| 94 | - switch (state as TorchState) { | 88 | + switch (state) { |
| 95 | case TorchState.off: | 89 | case TorchState.off: |
| 96 | return const Icon( | 90 | return const Icon( |
| 97 | Icons.flash_off, | 91 | Icons.flash_off, |
| @@ -134,13 +128,10 @@ class _BarcodeListScannerWithControllerState | @@ -134,13 +128,10 @@ class _BarcodeListScannerWithControllerState | ||
| 134 | ), | 128 | ), |
| 135 | IconButton( | 129 | IconButton( |
| 136 | color: Colors.white, | 130 | color: Colors.white, |
| 137 | - icon: ValueListenableBuilder( | 131 | + icon: ValueListenableBuilder<CameraFacing>( |
| 138 | valueListenable: controller.cameraFacingState, | 132 | valueListenable: controller.cameraFacingState, |
| 139 | builder: (context, state, child) { | 133 | builder: (context, state, child) { |
| 140 | - if (state == null) { | ||
| 141 | - return const Icon(Icons.camera_front); | ||
| 142 | - } | ||
| 143 | - switch (state as CameraFacing) { | 134 | + switch (state) { |
| 144 | case CameraFacing.front: | 135 | case CameraFacing.front: |
| 145 | return const Icon(Icons.camera_front); | 136 | return const Icon(Icons.camera_front); |
| 146 | case CameraFacing.back: | 137 | case CameraFacing.back: |
| @@ -4,10 +4,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -4,10 +4,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 4 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 4 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 5 | 5 | ||
| 6 | class BarcodeScannerWithController extends StatefulWidget { | 6 | class BarcodeScannerWithController extends StatefulWidget { |
| 7 | - const BarcodeScannerWithController({Key? key}) : super(key: key); | 7 | + const BarcodeScannerWithController({super.key}); |
| 8 | 8 | ||
| 9 | @override | 9 | @override |
| 10 | - _BarcodeScannerWithControllerState createState() => | 10 | + State<BarcodeScannerWithController> createState() => |
| 11 | _BarcodeScannerWithControllerState(); | 11 | _BarcodeScannerWithControllerState(); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| @@ -17,7 +17,7 @@ class _BarcodeScannerWithControllerState | @@ -17,7 +17,7 @@ class _BarcodeScannerWithControllerState | ||
| 17 | BarcodeCapture? barcode; | 17 | BarcodeCapture? barcode; |
| 18 | 18 | ||
| 19 | final MobileScannerController controller = MobileScannerController( | 19 | final MobileScannerController controller = MobileScannerController( |
| 20 | - torchEnabled: true, | 20 | + torchEnabled: true, useNewCameraSelector: true, |
| 21 | // formats: [BarcodeFormat.qrCode] | 21 | // formats: [BarcodeFormat.qrCode] |
| 22 | // facing: CameraFacing.front, | 22 | // facing: CameraFacing.front, |
| 23 | // detectionSpeed: DetectionSpeed.normal | 23 | // detectionSpeed: DetectionSpeed.normal |
| @@ -47,6 +47,8 @@ class _BarcodeScannerWithControllerState | @@ -47,6 +47,8 @@ class _BarcodeScannerWithControllerState | ||
| 47 | } | 47 | } |
| 48 | } | 48 | } |
| 49 | 49 | ||
| 50 | + int? nrOfCameras; | ||
| 51 | + | ||
| 50 | @override | 52 | @override |
| 51 | Widget build(BuildContext context) { | 53 | Widget build(BuildContext context) { |
| 52 | return Scaffold( | 54 | return Scaffold( |
| @@ -57,6 +59,12 @@ class _BarcodeScannerWithControllerState | @@ -57,6 +59,12 @@ class _BarcodeScannerWithControllerState | ||
| 57 | return Stack( | 59 | return Stack( |
| 58 | children: [ | 60 | children: [ |
| 59 | MobileScanner( | 61 | MobileScanner( |
| 62 | + onScannerStarted: (arguments) { | ||
| 63 | + if (arguments?.nrOfCameras != null) { | ||
| 64 | + nrOfCameras = arguments!.nrOfCameras; | ||
| 65 | + setState(() {}); | ||
| 66 | + } | ||
| 67 | + }, | ||
| 60 | controller: controller, | 68 | controller: controller, |
| 61 | errorBuilder: (context, error, child) { | 69 | errorBuilder: (context, error, child) { |
| 62 | return ScannerErrorWidget(error: error); | 70 | return ScannerErrorWidget(error: error); |
| @@ -85,16 +93,10 @@ class _BarcodeScannerWithControllerState | @@ -85,16 +93,10 @@ class _BarcodeScannerWithControllerState | ||
| 85 | } | 93 | } |
| 86 | return IconButton( | 94 | return IconButton( |
| 87 | color: Colors.white, | 95 | color: Colors.white, |
| 88 | - icon: ValueListenableBuilder( | 96 | + icon: ValueListenableBuilder<TorchState>( |
| 89 | valueListenable: controller.torchState, | 97 | valueListenable: controller.torchState, |
| 90 | builder: (context, state, child) { | 98 | builder: (context, state, child) { |
| 91 | - if (state == null) { | ||
| 92 | - return const Icon( | ||
| 93 | - Icons.flash_off, | ||
| 94 | - color: Colors.grey, | ||
| 95 | - ); | ||
| 96 | - } | ||
| 97 | - switch (state as TorchState) { | 99 | + switch (state) { |
| 98 | case TorchState.off: | 100 | case TorchState.off: |
| 99 | return const Icon( | 101 | return const Icon( |
| 100 | Icons.flash_off, | 102 | Icons.flash_off, |
| @@ -140,13 +142,10 @@ class _BarcodeScannerWithControllerState | @@ -140,13 +142,10 @@ class _BarcodeScannerWithControllerState | ||
| 140 | ), | 142 | ), |
| 141 | IconButton( | 143 | IconButton( |
| 142 | color: Colors.white, | 144 | color: Colors.white, |
| 143 | - icon: ValueListenableBuilder( | 145 | + icon: ValueListenableBuilder<CameraFacing>( |
| 144 | valueListenable: controller.cameraFacingState, | 146 | valueListenable: controller.cameraFacingState, |
| 145 | builder: (context, state, child) { | 147 | builder: (context, state, child) { |
| 146 | - if (state == null) { | ||
| 147 | - return const Icon(Icons.camera_front); | ||
| 148 | - } | ||
| 149 | - switch (state as CameraFacing) { | 148 | + switch (state) { |
| 150 | case CameraFacing.front: | 149 | case CameraFacing.front: |
| 151 | return const Icon(Icons.camera_front); | 150 | return const Icon(Icons.camera_front); |
| 152 | case CameraFacing.back: | 151 | case CameraFacing.back: |
| @@ -155,7 +154,9 @@ class _BarcodeScannerWithControllerState | @@ -155,7 +154,9 @@ class _BarcodeScannerWithControllerState | ||
| 155 | }, | 154 | }, |
| 156 | ), | 155 | ), |
| 157 | iconSize: 32.0, | 156 | iconSize: 32.0, |
| 158 | - onPressed: () => controller.switchCamera(), | 157 | + onPressed: nrOfCameras != null && nrOfCameras! < 2 |
| 158 | + ? null | ||
| 159 | + : () => controller.switchCamera(), | ||
| 159 | ), | 160 | ), |
| 160 | IconButton( | 161 | IconButton( |
| 161 | color: Colors.white, | 162 | color: Colors.white, |
| @@ -3,10 +3,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -3,10 +3,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 3 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 3 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 4 | 4 | ||
| 5 | class BarcodeScannerPageView extends StatefulWidget { | 5 | class BarcodeScannerPageView extends StatefulWidget { |
| 6 | - const BarcodeScannerPageView({Key? key}) : super(key: key); | 6 | + const BarcodeScannerPageView({super.key}); |
| 7 | 7 | ||
| 8 | @override | 8 | @override |
| 9 | - _BarcodeScannerPageViewState createState() => _BarcodeScannerPageViewState(); | 9 | + State<BarcodeScannerPageView> createState() => _BarcodeScannerPageViewState(); |
| 10 | } | 10 | } |
| 11 | 11 | ||
| 12 | class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> | 12 | class _BarcodeScannerPageViewState extends State<BarcodeScannerPageView> |
| @@ -5,10 +5,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -5,10 +5,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 6 | 6 | ||
| 7 | class BarcodeScannerReturningImage extends StatefulWidget { | 7 | class BarcodeScannerReturningImage extends StatefulWidget { |
| 8 | - const BarcodeScannerReturningImage({Key? key}) : super(key: key); | 8 | + const BarcodeScannerReturningImage({super.key}); |
| 9 | 9 | ||
| 10 | @override | 10 | @override |
| 11 | - _BarcodeScannerReturningImageState createState() => | 11 | + State<BarcodeScannerReturningImage> createState() => |
| 12 | _BarcodeScannerReturningImageState(); | 12 | _BarcodeScannerReturningImageState(); |
| 13 | } | 13 | } |
| 14 | 14 | ||
| @@ -16,7 +16,7 @@ class _BarcodeScannerReturningImageState | @@ -16,7 +16,7 @@ class _BarcodeScannerReturningImageState | ||
| 16 | extends State<BarcodeScannerReturningImage> | 16 | extends State<BarcodeScannerReturningImage> |
| 17 | with SingleTickerProviderStateMixin { | 17 | with SingleTickerProviderStateMixin { |
| 18 | BarcodeCapture? barcode; | 18 | BarcodeCapture? barcode; |
| 19 | - MobileScannerArguments? arguments; | 19 | + // MobileScannerArguments? arguments; |
| 20 | 20 | ||
| 21 | final MobileScannerController controller = MobileScannerController( | 21 | final MobileScannerController controller = MobileScannerController( |
| 22 | torchEnabled: true, | 22 | torchEnabled: true, |
| @@ -101,16 +101,10 @@ class _BarcodeScannerReturningImageState | @@ -101,16 +101,10 @@ class _BarcodeScannerReturningImageState | ||
| 101 | children: [ | 101 | children: [ |
| 102 | IconButton( | 102 | IconButton( |
| 103 | color: Colors.white, | 103 | color: Colors.white, |
| 104 | - icon: ValueListenableBuilder( | 104 | + icon: ValueListenableBuilder<TorchState>( |
| 105 | valueListenable: controller.torchState, | 105 | valueListenable: controller.torchState, |
| 106 | builder: (context, state, child) { | 106 | builder: (context, state, child) { |
| 107 | - if (state == null) { | ||
| 108 | - return const Icon( | ||
| 109 | - Icons.flash_off, | ||
| 110 | - color: Colors.grey, | ||
| 111 | - ); | ||
| 112 | - } | ||
| 113 | - switch (state as TorchState) { | 107 | + switch (state) { |
| 114 | case TorchState.off: | 108 | case TorchState.off: |
| 115 | return const Icon( | 109 | return const Icon( |
| 116 | Icons.flash_off, | 110 | Icons.flash_off, |
| @@ -154,13 +148,10 @@ class _BarcodeScannerReturningImageState | @@ -154,13 +148,10 @@ class _BarcodeScannerReturningImageState | ||
| 154 | ), | 148 | ), |
| 155 | IconButton( | 149 | IconButton( |
| 156 | color: Colors.white, | 150 | color: Colors.white, |
| 157 | - icon: ValueListenableBuilder( | 151 | + icon: ValueListenableBuilder<CameraFacing>( |
| 158 | valueListenable: controller.cameraFacingState, | 152 | valueListenable: controller.cameraFacingState, |
| 159 | builder: (context, state, child) { | 153 | builder: (context, state, child) { |
| 160 | - if (state == null) { | ||
| 161 | - return const Icon(Icons.camera_front); | ||
| 162 | - } | ||
| 163 | - switch (state as CameraFacing) { | 154 | + switch (state) { |
| 164 | case CameraFacing.front: | 155 | case CameraFacing.front: |
| 165 | return const Icon(Icons.camera_front); | 156 | return const Icon(Icons.camera_front); |
| 166 | case CameraFacing.back: | 157 | case CameraFacing.back: |
| 1 | import 'dart:io'; | 1 | import 'dart:io'; |
| 2 | 2 | ||
| 3 | +import 'package:flutter/foundation.dart'; | ||
| 3 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 4 | import 'package:mobile_scanner/mobile_scanner.dart'; | 5 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 5 | 6 | ||
| 6 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 7 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 7 | 8 | ||
| 8 | class BarcodeScannerWithScanWindow extends StatefulWidget { | 9 | class BarcodeScannerWithScanWindow extends StatefulWidget { |
| 9 | - const BarcodeScannerWithScanWindow({Key? key}) : super(key: key); | 10 | + const BarcodeScannerWithScanWindow({super.key}); |
| 10 | 11 | ||
| 11 | @override | 12 | @override |
| 12 | - _BarcodeScannerWithScanWindowState createState() => | 13 | + State<BarcodeScannerWithScanWindow> createState() => |
| 13 | _BarcodeScannerWithScanWindowState(); | 14 | _BarcodeScannerWithScanWindowState(); |
| 14 | } | 15 | } |
| 15 | 16 | ||
| @@ -150,7 +151,10 @@ class BarcodeOverlay extends CustomPainter { | @@ -150,7 +151,10 @@ class BarcodeOverlay extends CustomPainter { | ||
| 150 | 151 | ||
| 151 | @override | 152 | @override |
| 152 | void paint(Canvas canvas, Size size) { | 153 | void paint(Canvas canvas, Size size) { |
| 153 | - if (barcode.corners == null) return; | 154 | + if (barcode.corners.isEmpty) { |
| 155 | + return; | ||
| 156 | + } | ||
| 157 | + | ||
| 154 | final adjustedSize = applyBoxFit(boxFit, arguments.size, size); | 158 | final adjustedSize = applyBoxFit(boxFit, arguments.size, size); |
| 155 | 159 | ||
| 156 | double verticalPadding = size.height - adjustedSize.destination.height; | 160 | double verticalPadding = size.height - adjustedSize.destination.height; |
| @@ -167,15 +171,19 @@ class BarcodeOverlay extends CustomPainter { | @@ -167,15 +171,19 @@ class BarcodeOverlay extends CustomPainter { | ||
| 167 | horizontalPadding = 0; | 171 | horizontalPadding = 0; |
| 168 | } | 172 | } |
| 169 | 173 | ||
| 170 | - final ratioWidth = | ||
| 171 | - (Platform.isIOS ? capture.width! : arguments.size.width) / | ||
| 172 | - adjustedSize.destination.width; | ||
| 173 | - final ratioHeight = | ||
| 174 | - (Platform.isIOS ? capture.height! : arguments.size.height) / | ||
| 175 | - adjustedSize.destination.height; | 174 | + final double ratioWidth; |
| 175 | + final double ratioHeight; | ||
| 176 | + | ||
| 177 | + if (!kIsWeb && Platform.isIOS) { | ||
| 178 | + ratioWidth = capture.size.width / adjustedSize.destination.width; | ||
| 179 | + ratioHeight = capture.size.height / adjustedSize.destination.height; | ||
| 180 | + } else { | ||
| 181 | + ratioWidth = arguments.size.width / adjustedSize.destination.width; | ||
| 182 | + ratioHeight = arguments.size.height / adjustedSize.destination.height; | ||
| 183 | + } | ||
| 176 | 184 | ||
| 177 | final List<Offset> adjustedOffset = []; | 185 | final List<Offset> adjustedOffset = []; |
| 178 | - for (final offset in barcode.corners!) { | 186 | + for (final offset in barcode.corners) { |
| 179 | adjustedOffset.add( | 187 | adjustedOffset.add( |
| 180 | Offset( | 188 | Offset( |
| 181 | offset.dx / ratioWidth + horizontalPadding, | 189 | offset.dx / ratioWidth + horizontalPadding, |
| @@ -3,10 +3,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -3,10 +3,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 3 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 3 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 4 | 4 | ||
| 5 | class BarcodeScannerWithoutController extends StatefulWidget { | 5 | class BarcodeScannerWithoutController extends StatefulWidget { |
| 6 | - const BarcodeScannerWithoutController({Key? key}) : super(key: key); | 6 | + const BarcodeScannerWithoutController({super.key}); |
| 7 | 7 | ||
| 8 | @override | 8 | @override |
| 9 | - _BarcodeScannerWithoutControllerState createState() => | 9 | + State<BarcodeScannerWithoutController> createState() => |
| 10 | _BarcodeScannerWithoutControllerState(); | 10 | _BarcodeScannerWithoutControllerState(); |
| 11 | } | 11 | } |
| 12 | 12 |
| @@ -5,10 +5,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | @@ -5,10 +5,10 @@ import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; | 5 | import 'package:mobile_scanner_example/scanner_error_widget.dart'; |
| 6 | 6 | ||
| 7 | class BarcodeScannerWithZoom extends StatefulWidget { | 7 | class BarcodeScannerWithZoom extends StatefulWidget { |
| 8 | - const BarcodeScannerWithZoom({Key? key}) : super(key: key); | 8 | + const BarcodeScannerWithZoom({super.key}); |
| 9 | 9 | ||
| 10 | @override | 10 | @override |
| 11 | - _BarcodeScannerWithZoomState createState() => _BarcodeScannerWithZoomState(); | 11 | + State<BarcodeScannerWithZoom> createState() => _BarcodeScannerWithZoomState(); |
| 12 | } | 12 | } |
| 13 | 13 | ||
| 14 | class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | 14 | class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> |
| @@ -65,16 +65,10 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | @@ -65,16 +65,10 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | ||
| 65 | children: [ | 65 | children: [ |
| 66 | IconButton( | 66 | IconButton( |
| 67 | color: Colors.white, | 67 | color: Colors.white, |
| 68 | - icon: ValueListenableBuilder( | 68 | + icon: ValueListenableBuilder<TorchState>( |
| 69 | valueListenable: controller.torchState, | 69 | valueListenable: controller.torchState, |
| 70 | builder: (context, state, child) { | 70 | builder: (context, state, child) { |
| 71 | - if (state == null) { | ||
| 72 | - return const Icon( | ||
| 73 | - Icons.flash_off, | ||
| 74 | - color: Colors.grey, | ||
| 75 | - ); | ||
| 76 | - } | ||
| 77 | - switch (state as TorchState) { | 71 | + switch (state) { |
| 78 | case TorchState.off: | 72 | case TorchState.off: |
| 79 | return const Icon( | 73 | return const Icon( |
| 80 | Icons.flash_off, | 74 | Icons.flash_off, |
| @@ -123,13 +117,10 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | @@ -123,13 +117,10 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom> | ||
| 123 | ), | 117 | ), |
| 124 | IconButton( | 118 | IconButton( |
| 125 | color: Colors.white, | 119 | color: Colors.white, |
| 126 | - icon: ValueListenableBuilder( | 120 | + icon: ValueListenableBuilder<CameraFacing>( |
| 127 | valueListenable: controller.cameraFacingState, | 121 | valueListenable: controller.cameraFacingState, |
| 128 | builder: (context, state, child) { | 122 | builder: (context, state, child) { |
| 129 | - if (state == null) { | ||
| 130 | - return const Icon(Icons.camera_front); | ||
| 131 | - } | ||
| 132 | - switch (state as CameraFacing) { | 123 | + switch (state) { |
| 133 | case CameraFacing.front: | 124 | case CameraFacing.front: |
| 134 | return const Icon(Icons.camera_front); | 125 | return const Icon(Icons.camera_front); |
| 135 | case CameraFacing.back: | 126 | case CameraFacing.back: |
| @@ -6,11 +6,12 @@ import 'package:mobile_scanner_example/barcode_scanner_returning_image.dart'; | @@ -6,11 +6,12 @@ import 'package:mobile_scanner_example/barcode_scanner_returning_image.dart'; | ||
| 6 | import 'package:mobile_scanner_example/barcode_scanner_window.dart'; | 6 | import 'package:mobile_scanner_example/barcode_scanner_window.dart'; |
| 7 | import 'package:mobile_scanner_example/barcode_scanner_without_controller.dart'; | 7 | import 'package:mobile_scanner_example/barcode_scanner_without_controller.dart'; |
| 8 | import 'package:mobile_scanner_example/barcode_scanner_zoom.dart'; | 8 | import 'package:mobile_scanner_example/barcode_scanner_zoom.dart'; |
| 9 | +import 'package:mobile_scanner_example/mobile_scanner_overlay.dart'; | ||
| 9 | 10 | ||
| 10 | void main() => runApp(const MaterialApp(home: MyHome())); | 11 | void main() => runApp(const MaterialApp(home: MyHome())); |
| 11 | 12 | ||
| 12 | class MyHome extends StatelessWidget { | 13 | class MyHome extends StatelessWidget { |
| 13 | - const MyHome({Key? key}) : super(key: key); | 14 | + const MyHome({super.key}); |
| 14 | 15 | ||
| 15 | @override | 16 | @override |
| 16 | Widget build(BuildContext context) { | 17 | Widget build(BuildContext context) { |
| @@ -95,6 +96,16 @@ class MyHome extends StatelessWidget { | @@ -95,6 +96,16 @@ class MyHome extends StatelessWidget { | ||
| 95 | }, | 96 | }, |
| 96 | child: const Text('MobileScanner pageView'), | 97 | child: const Text('MobileScanner pageView'), |
| 97 | ), | 98 | ), |
| 99 | + ElevatedButton( | ||
| 100 | + onPressed: () { | ||
| 101 | + Navigator.of(context).push( | ||
| 102 | + MaterialPageRoute( | ||
| 103 | + builder: (context) => BarcodeScannerWithOverlay(), | ||
| 104 | + ), | ||
| 105 | + ); | ||
| 106 | + }, | ||
| 107 | + child: const Text('MobileScanner with Overlay'), | ||
| 108 | + ), | ||
| 98 | ], | 109 | ], |
| 99 | ), | 110 | ), |
| 100 | ), | 111 | ), |
| 1 | -//TODO: Create example with scanner overlay | ||
| 2 | - | ||
| 3 | -// import 'dart:ui'; | ||
| 4 | -// | ||
| 5 | -// import 'package:flutter/material.dart'; | ||
| 6 | -// import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 7 | -// | ||
| 8 | -// void main() { | ||
| 9 | -// runApp(const AnalyzeView()); | ||
| 10 | -// } | ||
| 11 | -// | ||
| 12 | -// class AnalyzeView extends StatefulWidget { | ||
| 13 | -// const AnalyzeView({Key? key}) : super(key: key); | ||
| 14 | -// | ||
| 15 | -// @override | ||
| 16 | -// _AnalyzeViewState createState() => _AnalyzeViewState(); | ||
| 17 | -// } | ||
| 18 | -// | ||
| 19 | -// class _AnalyzeViewState extends State<AnalyzeView> | ||
| 20 | -// with SingleTickerProviderStateMixin { | ||
| 21 | -// List<Offset> points = []; | ||
| 22 | -// | ||
| 23 | -// // CameraController cameraController = CameraController(context, width: 320, height: 150); | ||
| 24 | -// | ||
| 25 | -// String? barcode; | ||
| 26 | -// | ||
| 27 | -// @override | ||
| 28 | -// Widget build(BuildContext context) { | ||
| 29 | -// return MaterialApp( | ||
| 30 | -// home: Scaffold( | ||
| 31 | -// body: Builder(builder: (context) { | ||
| 32 | -// return Stack( | ||
| 33 | -// children: [ | ||
| 34 | -// MobileScanner( | ||
| 35 | -// // fitScreen: false, | ||
| 36 | -// // controller: cameraController, | ||
| 37 | -// onDetect: (barcode, args) { | ||
| 38 | -// if (this.barcode != barcode.rawValue) { | ||
| 39 | -// this.barcode = barcode.rawValue; | ||
| 40 | -// if (barcode.corners != null) { | ||
| 41 | -// ScaffoldMessenger.of(context).showSnackBar(SnackBar( | ||
| 42 | -// content: Text(barcode.rawValue), | ||
| 43 | -// duration: const Duration(milliseconds: 200), | ||
| 44 | -// animation: null, | ||
| 45 | -// )); | ||
| 46 | -// setState(() { | ||
| 47 | -// final List<Offset> points = []; | ||
| 48 | -// // double factorWidth = args.size.width / 520; | ||
| 49 | -// // double factorHeight = wanted / args.size.height; | ||
| 50 | -// final size = MediaQuery.of(context).devicePixelRatio; | ||
| 51 | -// debugPrint('Size: ${barcode.corners}'); | ||
| 52 | -// for (var point in barcode.corners!) { | ||
| 53 | -// final adjustedWith = point.dx; | ||
| 54 | -// final adjustedHeight = point.dy; | ||
| 55 | -// points.add( | ||
| 56 | -// Offset(adjustedWith / size, adjustedHeight / size)); | ||
| 57 | -// // points.add(Offset((point.dx ) / size, | ||
| 58 | -// // (point.dy) / size)); | ||
| 59 | -// // final differenceWidth = (args.wantedSize!.width - args.size.width) / 2; | ||
| 60 | -// // final differenceHeight = (args.wantedSize!.height - args.size.height) / 2; | ||
| 61 | -// // points.add(Offset((point.dx + differenceWidth) / size, | ||
| 62 | -// // (point.dy + differenceHeight) / size)); | ||
| 63 | -// } | ||
| 64 | -// this.points = points; | ||
| 65 | -// }); | ||
| 66 | -// } | ||
| 67 | -// } | ||
| 68 | -// // Default 640 x480 | ||
| 69 | -// }), | ||
| 70 | -// CustomPaint( | ||
| 71 | -// painter: OpenPainter(points), | ||
| 72 | -// ), | ||
| 73 | -// // Container( | ||
| 74 | -// // alignment: Alignment.bottomCenter, | ||
| 75 | -// // margin: EdgeInsets.only(bottom: 80.0), | ||
| 76 | -// // child: IconButton( | ||
| 77 | -// // icon: ValueListenableBuilder( | ||
| 78 | -// // valueListenable: cameraController.torchState, | ||
| 79 | -// // builder: (context, state, child) { | ||
| 80 | -// // final color = | ||
| 81 | -// // state == TorchState.off ? Colors.grey : Colors.white; | ||
| 82 | -// // return Icon(Icons.bolt, color: color); | ||
| 83 | -// // }, | ||
| 84 | -// // ), | ||
| 85 | -// // iconSize: 32.0, | ||
| 86 | -// // onPressed: () => cameraController.torch(), | ||
| 87 | -// // ), | ||
| 88 | -// // ), | ||
| 89 | -// ], | ||
| 90 | -// ); | ||
| 91 | -// }), | ||
| 92 | -// ), | ||
| 93 | -// ); | ||
| 94 | -// } | ||
| 95 | -// | ||
| 96 | -// @override | ||
| 97 | -// void dispose() { | ||
| 98 | -// // cameraController.dispose(); | ||
| 99 | -// super.dispose(); | ||
| 100 | -// } | ||
| 101 | -// | ||
| 102 | -// void display(Barcode barcode) { | ||
| 103 | -// Navigator.of(context).popAndPushNamed('display', arguments: barcode); | ||
| 104 | -// } | ||
| 105 | -// } | ||
| 106 | -// | ||
| 107 | -// class OpenPainter extends CustomPainter { | ||
| 108 | -// final List<Offset> points; | ||
| 109 | -// | ||
| 110 | -// OpenPainter(this.points); | ||
| 111 | -// @override | ||
| 112 | -// void paint(Canvas canvas, Size size) { | ||
| 113 | -// var paint1 = Paint() | ||
| 114 | -// ..color = const Color(0xff63aa65) | ||
| 115 | -// ..strokeWidth = 10; | ||
| 116 | -// //draw points on canvas | ||
| 117 | -// canvas.drawPoints(PointMode.points, points, paint1); | ||
| 118 | -// } | ||
| 119 | -// | ||
| 120 | -// @override | ||
| 121 | -// bool shouldRepaint(CustomPainter oldDelegate) => true; | ||
| 122 | -// } | ||
| 123 | -// | ||
| 124 | -// class OpacityCurve extends Curve { | ||
| 125 | -// @override | ||
| 126 | -// double transform(double t) { | ||
| 127 | -// if (t < 0.1) { | ||
| 128 | -// return t * 10; | ||
| 129 | -// } else if (t <= 0.9) { | ||
| 130 | -// return 1.0; | ||
| 131 | -// } else { | ||
| 132 | -// return (1.0 - t) * 10; | ||
| 133 | -// } | ||
| 134 | -// } | ||
| 135 | -// } | ||
| 136 | -// | ||
| 137 | -// // import 'package:flutter/material.dart'; | ||
| 138 | -// // import 'package:flutter/rendering.dart'; | ||
| 139 | -// // import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 140 | -// // | ||
| 141 | -// // void main() { | ||
| 142 | -// // debugPaintSizeEnabled = false; | ||
| 143 | -// // runApp(HomePage()); | ||
| 144 | -// // } | ||
| 145 | -// // | ||
| 146 | -// // class HomePage extends StatefulWidget { | ||
| 147 | -// // @override | ||
| 148 | -// // HomeState createState() => HomeState(); | ||
| 149 | -// // } | ||
| 150 | -// // | ||
| 151 | -// // class HomeState extends State<HomePage> { | ||
| 152 | -// // @override | ||
| 153 | -// // Widget build(BuildContext context) { | ||
| 154 | -// // return MaterialApp(home: MyApp()); | ||
| 155 | -// // } | ||
| 156 | -// // } | ||
| 157 | -// // | ||
| 158 | -// // class MyApp extends StatefulWidget { | ||
| 159 | -// // @override | ||
| 160 | -// // _MyAppState createState() => _MyAppState(); | ||
| 161 | -// // } | ||
| 162 | -// // | ||
| 163 | -// // class _MyAppState extends State<MyApp> { | ||
| 164 | -// // String? qr; | ||
| 165 | -// // bool camState = false; | ||
| 166 | -// // | ||
| 167 | -// // @override | ||
| 168 | -// // initState() { | ||
| 169 | -// // super.initState(); | ||
| 170 | -// // } | ||
| 171 | -// // | ||
| 172 | -// // @override | ||
| 173 | -// // Widget build(BuildContext context) { | ||
| 174 | -// // return Scaffold( | ||
| 175 | -// // appBar: AppBar( | ||
| 176 | -// // title: Text('Plugin example app'), | ||
| 177 | -// // ), | ||
| 178 | -// // body: Center( | ||
| 179 | -// // child: Column( | ||
| 180 | -// // crossAxisAlignment: CrossAxisAlignment.center, | ||
| 181 | -// // mainAxisAlignment: MainAxisAlignment.center, | ||
| 182 | -// // children: <Widget>[ | ||
| 183 | -// // Expanded( | ||
| 184 | -// // child: camState | ||
| 185 | -// // ? Center( | ||
| 186 | -// // child: SizedBox( | ||
| 187 | -// // width: 300.0, | ||
| 188 | -// // height: 600.0, | ||
| 189 | -// // child: MobileScanner( | ||
| 190 | -// // onError: (context, error) => Text( | ||
| 191 | -// // error.toString(), | ||
| 192 | -// // style: TextStyle(color: Colors.red), | ||
| 193 | -// // ), | ||
| 194 | -// // qrCodeCallback: (code) { | ||
| 195 | -// // setState(() { | ||
| 196 | -// // qr = code; | ||
| 197 | -// // }); | ||
| 198 | -// // }, | ||
| 199 | -// // child: Container( | ||
| 200 | -// // decoration: BoxDecoration( | ||
| 201 | -// // color: Colors.transparent, | ||
| 202 | -// // border: Border.all( | ||
| 203 | -// // color: Colors.orange, | ||
| 204 | -// // width: 10.0, | ||
| 205 | -// // style: BorderStyle.solid), | ||
| 206 | -// // ), | ||
| 207 | -// // ), | ||
| 208 | -// // ), | ||
| 209 | -// // ), | ||
| 210 | -// // ) | ||
| 211 | -// // : Center(child: Text("Camera inactive"))), | ||
| 212 | -// // Text("QRCODE: $qr"), | ||
| 213 | -// // ], | ||
| 214 | -// // ), | ||
| 215 | -// // ), | ||
| 216 | -// // floatingActionButton: FloatingActionButton( | ||
| 217 | -// // child: Text( | ||
| 218 | -// // "press me", | ||
| 219 | -// // textAlign: TextAlign.center, | ||
| 220 | -// // ), | ||
| 221 | -// // onPressed: () { | ||
| 222 | -// // setState(() { | ||
| 223 | -// // camState = !camState; | ||
| 224 | -// // }); | ||
| 225 | -// // }), | ||
| 226 | -// // ); | ||
| 227 | -// // } | ||
| 228 | -// // } | 1 | +import 'package:flutter/material.dart'; |
| 2 | +import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 3 | +import 'package:mobile_scanner_example/scanner_error_widget.dart'; | ||
| 4 | + | ||
| 5 | +class BarcodeScannerWithOverlay extends StatefulWidget { | ||
| 6 | + @override | ||
| 7 | + _BarcodeScannerWithOverlayState createState() => | ||
| 8 | + _BarcodeScannerWithOverlayState(); | ||
| 9 | +} | ||
| 10 | + | ||
| 11 | +class _BarcodeScannerWithOverlayState extends State<BarcodeScannerWithOverlay> { | ||
| 12 | + String overlayText = "Please scan QR Code"; | ||
| 13 | + bool camStarted = false; | ||
| 14 | + | ||
| 15 | + final MobileScannerController controller = MobileScannerController( | ||
| 16 | + formats: const [BarcodeFormat.qrCode], | ||
| 17 | + autoStart: false, | ||
| 18 | + ); | ||
| 19 | + | ||
| 20 | + @override | ||
| 21 | + void dispose() { | ||
| 22 | + controller.dispose(); | ||
| 23 | + super.dispose(); | ||
| 24 | + } | ||
| 25 | + | ||
| 26 | + void startCamera() { | ||
| 27 | + if (camStarted) { | ||
| 28 | + return; | ||
| 29 | + } | ||
| 30 | + | ||
| 31 | + controller.start().then((_) { | ||
| 32 | + if (mounted) { | ||
| 33 | + setState(() { | ||
| 34 | + camStarted = true; | ||
| 35 | + }); | ||
| 36 | + } | ||
| 37 | + }).catchError((Object error, StackTrace stackTrace) { | ||
| 38 | + if (mounted) { | ||
| 39 | + ScaffoldMessenger.of(context).showSnackBar( | ||
| 40 | + SnackBar( | ||
| 41 | + content: Text('Something went wrong! $error'), | ||
| 42 | + backgroundColor: Colors.red, | ||
| 43 | + ), | ||
| 44 | + ); | ||
| 45 | + } | ||
| 46 | + }); | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + void onBarcodeDetect(BarcodeCapture barcodeCapture) { | ||
| 50 | + final barcode = barcodeCapture.barcodes.last; | ||
| 51 | + setState(() { | ||
| 52 | + overlayText = barcodeCapture.barcodes.last.displayValue ?? | ||
| 53 | + barcode.rawValue ?? | ||
| 54 | + 'Barcode has no displayable value'; | ||
| 55 | + }); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | + @override | ||
| 59 | + Widget build(BuildContext context) { | ||
| 60 | + final scanWindow = Rect.fromCenter( | ||
| 61 | + center: MediaQuery.of(context).size.center(Offset.zero), | ||
| 62 | + width: 200, | ||
| 63 | + height: 200, | ||
| 64 | + ); | ||
| 65 | + | ||
| 66 | + return Scaffold( | ||
| 67 | + appBar: AppBar( | ||
| 68 | + title: const Text('Scanner with Overlay Example app'), | ||
| 69 | + ), | ||
| 70 | + body: Center( | ||
| 71 | + child: Column( | ||
| 72 | + mainAxisAlignment: MainAxisAlignment.center, | ||
| 73 | + children: <Widget>[ | ||
| 74 | + Expanded( | ||
| 75 | + child: camStarted | ||
| 76 | + ? Stack( | ||
| 77 | + fit: StackFit.expand, | ||
| 78 | + children: [ | ||
| 79 | + Center( | ||
| 80 | + child: MobileScanner( | ||
| 81 | + fit: BoxFit.contain, | ||
| 82 | + onDetect: onBarcodeDetect, | ||
| 83 | + overlay: Padding( | ||
| 84 | + padding: const EdgeInsets.all(16.0), | ||
| 85 | + child: Align( | ||
| 86 | + alignment: Alignment.bottomCenter, | ||
| 87 | + child: Opacity( | ||
| 88 | + opacity: 0.7, | ||
| 89 | + child: Text( | ||
| 90 | + overlayText, | ||
| 91 | + style: const TextStyle( | ||
| 92 | + backgroundColor: Colors.black26, | ||
| 93 | + color: Colors.white, | ||
| 94 | + fontWeight: FontWeight.bold, | ||
| 95 | + fontSize: 24, | ||
| 96 | + overflow: TextOverflow.ellipsis, | ||
| 97 | + ), | ||
| 98 | + maxLines: 1, | ||
| 99 | + ), | ||
| 100 | + ), | ||
| 101 | + ), | ||
| 102 | + ), | ||
| 103 | + controller: controller, | ||
| 104 | + scanWindow: scanWindow, | ||
| 105 | + errorBuilder: (context, error, child) { | ||
| 106 | + return ScannerErrorWidget(error: error); | ||
| 107 | + }, | ||
| 108 | + ), | ||
| 109 | + ), | ||
| 110 | + CustomPaint( | ||
| 111 | + painter: ScannerOverlay(scanWindow), | ||
| 112 | + ), | ||
| 113 | + Padding( | ||
| 114 | + padding: const EdgeInsets.all(16.0), | ||
| 115 | + child: Align( | ||
| 116 | + alignment: Alignment.bottomCenter, | ||
| 117 | + child: Row( | ||
| 118 | + mainAxisAlignment: MainAxisAlignment.spaceEvenly, | ||
| 119 | + children: [ | ||
| 120 | + ValueListenableBuilder<TorchState>( | ||
| 121 | + valueListenable: controller.torchState, | ||
| 122 | + builder: (context, value, child) { | ||
| 123 | + final Color iconColor; | ||
| 124 | + | ||
| 125 | + switch (value) { | ||
| 126 | + case TorchState.off: | ||
| 127 | + iconColor = Colors.black; | ||
| 128 | + break; | ||
| 129 | + case TorchState.on: | ||
| 130 | + iconColor = Colors.yellow; | ||
| 131 | + break; | ||
| 132 | + } | ||
| 133 | + | ||
| 134 | + return IconButton( | ||
| 135 | + onPressed: () => controller.toggleTorch(), | ||
| 136 | + icon: Icon( | ||
| 137 | + Icons.flashlight_on, | ||
| 138 | + color: iconColor, | ||
| 139 | + ), | ||
| 140 | + ); | ||
| 141 | + }, | ||
| 142 | + ), | ||
| 143 | + IconButton( | ||
| 144 | + onPressed: () => controller.switchCamera(), | ||
| 145 | + icon: const Icon( | ||
| 146 | + Icons.cameraswitch_rounded, | ||
| 147 | + color: Colors.white, | ||
| 148 | + ), | ||
| 149 | + ), | ||
| 150 | + ], | ||
| 151 | + ), | ||
| 152 | + ), | ||
| 153 | + ), | ||
| 154 | + ], | ||
| 155 | + ) | ||
| 156 | + : const Center( | ||
| 157 | + child: Text("Tap on Camera to activate QR Scanner"), | ||
| 158 | + ), | ||
| 159 | + ), | ||
| 160 | + ], | ||
| 161 | + ), | ||
| 162 | + ), | ||
| 163 | + floatingActionButton: camStarted | ||
| 164 | + ? null | ||
| 165 | + : FloatingActionButton( | ||
| 166 | + onPressed: startCamera, | ||
| 167 | + child: const Icon(Icons.camera_alt), | ||
| 168 | + ), | ||
| 169 | + ); | ||
| 170 | + } | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +class ScannerOverlay extends CustomPainter { | ||
| 174 | + ScannerOverlay(this.scanWindow); | ||
| 175 | + | ||
| 176 | + final Rect scanWindow; | ||
| 177 | + final double borderRadius = 12.0; | ||
| 178 | + | ||
| 179 | + @override | ||
| 180 | + void paint(Canvas canvas, Size size) { | ||
| 181 | + final backgroundPath = Path()..addRect(Rect.largest); | ||
| 182 | + final cutoutPath = Path() | ||
| 183 | + ..addRRect( | ||
| 184 | + RRect.fromRectAndCorners( | ||
| 185 | + scanWindow, | ||
| 186 | + topLeft: Radius.circular(borderRadius), | ||
| 187 | + topRight: Radius.circular(borderRadius), | ||
| 188 | + bottomLeft: Radius.circular(borderRadius), | ||
| 189 | + bottomRight: Radius.circular(borderRadius), | ||
| 190 | + ), | ||
| 191 | + ); | ||
| 192 | + | ||
| 193 | + final backgroundPaint = Paint() | ||
| 194 | + ..color = Colors.black.withOpacity(0.5) | ||
| 195 | + ..style = PaintingStyle.fill | ||
| 196 | + ..blendMode = BlendMode.dstOut; | ||
| 197 | + | ||
| 198 | + final backgroundWithCutout = Path.combine( | ||
| 199 | + PathOperation.difference, | ||
| 200 | + backgroundPath, | ||
| 201 | + cutoutPath, | ||
| 202 | + ); | ||
| 203 | + | ||
| 204 | + // Create a Paint object for the white border | ||
| 205 | + final borderPaint = Paint() | ||
| 206 | + ..color = Colors.white | ||
| 207 | + ..style = PaintingStyle.stroke | ||
| 208 | + ..strokeWidth = 4.0; // Adjust the border width as needed | ||
| 209 | + | ||
| 210 | + // Calculate the border rectangle with rounded corners | ||
| 211 | +// Adjust the radius as needed | ||
| 212 | + final borderRect = RRect.fromRectAndCorners( | ||
| 213 | + scanWindow, | ||
| 214 | + topLeft: Radius.circular(borderRadius), | ||
| 215 | + topRight: Radius.circular(borderRadius), | ||
| 216 | + bottomLeft: Radius.circular(borderRadius), | ||
| 217 | + bottomRight: Radius.circular(borderRadius), | ||
| 218 | + ); | ||
| 219 | + | ||
| 220 | + // Draw the white border | ||
| 221 | + canvas.drawPath(backgroundWithCutout, backgroundPaint); | ||
| 222 | + canvas.drawRRect(borderRect, borderPaint); | ||
| 223 | + } | ||
| 224 | + | ||
| 225 | + @override | ||
| 226 | + bool shouldRepaint(covariant CustomPainter oldDelegate) { | ||
| 227 | + return false; | ||
| 228 | + } | ||
| 229 | +} |
| @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; | @@ -2,7 +2,7 @@ import 'package:flutter/material.dart'; | ||
| 2 | import 'package:mobile_scanner/mobile_scanner.dart'; | 2 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 3 | 3 | ||
| 4 | class ScannerErrorWidget extends StatelessWidget { | 4 | class ScannerErrorWidget extends StatelessWidget { |
| 5 | - const ScannerErrorWidget({Key? key, required this.error}) : super(key: key); | 5 | + const ScannerErrorWidget({super.key, required this.error}); |
| 6 | 6 | ||
| 7 | final MobileScannerException error; | 7 | final MobileScannerException error; |
| 8 | 8 |
| @@ -31,6 +31,9 @@ target 'Runner' do | @@ -31,6 +31,9 @@ target 'Runner' do | ||
| 31 | use_modular_headers! | 31 | use_modular_headers! |
| 32 | 32 | ||
| 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) | 33 | flutter_install_all_macos_pods File.dirname(File.realpath(__FILE__)) |
| 34 | + target 'RunnerTests' do | ||
| 35 | + inherit! :search_paths | ||
| 36 | + end | ||
| 34 | end | 37 | end |
| 35 | 38 | ||
| 36 | post_install do |installer| | 39 | post_install do |installer| |
| @@ -21,15 +21,24 @@ | @@ -21,15 +21,24 @@ | ||
| 21 | /* End PBXAggregateTarget section */ | 21 | /* End PBXAggregateTarget section */ |
| 22 | 22 | ||
| 23 | /* Begin PBXBuildFile section */ | 23 | /* Begin PBXBuildFile section */ |
| 24 | + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 331C80D7294CF71000263BE5 /* RunnerTests.swift */; }; | ||
| 24 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; | 25 | 335BBD1B22A9A15E00E9071D /* GeneratedPluginRegistrant.swift in Sources */ = {isa = PBXBuildFile; fileRef = 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */; }; |
| 25 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; | 26 | 33CC10F12044A3C60003C045 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC10F02044A3C60003C045 /* AppDelegate.swift */; }; |
| 26 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; | 27 | 33CC10F32044A3C60003C045 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F22044A3C60003C045 /* Assets.xcassets */; }; |
| 27 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; | 28 | 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; |
| 28 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; | 29 | 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; |
| 29 | - 5348E36EDC155A01222C3599 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 41950513928B2DA794C685E3 /* Pods_Runner.framework */; }; | 30 | + 58A22AC50A792ECA6D027507 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 419C3BC9593F6DE903D740F0 /* Pods_RunnerTests.framework */; }; |
| 31 | + F209F1436A19CBC32BFFB26A /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = EEB40B96A6FFC92607527710 /* Pods_Runner.framework */; }; | ||
| 30 | /* End PBXBuildFile section */ | 32 | /* End PBXBuildFile section */ |
| 31 | 33 | ||
| 32 | /* Begin PBXContainerItemProxy section */ | 34 | /* Begin PBXContainerItemProxy section */ |
| 35 | + 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */ = { | ||
| 36 | + isa = PBXContainerItemProxy; | ||
| 37 | + containerPortal = 33CC10E52044A3C60003C045 /* Project object */; | ||
| 38 | + proxyType = 1; | ||
| 39 | + remoteGlobalIDString = 33CC10EC2044A3C60003C045; | ||
| 40 | + remoteInfo = Runner; | ||
| 41 | + }; | ||
| 33 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { | 42 | 33CC111F2044C79F0003C045 /* PBXContainerItemProxy */ = { |
| 34 | isa = PBXContainerItemProxy; | 43 | isa = PBXContainerItemProxy; |
| 35 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */; | 44 | containerPortal = 33CC10E52044A3C60003C045 /* Project object */; |
| @@ -53,6 +62,9 @@ | @@ -53,6 +62,9 @@ | ||
| 53 | /* End PBXCopyFilesBuildPhase section */ | 62 | /* End PBXCopyFilesBuildPhase section */ |
| 54 | 63 | ||
| 55 | /* Begin PBXFileReference section */ | 64 | /* Begin PBXFileReference section */ |
| 65 | + 23E216AF8BD40EB8E52863BE /* Pods-RunnerTests.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.release.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 66 | + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = RunnerTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 67 | + 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = "<group>"; }; | ||
| 56 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; | 68 | 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = "<group>"; }; |
| 57 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; | 69 | 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = "<group>"; }; |
| 58 | 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile_scanner_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; | 70 | 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = mobile_scanner_example.app; sourceTree = BUILT_PRODUCTS_DIR; }; |
| @@ -67,34 +79,43 @@ | @@ -67,34 +79,43 @@ | ||
| 67 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; | 79 | 33E51913231747F40026EE4D /* DebugProfile.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = DebugProfile.entitlements; sourceTree = "<group>"; }; |
| 68 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; | 80 | 33E51914231749380026EE4D /* Release.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = Release.entitlements; sourceTree = "<group>"; }; |
| 69 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; | 81 | 33E5194F232828860026EE4D /* AppInfo.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = AppInfo.xcconfig; sourceTree = "<group>"; }; |
| 70 | - 41950513928B2DA794C685E3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 71 | - 433FCBF2B1E3F653F96B3C79 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | 82 | + 419C3BC9593F6DE903D740F0 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; |
| 72 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; | 83 | 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Release.xcconfig; sourceTree = "<group>"; }; |
| 84 | + 8201964090D9F78BC65FF5D2 /* Pods-Runner.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.profile.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.profile.xcconfig"; sourceTree = "<group>"; }; | ||
| 73 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; | 85 | 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = "<group>"; }; |
| 74 | - 97D4F0103EE99761EF216A9C /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 75 | - B67CB61EED45BF13A197D997 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | 86 | + 9AB9A83BADC7F755AA147D15 /* Pods-RunnerTests.profile.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.profile.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.profile.xcconfig"; sourceTree = "<group>"; }; |
| 87 | + AA0CC45DA8C7F4A77CA80A2E /* Pods-RunnerTests.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-RunnerTests.debug.xcconfig"; path = "Target Support Files/Pods-RunnerTests/Pods-RunnerTests.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 88 | + CAEA4D71BCF9158847C4F6AC /* Pods-Runner.release.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.release.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.release.xcconfig"; sourceTree = "<group>"; }; | ||
| 89 | + E1DC1D18B8C13B1C75763506 /* Pods-Runner.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-Runner.debug.xcconfig"; path = "Target Support Files/Pods-Runner/Pods-Runner.debug.xcconfig"; sourceTree = "<group>"; }; | ||
| 90 | + EEB40B96A6FFC92607527710 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; | ||
| 76 | /* End PBXFileReference section */ | 91 | /* End PBXFileReference section */ |
| 77 | 92 | ||
| 78 | /* Begin PBXFrameworksBuildPhase section */ | 93 | /* Begin PBXFrameworksBuildPhase section */ |
| 94 | + 331C80D2294CF70F00263BE5 /* Frameworks */ = { | ||
| 95 | + isa = PBXFrameworksBuildPhase; | ||
| 96 | + buildActionMask = 2147483647; | ||
| 97 | + files = ( | ||
| 98 | + 58A22AC50A792ECA6D027507 /* Pods_RunnerTests.framework in Frameworks */, | ||
| 99 | + ); | ||
| 100 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 101 | + }; | ||
| 79 | 33CC10EA2044A3C60003C045 /* Frameworks */ = { | 102 | 33CC10EA2044A3C60003C045 /* Frameworks */ = { |
| 80 | isa = PBXFrameworksBuildPhase; | 103 | isa = PBXFrameworksBuildPhase; |
| 81 | buildActionMask = 2147483647; | 104 | buildActionMask = 2147483647; |
| 82 | files = ( | 105 | files = ( |
| 83 | - 5348E36EDC155A01222C3599 /* Pods_Runner.framework in Frameworks */, | 106 | + F209F1436A19CBC32BFFB26A /* Pods_Runner.framework in Frameworks */, |
| 84 | ); | 107 | ); |
| 85 | runOnlyForDeploymentPostprocessing = 0; | 108 | runOnlyForDeploymentPostprocessing = 0; |
| 86 | }; | 109 | }; |
| 87 | /* End PBXFrameworksBuildPhase section */ | 110 | /* End PBXFrameworksBuildPhase section */ |
| 88 | 111 | ||
| 89 | /* Begin PBXGroup section */ | 112 | /* Begin PBXGroup section */ |
| 90 | - 20F8C9AA20C2A495C125E194 /* Pods */ = { | 113 | + 331C80D6294CF71000263BE5 /* RunnerTests */ = { |
| 91 | isa = PBXGroup; | 114 | isa = PBXGroup; |
| 92 | children = ( | 115 | children = ( |
| 93 | - B67CB61EED45BF13A197D997 /* Pods-Runner.debug.xcconfig */, | ||
| 94 | - 97D4F0103EE99761EF216A9C /* Pods-Runner.release.xcconfig */, | ||
| 95 | - 433FCBF2B1E3F653F96B3C79 /* Pods-Runner.profile.xcconfig */, | 116 | + 331C80D7294CF71000263BE5 /* RunnerTests.swift */, |
| 96 | ); | 117 | ); |
| 97 | - path = Pods; | 118 | + path = RunnerTests; |
| 98 | sourceTree = "<group>"; | 119 | sourceTree = "<group>"; |
| 99 | }; | 120 | }; |
| 100 | 33BA886A226E78AF003329D5 /* Configs */ = { | 121 | 33BA886A226E78AF003329D5 /* Configs */ = { |
| @@ -113,9 +134,10 @@ | @@ -113,9 +134,10 @@ | ||
| 113 | children = ( | 134 | children = ( |
| 114 | 33FAB671232836740065AC1E /* Runner */, | 135 | 33FAB671232836740065AC1E /* Runner */, |
| 115 | 33CEB47122A05771004F2AC0 /* Flutter */, | 136 | 33CEB47122A05771004F2AC0 /* Flutter */, |
| 137 | + 331C80D6294CF71000263BE5 /* RunnerTests */, | ||
| 116 | 33CC10EE2044A3C60003C045 /* Products */, | 138 | 33CC10EE2044A3C60003C045 /* Products */, |
| 117 | - 20F8C9AA20C2A495C125E194 /* Pods */, | ||
| 118 | - 64FFE901A03ED70F67D8DCD6 /* Frameworks */, | 139 | + D73912EC22F37F3D000D13A0 /* Frameworks */, |
| 140 | + 48C702343BA8440B9F0C4C3B /* Pods */, | ||
| 119 | ); | 141 | ); |
| 120 | sourceTree = "<group>"; | 142 | sourceTree = "<group>"; |
| 121 | }; | 143 | }; |
| @@ -123,6 +145,7 @@ | @@ -123,6 +145,7 @@ | ||
| 123 | isa = PBXGroup; | 145 | isa = PBXGroup; |
| 124 | children = ( | 146 | children = ( |
| 125 | 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */, | 147 | 33CC10ED2044A3C60003C045 /* mobile_scanner_example.app */, |
| 148 | + 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, | ||
| 126 | ); | 149 | ); |
| 127 | name = Products; | 150 | name = Products; |
| 128 | sourceTree = "<group>"; | 151 | sourceTree = "<group>"; |
| @@ -162,10 +185,25 @@ | @@ -162,10 +185,25 @@ | ||
| 162 | path = Runner; | 185 | path = Runner; |
| 163 | sourceTree = "<group>"; | 186 | sourceTree = "<group>"; |
| 164 | }; | 187 | }; |
| 165 | - 64FFE901A03ED70F67D8DCD6 /* Frameworks */ = { | 188 | + 48C702343BA8440B9F0C4C3B /* Pods */ = { |
| 166 | isa = PBXGroup; | 189 | isa = PBXGroup; |
| 167 | children = ( | 190 | children = ( |
| 168 | - 41950513928B2DA794C685E3 /* Pods_Runner.framework */, | 191 | + E1DC1D18B8C13B1C75763506 /* Pods-Runner.debug.xcconfig */, |
| 192 | + CAEA4D71BCF9158847C4F6AC /* Pods-Runner.release.xcconfig */, | ||
| 193 | + 8201964090D9F78BC65FF5D2 /* Pods-Runner.profile.xcconfig */, | ||
| 194 | + AA0CC45DA8C7F4A77CA80A2E /* Pods-RunnerTests.debug.xcconfig */, | ||
| 195 | + 23E216AF8BD40EB8E52863BE /* Pods-RunnerTests.release.xcconfig */, | ||
| 196 | + 9AB9A83BADC7F755AA147D15 /* Pods-RunnerTests.profile.xcconfig */, | ||
| 197 | + ); | ||
| 198 | + name = Pods; | ||
| 199 | + path = Pods; | ||
| 200 | + sourceTree = "<group>"; | ||
| 201 | + }; | ||
| 202 | + D73912EC22F37F3D000D13A0 /* Frameworks */ = { | ||
| 203 | + isa = PBXGroup; | ||
| 204 | + children = ( | ||
| 205 | + EEB40B96A6FFC92607527710 /* Pods_Runner.framework */, | ||
| 206 | + 419C3BC9593F6DE903D740F0 /* Pods_RunnerTests.framework */, | ||
| 169 | ); | 207 | ); |
| 170 | name = Frameworks; | 208 | name = Frameworks; |
| 171 | sourceTree = "<group>"; | 209 | sourceTree = "<group>"; |
| @@ -173,17 +211,36 @@ | @@ -173,17 +211,36 @@ | ||
| 173 | /* End PBXGroup section */ | 211 | /* End PBXGroup section */ |
| 174 | 212 | ||
| 175 | /* Begin PBXNativeTarget section */ | 213 | /* Begin PBXNativeTarget section */ |
| 214 | + 331C80D4294CF70F00263BE5 /* RunnerTests */ = { | ||
| 215 | + isa = PBXNativeTarget; | ||
| 216 | + buildConfigurationList = 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */; | ||
| 217 | + buildPhases = ( | ||
| 218 | + 26566874B4D5506B07E5CB72 /* [CP] Check Pods Manifest.lock */, | ||
| 219 | + 331C80D1294CF70F00263BE5 /* Sources */, | ||
| 220 | + 331C80D2294CF70F00263BE5 /* Frameworks */, | ||
| 221 | + 331C80D3294CF70F00263BE5 /* Resources */, | ||
| 222 | + ); | ||
| 223 | + buildRules = ( | ||
| 224 | + ); | ||
| 225 | + dependencies = ( | ||
| 226 | + 331C80DA294CF71000263BE5 /* PBXTargetDependency */, | ||
| 227 | + ); | ||
| 228 | + name = RunnerTests; | ||
| 229 | + productName = RunnerTests; | ||
| 230 | + productReference = 331C80D5294CF71000263BE5 /* RunnerTests.xctest */; | ||
| 231 | + productType = "com.apple.product-type.bundle.unit-test"; | ||
| 232 | + }; | ||
| 176 | 33CC10EC2044A3C60003C045 /* Runner */ = { | 233 | 33CC10EC2044A3C60003C045 /* Runner */ = { |
| 177 | isa = PBXNativeTarget; | 234 | isa = PBXNativeTarget; |
| 178 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; | 235 | buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; |
| 179 | buildPhases = ( | 236 | buildPhases = ( |
| 180 | - 11C0752B00246A027DB2D859 /* [CP] Check Pods Manifest.lock */, | 237 | + FF04EF1310CEA88A9000CF01 /* [CP] Check Pods Manifest.lock */, |
| 181 | 33CC10E92044A3C60003C045 /* Sources */, | 238 | 33CC10E92044A3C60003C045 /* Sources */, |
| 182 | 33CC10EA2044A3C60003C045 /* Frameworks */, | 239 | 33CC10EA2044A3C60003C045 /* Frameworks */, |
| 183 | 33CC10EB2044A3C60003C045 /* Resources */, | 240 | 33CC10EB2044A3C60003C045 /* Resources */, |
| 184 | 33CC110E2044A8840003C045 /* Bundle Framework */, | 241 | 33CC110E2044A8840003C045 /* Bundle Framework */, |
| 185 | 3399D490228B24CF009A79C7 /* ShellScript */, | 242 | 3399D490228B24CF009A79C7 /* ShellScript */, |
| 186 | - E6424ECF3C1308BAAF6E5E67 /* [CP] Embed Pods Frameworks */, | 243 | + 861067CDDCB0554984369B14 /* [CP] Embed Pods Frameworks */, |
| 187 | ); | 244 | ); |
| 188 | buildRules = ( | 245 | buildRules = ( |
| 189 | ); | 246 | ); |
| @@ -202,9 +259,13 @@ | @@ -202,9 +259,13 @@ | ||
| 202 | isa = PBXProject; | 259 | isa = PBXProject; |
| 203 | attributes = { | 260 | attributes = { |
| 204 | LastSwiftUpdateCheck = 0920; | 261 | LastSwiftUpdateCheck = 0920; |
| 205 | - LastUpgradeCheck = 1300; | 262 | + LastUpgradeCheck = 1430; |
| 206 | ORGANIZATIONNAME = ""; | 263 | ORGANIZATIONNAME = ""; |
| 207 | TargetAttributes = { | 264 | TargetAttributes = { |
| 265 | + 331C80D4294CF70F00263BE5 = { | ||
| 266 | + CreatedOnToolsVersion = 14.0; | ||
| 267 | + TestTargetID = 33CC10EC2044A3C60003C045; | ||
| 268 | + }; | ||
| 208 | 33CC10EC2044A3C60003C045 = { | 269 | 33CC10EC2044A3C60003C045 = { |
| 209 | CreatedOnToolsVersion = 9.2; | 270 | CreatedOnToolsVersion = 9.2; |
| 210 | LastSwiftMigration = 1100; | 271 | LastSwiftMigration = 1100; |
| @@ -235,12 +296,20 @@ | @@ -235,12 +296,20 @@ | ||
| 235 | projectRoot = ""; | 296 | projectRoot = ""; |
| 236 | targets = ( | 297 | targets = ( |
| 237 | 33CC10EC2044A3C60003C045 /* Runner */, | 298 | 33CC10EC2044A3C60003C045 /* Runner */, |
| 299 | + 331C80D4294CF70F00263BE5 /* RunnerTests */, | ||
| 238 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */, | 300 | 33CC111A2044C6BA0003C045 /* Flutter Assemble */, |
| 239 | ); | 301 | ); |
| 240 | }; | 302 | }; |
| 241 | /* End PBXProject section */ | 303 | /* End PBXProject section */ |
| 242 | 304 | ||
| 243 | /* Begin PBXResourcesBuildPhase section */ | 305 | /* Begin PBXResourcesBuildPhase section */ |
| 306 | + 331C80D3294CF70F00263BE5 /* Resources */ = { | ||
| 307 | + isa = PBXResourcesBuildPhase; | ||
| 308 | + buildActionMask = 2147483647; | ||
| 309 | + files = ( | ||
| 310 | + ); | ||
| 311 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 312 | + }; | ||
| 244 | 33CC10EB2044A3C60003C045 /* Resources */ = { | 313 | 33CC10EB2044A3C60003C045 /* Resources */ = { |
| 245 | isa = PBXResourcesBuildPhase; | 314 | isa = PBXResourcesBuildPhase; |
| 246 | buildActionMask = 2147483647; | 315 | buildActionMask = 2147483647; |
| @@ -253,7 +322,7 @@ | @@ -253,7 +322,7 @@ | ||
| 253 | /* End PBXResourcesBuildPhase section */ | 322 | /* End PBXResourcesBuildPhase section */ |
| 254 | 323 | ||
| 255 | /* Begin PBXShellScriptBuildPhase section */ | 324 | /* Begin PBXShellScriptBuildPhase section */ |
| 256 | - 11C0752B00246A027DB2D859 /* [CP] Check Pods Manifest.lock */ = { | 325 | + 26566874B4D5506B07E5CB72 /* [CP] Check Pods Manifest.lock */ = { |
| 257 | isa = PBXShellScriptBuildPhase; | 326 | isa = PBXShellScriptBuildPhase; |
| 258 | buildActionMask = 2147483647; | 327 | buildActionMask = 2147483647; |
| 259 | files = ( | 328 | files = ( |
| @@ -268,7 +337,7 @@ | @@ -268,7 +337,7 @@ | ||
| 268 | outputFileListPaths = ( | 337 | outputFileListPaths = ( |
| 269 | ); | 338 | ); |
| 270 | outputPaths = ( | 339 | outputPaths = ( |
| 271 | - "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | 340 | + "$(DERIVED_FILE_DIR)/Pods-RunnerTests-checkManifestLockResult.txt", |
| 272 | ); | 341 | ); |
| 273 | runOnlyForDeploymentPostprocessing = 0; | 342 | runOnlyForDeploymentPostprocessing = 0; |
| 274 | shellPath = /bin/sh; | 343 | shellPath = /bin/sh; |
| @@ -313,7 +382,7 @@ | @@ -313,7 +382,7 @@ | ||
| 313 | shellPath = /bin/sh; | 382 | shellPath = /bin/sh; |
| 314 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; | 383 | shellScript = "\"$FLUTTER_ROOT\"/packages/flutter_tools/bin/macos_assemble.sh && touch Flutter/ephemeral/tripwire"; |
| 315 | }; | 384 | }; |
| 316 | - E6424ECF3C1308BAAF6E5E67 /* [CP] Embed Pods Frameworks */ = { | 385 | + 861067CDDCB0554984369B14 /* [CP] Embed Pods Frameworks */ = { |
| 317 | isa = PBXShellScriptBuildPhase; | 386 | isa = PBXShellScriptBuildPhase; |
| 318 | buildActionMask = 2147483647; | 387 | buildActionMask = 2147483647; |
| 319 | files = ( | 388 | files = ( |
| @@ -330,9 +399,39 @@ | @@ -330,9 +399,39 @@ | ||
| 330 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; | 399 | shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-Runner/Pods-Runner-frameworks.sh\"\n"; |
| 331 | showEnvVarsInLog = 0; | 400 | showEnvVarsInLog = 0; |
| 332 | }; | 401 | }; |
| 402 | + FF04EF1310CEA88A9000CF01 /* [CP] Check Pods Manifest.lock */ = { | ||
| 403 | + isa = PBXShellScriptBuildPhase; | ||
| 404 | + buildActionMask = 2147483647; | ||
| 405 | + files = ( | ||
| 406 | + ); | ||
| 407 | + inputFileListPaths = ( | ||
| 408 | + ); | ||
| 409 | + inputPaths = ( | ||
| 410 | + "${PODS_PODFILE_DIR_PATH}/Podfile.lock", | ||
| 411 | + "${PODS_ROOT}/Manifest.lock", | ||
| 412 | + ); | ||
| 413 | + name = "[CP] Check Pods Manifest.lock"; | ||
| 414 | + outputFileListPaths = ( | ||
| 415 | + ); | ||
| 416 | + outputPaths = ( | ||
| 417 | + "$(DERIVED_FILE_DIR)/Pods-Runner-checkManifestLockResult.txt", | ||
| 418 | + ); | ||
| 419 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 420 | + shellPath = /bin/sh; | ||
| 421 | + shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; | ||
| 422 | + showEnvVarsInLog = 0; | ||
| 423 | + }; | ||
| 333 | /* End PBXShellScriptBuildPhase section */ | 424 | /* End PBXShellScriptBuildPhase section */ |
| 334 | 425 | ||
| 335 | /* Begin PBXSourcesBuildPhase section */ | 426 | /* Begin PBXSourcesBuildPhase section */ |
| 427 | + 331C80D1294CF70F00263BE5 /* Sources */ = { | ||
| 428 | + isa = PBXSourcesBuildPhase; | ||
| 429 | + buildActionMask = 2147483647; | ||
| 430 | + files = ( | ||
| 431 | + 331C80D8294CF71000263BE5 /* RunnerTests.swift in Sources */, | ||
| 432 | + ); | ||
| 433 | + runOnlyForDeploymentPostprocessing = 0; | ||
| 434 | + }; | ||
| 336 | 33CC10E92044A3C60003C045 /* Sources */ = { | 435 | 33CC10E92044A3C60003C045 /* Sources */ = { |
| 337 | isa = PBXSourcesBuildPhase; | 436 | isa = PBXSourcesBuildPhase; |
| 338 | buildActionMask = 2147483647; | 437 | buildActionMask = 2147483647; |
| @@ -346,6 +445,11 @@ | @@ -346,6 +445,11 @@ | ||
| 346 | /* End PBXSourcesBuildPhase section */ | 445 | /* End PBXSourcesBuildPhase section */ |
| 347 | 446 | ||
| 348 | /* Begin PBXTargetDependency section */ | 447 | /* Begin PBXTargetDependency section */ |
| 448 | + 331C80DA294CF71000263BE5 /* PBXTargetDependency */ = { | ||
| 449 | + isa = PBXTargetDependency; | ||
| 450 | + target = 33CC10EC2044A3C60003C045 /* Runner */; | ||
| 451 | + targetProxy = 331C80D9294CF71000263BE5 /* PBXContainerItemProxy */; | ||
| 452 | + }; | ||
| 349 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { | 453 | 33CC11202044C79F0003C045 /* PBXTargetDependency */ = { |
| 350 | isa = PBXTargetDependency; | 454 | isa = PBXTargetDependency; |
| 351 | target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; | 455 | target = 33CC111A2044C6BA0003C045 /* Flutter Assemble */; |
| @@ -366,6 +470,51 @@ | @@ -366,6 +470,51 @@ | ||
| 366 | /* End PBXVariantGroup section */ | 470 | /* End PBXVariantGroup section */ |
| 367 | 471 | ||
| 368 | /* Begin XCBuildConfiguration section */ | 472 | /* Begin XCBuildConfiguration section */ |
| 473 | + 331C80DB294CF71000263BE5 /* Debug */ = { | ||
| 474 | + isa = XCBuildConfiguration; | ||
| 475 | + baseConfigurationReference = AA0CC45DA8C7F4A77CA80A2E /* Pods-RunnerTests.debug.xcconfig */; | ||
| 476 | + buildSettings = { | ||
| 477 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 478 | + CURRENT_PROJECT_VERSION = 1; | ||
| 479 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 480 | + MARKETING_VERSION = 1.0; | ||
| 481 | + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample.RunnerTests; | ||
| 482 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 483 | + SWIFT_VERSION = 5.0; | ||
| 484 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile_scanner_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mobile_scanner_example"; | ||
| 485 | + }; | ||
| 486 | + name = Debug; | ||
| 487 | + }; | ||
| 488 | + 331C80DC294CF71000263BE5 /* Release */ = { | ||
| 489 | + isa = XCBuildConfiguration; | ||
| 490 | + baseConfigurationReference = 23E216AF8BD40EB8E52863BE /* Pods-RunnerTests.release.xcconfig */; | ||
| 491 | + buildSettings = { | ||
| 492 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 493 | + CURRENT_PROJECT_VERSION = 1; | ||
| 494 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 495 | + MARKETING_VERSION = 1.0; | ||
| 496 | + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample.RunnerTests; | ||
| 497 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 498 | + SWIFT_VERSION = 5.0; | ||
| 499 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile_scanner_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mobile_scanner_example"; | ||
| 500 | + }; | ||
| 501 | + name = Release; | ||
| 502 | + }; | ||
| 503 | + 331C80DD294CF71000263BE5 /* Profile */ = { | ||
| 504 | + isa = XCBuildConfiguration; | ||
| 505 | + baseConfigurationReference = 9AB9A83BADC7F755AA147D15 /* Pods-RunnerTests.profile.xcconfig */; | ||
| 506 | + buildSettings = { | ||
| 507 | + BUNDLE_LOADER = "$(TEST_HOST)"; | ||
| 508 | + CURRENT_PROJECT_VERSION = 1; | ||
| 509 | + GENERATE_INFOPLIST_FILE = YES; | ||
| 510 | + MARKETING_VERSION = 1.0; | ||
| 511 | + PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample.RunnerTests; | ||
| 512 | + PRODUCT_NAME = "$(TARGET_NAME)"; | ||
| 513 | + SWIFT_VERSION = 5.0; | ||
| 514 | + TEST_HOST = "$(BUILT_PRODUCTS_DIR)/mobile_scanner_example.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/mobile_scanner_example"; | ||
| 515 | + }; | ||
| 516 | + name = Profile; | ||
| 517 | + }; | ||
| 369 | 338D0CE9231458BD00FA5F75 /* Profile */ = { | 518 | 338D0CE9231458BD00FA5F75 /* Profile */ = { |
| 370 | isa = XCBuildConfiguration; | 519 | isa = XCBuildConfiguration; |
| 371 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; | 520 | baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; |
| @@ -599,6 +748,16 @@ | @@ -599,6 +748,16 @@ | ||
| 599 | /* End XCBuildConfiguration section */ | 748 | /* End XCBuildConfiguration section */ |
| 600 | 749 | ||
| 601 | /* Begin XCConfigurationList section */ | 750 | /* Begin XCConfigurationList section */ |
| 751 | + 331C80DE294CF71000263BE5 /* Build configuration list for PBXNativeTarget "RunnerTests" */ = { | ||
| 752 | + isa = XCConfigurationList; | ||
| 753 | + buildConfigurations = ( | ||
| 754 | + 331C80DB294CF71000263BE5 /* Debug */, | ||
| 755 | + 331C80DC294CF71000263BE5 /* Release */, | ||
| 756 | + 331C80DD294CF71000263BE5 /* Profile */, | ||
| 757 | + ); | ||
| 758 | + defaultConfigurationIsVisible = 0; | ||
| 759 | + defaultConfigurationName = Release; | ||
| 760 | + }; | ||
| 602 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { | 761 | 33CC10E82044A3C60003C045 /* Build configuration list for PBXProject "Runner" */ = { |
| 603 | isa = XCConfigurationList; | 762 | isa = XCConfigurationList; |
| 604 | buildConfigurations = ( | 763 | buildConfigurations = ( |
| 1 | <?xml version="1.0" encoding="UTF-8"?> | 1 | <?xml version="1.0" encoding="UTF-8"?> |
| 2 | <Scheme | 2 | <Scheme |
| 3 | - LastUpgradeVersion = "1300" | 3 | + LastUpgradeVersion = "1430" |
| 4 | version = "1.3"> | 4 | version = "1.3"> |
| 5 | <BuildAction | 5 | <BuildAction |
| 6 | parallelizeBuildables = "YES" | 6 | parallelizeBuildables = "YES" |
| @@ -37,6 +37,17 @@ | @@ -37,6 +37,17 @@ | ||
| 37 | </BuildableReference> | 37 | </BuildableReference> |
| 38 | </MacroExpansion> | 38 | </MacroExpansion> |
| 39 | <Testables> | 39 | <Testables> |
| 40 | + <TestableReference | ||
| 41 | + skipped = "NO" | ||
| 42 | + parallelizable = "YES"> | ||
| 43 | + <BuildableReference | ||
| 44 | + BuildableIdentifier = "primary" | ||
| 45 | + BlueprintIdentifier = "331C80D4294CF70F00263BE5" | ||
| 46 | + BuildableName = "RunnerTests.xctest" | ||
| 47 | + BlueprintName = "RunnerTests" | ||
| 48 | + ReferencedContainer = "container:Runner.xcodeproj"> | ||
| 49 | + </BuildableReference> | ||
| 50 | + </TestableReference> | ||
| 40 | </Testables> | 51 | </Testables> |
| 41 | </TestAction> | 52 | </TestAction> |
| 42 | <LaunchAction | 53 | <LaunchAction |
| @@ -11,4 +11,4 @@ PRODUCT_NAME = mobile_scanner_example | @@ -11,4 +11,4 @@ PRODUCT_NAME = mobile_scanner_example | ||
| 11 | PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample | 11 | PRODUCT_BUNDLE_IDENTIFIER = dev.steenbakker.mobileScannerExample |
| 12 | 12 | ||
| 13 | // The copyright displayed in application information | 13 | // The copyright displayed in application information |
| 14 | -PRODUCT_COPYRIGHT = Copyright © 2022 dev.steenbakker. All rights reserved. | 14 | +PRODUCT_COPYRIGHT = Copyright © 2023 dev.steenbakker. All rights reserved. |
| @@ -3,7 +3,7 @@ import FlutterMacOS | @@ -3,7 +3,7 @@ import FlutterMacOS | ||
| 3 | 3 | ||
| 4 | class MainFlutterWindow: NSWindow { | 4 | class MainFlutterWindow: NSWindow { |
| 5 | override func awakeFromNib() { | 5 | override func awakeFromNib() { |
| 6 | - let flutterViewController = FlutterViewController.init() | 6 | + let flutterViewController = FlutterViewController() |
| 7 | let windowFrame = self.frame | 7 | let windowFrame = self.frame |
| 8 | self.contentViewController = flutterViewController | 8 | self.contentViewController = flutterViewController |
| 9 | self.setFrame(windowFrame, display: true) | 9 | self.setFrame(windowFrame, display: true) |
example/macos/RunnerTests/RunnerTests.swift
0 → 100644
| 1 | +import FlutterMacOS | ||
| 2 | +import Cocoa | ||
| 3 | +import XCTest | ||
| 4 | + | ||
| 5 | +@testable import mobile_scanner | ||
| 6 | + | ||
| 7 | +// This demonstrates a simple unit test of the Swift portion of this plugin's implementation. | ||
| 8 | +// | ||
| 9 | +// See https://developer.apple.com/documentation/xctest for more information about using XCTest. | ||
| 10 | + | ||
| 11 | +class RunnerTests: XCTestCase { | ||
| 12 | + | ||
| 13 | + // TODO: this test was left as-is from the template, but it obviuosly fails for now. | ||
| 14 | + // Add new tests later. | ||
| 15 | + /* | ||
| 16 | + func testGetPlatformVersion() { | ||
| 17 | + let plugin = MobileScannerPlugin() | ||
| 18 | + | ||
| 19 | + let call = FlutterMethodCall(methodName: "getPlatformVersion", arguments: []) | ||
| 20 | + | ||
| 21 | + let resultExpectation = expectation(description: "result block must be called.") | ||
| 22 | + plugin.handle(call) { result in | ||
| 23 | + XCTAssertEqual(result as! String, | ||
| 24 | + "macOS " + ProcessInfo.processInfo.operatingSystemVersionString) | ||
| 25 | + resultExpectation.fulfill() | ||
| 26 | + } | ||
| 27 | + waitForExpectations(timeout: 1) | ||
| 28 | + }*/ | ||
| 29 | + | ||
| 30 | +} |
| 1 | name: mobile_scanner_example | 1 | name: mobile_scanner_example |
| 2 | description: Demonstrates how to use the mobile_scanner plugin. | 2 | description: Demonstrates how to use the mobile_scanner plugin. |
| 3 | +# The following line prevents the package from being accidentally published to | ||
| 4 | +# pub.dev using `flutter pub publish`. This is preferred for private packages. | ||
| 3 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev | 5 | publish_to: 'none' # Remove this line if you wish to publish to pub.dev |
| 6 | +version: 0.0.1 | ||
| 4 | 7 | ||
| 5 | environment: | 8 | environment: |
| 6 | - sdk: ">=2.12.0 <4.0.0" | 9 | + sdk: '>=2.18.0 <4.0.0' |
| 7 | 10 | ||
| 11 | +# Dependencies specify other packages that your package needs in order to work. | ||
| 12 | +# To automatically upgrade your package dependencies to the latest versions | ||
| 13 | +# consider running `flutter pub upgrade --major-versions`. Alternatively, | ||
| 14 | +# dependencies can be manually updated by changing the version numbers below to | ||
| 15 | +# the latest version available on pub.dev. To see which dependencies have newer | ||
| 16 | +# versions available, run `flutter pub outdated`. | ||
| 8 | dependencies: | 17 | dependencies: |
| 9 | flutter: | 18 | flutter: |
| 10 | sdk: flutter | 19 | sdk: flutter |
| 11 | - image_picker: ^1.0.0 | ||
| 12 | 20 | ||
| 21 | + image_picker: ^1.0.4 | ||
| 13 | mobile_scanner: | 22 | mobile_scanner: |
| 23 | + # When depending on this package from a real application you should use: | ||
| 24 | + # mobile_scanner: ^x.y.z | ||
| 25 | + # See https://dart.dev/tools/pub/dependencies#version-constraints | ||
| 26 | + # The example app is bundled with the plugin so we use a path dependency on | ||
| 27 | + # the parent directory to use the current plugin's version. | ||
| 14 | path: ../ | 28 | path: ../ |
| 15 | 29 | ||
| 16 | dev_dependencies: | 30 | dev_dependencies: |
| 17 | flutter_test: | 31 | flutter_test: |
| 18 | sdk: flutter | 32 | sdk: flutter |
| 19 | - lint: ^2.0.1 | 33 | + integration_test: |
| 34 | + sdk: flutter | ||
| 35 | + lint: ^2.1.2 | ||
| 20 | 36 | ||
| 21 | flutter: | 37 | flutter: |
| 22 | uses-material-design: true | 38 | uses-material-design: true |
example/test/widget_test.dart
deleted
100644 → 0
| 1 | -// This is a basic Flutter widget test. | ||
| 2 | -// | ||
| 3 | -// To perform an interaction with a widget in your test, use the WidgetTester | ||
| 4 | -// utility that Flutter provides. For example, you can send tap and scroll | ||
| 5 | -// gestures. You can also use WidgetTester to find child widgets in the widget | ||
| 6 | -// tree, read text, and verify that the values of widget properties are correct. | ||
| 7 | - | ||
| 8 | -import 'package:flutter_test/flutter_test.dart'; | ||
| 9 | - | ||
| 10 | -void main() { | ||
| 11 | - testWidgets('Verify Platform version', (WidgetTester tester) async { | ||
| 12 | - // Build our app and trigger a frame. | ||
| 13 | - }); | ||
| 14 | -} |
| @@ -34,7 +34,7 @@ | @@ -34,7 +34,7 @@ | ||
| 34 | 34 | ||
| 35 | <script> | 35 | <script> |
| 36 | // The value below is injected by flutter build, do not touch. | 36 | // The value below is injected by flutter build, do not touch. |
| 37 | - var serviceWorkerVersion = null; | 37 | + const serviceWorkerVersion = null; |
| 38 | </script> | 38 | </script> |
| 39 | <!-- This script adds the flutter initialization JS code --> | 39 | <!-- This script adds the flutter initialization JS code --> |
| 40 | <script src="flutter.js" defer></script> | 40 | <script src="flutter.js" defer></script> |
| @@ -5,12 +5,10 @@ | @@ -5,12 +5,10 @@ | ||
| 5 | // Created by Julian Steenbakker on 24/08/2022. | 5 | // Created by Julian Steenbakker on 24/08/2022. |
| 6 | // | 6 | // |
| 7 | 7 | ||
| 8 | +import Flutter | ||
| 8 | import Foundation | 9 | import Foundation |
| 9 | 10 | ||
| 10 | public class BarcodeHandler: NSObject, FlutterStreamHandler { | 11 | public class BarcodeHandler: NSObject, FlutterStreamHandler { |
| 11 | - | ||
| 12 | - var event: [String: Any?] = [:] | ||
| 13 | - | ||
| 14 | private var eventSink: FlutterEventSink? | 12 | private var eventSink: FlutterEventSink? |
| 15 | private let eventChannel: FlutterEventChannel | 13 | private let eventChannel: FlutterEventChannel |
| 16 | 14 | ||
| @@ -22,8 +20,9 @@ public class BarcodeHandler: NSObject, FlutterStreamHandler { | @@ -22,8 +20,9 @@ public class BarcodeHandler: NSObject, FlutterStreamHandler { | ||
| 22 | } | 20 | } |
| 23 | 21 | ||
| 24 | func publishEvent(_ event: [String: Any?]) { | 22 | func publishEvent(_ event: [String: Any?]) { |
| 25 | - self.event = event | ||
| 26 | - eventSink?(event) | 23 | + DispatchQueue.main.async { |
| 24 | + self.eventSink?(event) | ||
| 25 | + } | ||
| 27 | } | 26 | } |
| 28 | 27 | ||
| 29 | public func onListen(withArguments arguments: Any?, | 28 | public func onListen(withArguments arguments: Any?, |
| 1 | // | 1 | // |
| 2 | -// SwiftMobileScanner.swift | 2 | +// MobileScanner.swift |
| 3 | // mobile_scanner | 3 | // mobile_scanner |
| 4 | // | 4 | // |
| 5 | // Created by Julian Steenbakker on 15/02/2022. | 5 | // Created by Julian Steenbakker on 15/02/2022. |
| 6 | // | 6 | // |
| 7 | 7 | ||
| 8 | import Foundation | 8 | import Foundation |
| 9 | - | 9 | +import Flutter |
| 10 | import AVFoundation | 10 | import AVFoundation |
| 11 | import MLKitVision | 11 | import MLKitVision |
| 12 | import MLKitBarcodeScanning | 12 | import MLKitBarcodeScanning |
| @@ -55,6 +55,12 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -55,6 +55,12 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 55 | 55 | ||
| 56 | var standardZoomFactor: CGFloat = 1 | 56 | var standardZoomFactor: CGFloat = 1 |
| 57 | 57 | ||
| 58 | + private var nextScanTime = 0.0 | ||
| 59 | + | ||
| 60 | + private var imagesCurrentlyBeingProcessed = false | ||
| 61 | + | ||
| 62 | + public var timeoutSeconds: Double = 0 | ||
| 63 | + | ||
| 58 | init(registry: FlutterTextureRegistry?, mobileScannerCallback: @escaping MobileScannerCallback, torchModeChangeCallback: @escaping TorchModeChangeCallback, zoomScaleChangeCallback: @escaping ZoomScaleChangeCallback) { | 64 | init(registry: FlutterTextureRegistry?, mobileScannerCallback: @escaping MobileScannerCallback, torchModeChangeCallback: @escaping TorchModeChangeCallback, zoomScaleChangeCallback: @escaping ZoomScaleChangeCallback) { |
| 59 | self.registry = registry | 65 | self.registry = registry |
| 60 | self.mobileScannerCallback = mobileScannerCallback | 66 | self.mobileScannerCallback = mobileScannerCallback |
| @@ -89,8 +95,15 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -89,8 +95,15 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 89 | } | 95 | } |
| 90 | latestBuffer = imageBuffer | 96 | latestBuffer = imageBuffer |
| 91 | registry?.textureFrameAvailable(textureId) | 97 | registry?.textureFrameAvailable(textureId) |
| 92 | - if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && i > 10 || detectionSpeed == DetectionSpeed.unrestricted) { | ||
| 93 | - i = 0 | 98 | + |
| 99 | + let currentTime = Date().timeIntervalSince1970 | ||
| 100 | + let eligibleForScan = currentTime > nextScanTime && !imagesCurrentlyBeingProcessed | ||
| 101 | + | ||
| 102 | + if ((detectionSpeed == DetectionSpeed.normal || detectionSpeed == DetectionSpeed.noDuplicates) && eligibleForScan || detectionSpeed == DetectionSpeed.unrestricted) { | ||
| 103 | + | ||
| 104 | + nextScanTime = currentTime + timeoutSeconds | ||
| 105 | + imagesCurrentlyBeingProcessed = true | ||
| 106 | + | ||
| 94 | let ciImage = latestBuffer.image | 107 | let ciImage = latestBuffer.image |
| 95 | 108 | ||
| 96 | let image = VisionImage(image: ciImage) | 109 | let image = VisionImage(image: ciImage) |
| @@ -101,31 +114,33 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -101,31 +114,33 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 101 | ) | 114 | ) |
| 102 | 115 | ||
| 103 | scanner.process(image) { [self] barcodes, error in | 116 | scanner.process(image) { [self] barcodes, error in |
| 117 | + imagesCurrentlyBeingProcessed = false | ||
| 118 | + | ||
| 104 | if (detectionSpeed == DetectionSpeed.noDuplicates) { | 119 | if (detectionSpeed == DetectionSpeed.noDuplicates) { |
| 105 | - let newScannedBarcodes = barcodes?.map { barcode in | 120 | + let newScannedBarcodes = barcodes?.compactMap({ barcode in |
| 106 | return barcode.rawValue | 121 | return barcode.rawValue |
| 107 | - } | 122 | + }).sorted() |
| 123 | + | ||
| 108 | if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) { | 124 | if (error == nil && barcodesString != nil && newScannedBarcodes != nil && barcodesString!.elementsEqual(newScannedBarcodes!)) { |
| 109 | return | 125 | return |
| 110 | - } else { | 126 | + } else if (newScannedBarcodes?.isEmpty == false) { |
| 111 | barcodesString = newScannedBarcodes | 127 | barcodesString = newScannedBarcodes |
| 112 | } | 128 | } |
| 113 | } | 129 | } |
| 114 | 130 | ||
| 115 | mobileScannerCallback(barcodes, error, ciImage) | 131 | mobileScannerCallback(barcodes, error, ciImage) |
| 116 | } | 132 | } |
| 117 | - } else { | ||
| 118 | - i+=1 | ||
| 119 | } | 133 | } |
| 120 | } | 134 | } |
| 121 | 135 | ||
| 122 | /// Start scanning for barcodes | 136 | /// Start scanning for barcodes |
| 123 | - func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: AVCaptureDevice.TorchMode, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { | 137 | + func start(barcodeScannerOptions: BarcodeScannerOptions?, returnImage: Bool, cameraPosition: AVCaptureDevice.Position, torch: Bool, detectionSpeed: DetectionSpeed, completion: @escaping (MobileScannerStartParameters) -> ()) throws { |
| 124 | self.detectionSpeed = detectionSpeed | 138 | self.detectionSpeed = detectionSpeed |
| 125 | if (device != nil) { | 139 | if (device != nil) { |
| 126 | throw MobileScannerError.alreadyStarted | 140 | throw MobileScannerError.alreadyStarted |
| 127 | } | 141 | } |
| 128 | 142 | ||
| 143 | + barcodesString = nil | ||
| 129 | scanner = barcodeScannerOptions != nil ? BarcodeScanner.barcodeScanner(options: barcodeScannerOptions!) : BarcodeScanner.barcodeScanner() | 144 | scanner = barcodeScannerOptions != nil ? BarcodeScanner.barcodeScanner(options: barcodeScannerOptions!) : BarcodeScanner.barcodeScanner() |
| 130 | captureSession = AVCaptureSession() | 145 | captureSession = AVCaptureSession() |
| 131 | textureId = registry?.register(self) | 146 | textureId = registry?.register(self) |
| @@ -178,7 +193,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -178,7 +193,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 178 | throw MobileScannerError.cameraError(error) | 193 | throw MobileScannerError.cameraError(error) |
| 179 | } | 194 | } |
| 180 | 195 | ||
| 181 | - captureSession.sessionPreset = AVCaptureSession.Preset.photo; | 196 | + captureSession.sessionPreset = AVCaptureSession.Preset.photo |
| 182 | // Add video output. | 197 | // Add video output. |
| 183 | let videoOutput = AVCaptureVideoDataOutput() | 198 | let videoOutput = AVCaptureVideoDataOutput() |
| 184 | 199 | ||
| @@ -200,32 +215,43 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -200,32 +215,43 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 200 | 215 | ||
| 201 | backgroundQueue.async { | 216 | backgroundQueue.async { |
| 202 | self.captureSession.startRunning() | 217 | self.captureSession.startRunning() |
| 203 | - // Enable the torch if parameter is set and torch is available | ||
| 204 | - // torch should be set after 'startRunning' is called | 218 | + |
| 219 | + // Turn on the flashlight if requested, | ||
| 220 | + // but after the capture session started. | ||
| 221 | + if (torch) { | ||
| 205 | do { | 222 | do { |
| 206 | - try self.toggleTorch(torch) | 223 | + try self.toggleTorch(.on) |
| 207 | } catch { | 224 | } catch { |
| 208 | - print("Failed to set initial torch state.") | 225 | + // If the torch does not turn on, |
| 226 | + // continue with the capture session anyway. | ||
| 227 | + } | ||
| 209 | } | 228 | } |
| 210 | 229 | ||
| 211 | do { | 230 | do { |
| 212 | try self.resetScale() | 231 | try self.resetScale() |
| 213 | } catch { | 232 | } catch { |
| 214 | - print("Failed to reset zoom scale") | 233 | + // If the zoom scale could not be reset, |
| 234 | + // continue with the capture session anyway. | ||
| 215 | } | 235 | } |
| 216 | 236 | ||
| 217 | - let dimensions = CMVideoFormatDescriptionGetDimensions(self.device.activeFormat.formatDescription) | 237 | + if let device = self.device { |
| 238 | + let dimensions = CMVideoFormatDescriptionGetDimensions( | ||
| 239 | + device.activeFormat.formatDescription) | ||
| 240 | + let hasTorch = device.hasTorch | ||
| 218 | 241 | ||
| 219 | - DispatchQueue.main.async { | ||
| 220 | completion( | 242 | completion( |
| 221 | MobileScannerStartParameters( | 243 | MobileScannerStartParameters( |
| 222 | width: Double(dimensions.height), | 244 | width: Double(dimensions.height), |
| 223 | height: Double(dimensions.width), | 245 | height: Double(dimensions.width), |
| 224 | - hasTorch: self.device.hasTorch, | ||
| 225 | - textureId: self.textureId | 246 | + hasTorch: hasTorch, |
| 247 | + textureId: self.textureId ?? 0 | ||
| 226 | ) | 248 | ) |
| 227 | ) | 249 | ) |
| 250 | + | ||
| 251 | + return | ||
| 228 | } | 252 | } |
| 253 | + | ||
| 254 | + completion(MobileScannerStartParameters()) | ||
| 229 | } | 255 | } |
| 230 | } | 256 | } |
| 231 | 257 | ||
| @@ -251,19 +277,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -251,19 +277,16 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 251 | device = nil | 277 | device = nil |
| 252 | } | 278 | } |
| 253 | 279 | ||
| 254 | - /// Toggle the flashlight between on and off | 280 | + /// Toggle the flashlight between on and off. |
| 255 | func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws { | 281 | func toggleTorch(_ torch: AVCaptureDevice.TorchMode) throws { |
| 256 | - if (device == nil) { | ||
| 257 | - throw MobileScannerError.torchWhenStopped | 282 | + if (device == nil || !device.hasTorch || !device.isTorchAvailable) { |
| 283 | + return | ||
| 258 | } | 284 | } |
| 259 | - if (device.hasTorch && device.isTorchAvailable) { | ||
| 260 | - do { | 285 | + |
| 286 | + if (device.torchMode != torch) { | ||
| 261 | try device.lockForConfiguration() | 287 | try device.lockForConfiguration() |
| 262 | device.torchMode = torch | 288 | device.torchMode = torch |
| 263 | device.unlockForConfiguration() | 289 | device.unlockForConfiguration() |
| 264 | - } catch { | ||
| 265 | - throw MobileScannerError.torchError(error) | ||
| 266 | - } | ||
| 267 | } | 290 | } |
| 268 | } | 291 | } |
| 269 | 292 | ||
| @@ -271,7 +294,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -271,7 +294,7 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 271 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { | 294 | public override func observeValue(forKeyPath keyPath: String?, of object: Any?, change: [NSKeyValueChangeKey : Any]?, context: UnsafeMutableRawPointer?) { |
| 272 | switch keyPath { | 295 | switch keyPath { |
| 273 | case "torchMode": | 296 | case "torchMode": |
| 274 | - // off = 0; on = 1; auto = 2; | 297 | + // off = 0; on = 1; auto = 2 |
| 275 | let state = change?[.newKey] as? Int | 298 | let state = change?[.newKey] as? Int |
| 276 | torchModeChangeCallback(state) | 299 | torchModeChangeCallback(state) |
| 277 | case "videoZoomFactor": | 300 | case "videoZoomFactor": |
| @@ -286,12 +309,12 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -286,12 +309,12 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 286 | /// Set the zoom factor of the camera | 309 | /// Set the zoom factor of the camera |
| 287 | func setScale(_ scale: CGFloat) throws { | 310 | func setScale(_ scale: CGFloat) throws { |
| 288 | if (device == nil) { | 311 | if (device == nil) { |
| 289 | - throw MobileScannerError.torchWhenStopped | 312 | + throw MobileScannerError.zoomWhenStopped |
| 290 | } | 313 | } |
| 291 | 314 | ||
| 292 | do { | 315 | do { |
| 293 | try device.lockForConfiguration() | 316 | try device.lockForConfiguration() |
| 294 | - var maxZoomFactor = device.activeFormat.videoMaxZoomFactor | 317 | + let maxZoomFactor = device.activeFormat.videoMaxZoomFactor |
| 295 | 318 | ||
| 296 | var actualScale = (scale * 4) + 1 | 319 | var actualScale = (scale * 4) + 1 |
| 297 | 320 | ||
| @@ -325,7 +348,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -325,7 +348,6 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 325 | } | 348 | } |
| 326 | } | 349 | } |
| 327 | 350 | ||
| 328 | - | ||
| 329 | /// Analyze a single image | 351 | /// Analyze a single image |
| 330 | func analyzeImage(image: UIImage, position: AVCaptureDevice.Position, callback: @escaping BarcodeScanningCallback) { | 352 | func analyzeImage(image: UIImage, position: AVCaptureDevice.Position, callback: @escaping BarcodeScanningCallback) { |
| 331 | let image = VisionImage(image: image) | 353 | let image = VisionImage(image: image) |
| @@ -338,22 +360,18 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | @@ -338,22 +360,18 @@ public class MobileScanner: NSObject, AVCaptureVideoDataOutputSampleBufferDelega | ||
| 338 | scanner.process(image, completion: callback) | 360 | scanner.process(image, completion: callback) |
| 339 | } | 361 | } |
| 340 | 362 | ||
| 341 | - var i = 0 | ||
| 342 | - | ||
| 343 | var barcodesString: Array<String?>? | 363 | var barcodesString: Array<String?>? |
| 344 | 364 | ||
| 345 | - | ||
| 346 | - | ||
| 347 | -// /// Convert image buffer to jpeg | ||
| 348 | -// private func ciImageToJpeg(ciImage: CIImage) -> Data { | ||
| 349 | -// | ||
| 350 | -// // let ciImage = CIImage(cvPixelBuffer: latestBuffer) | ||
| 351 | -// let context:CIContext = CIContext.init(options: nil) | ||
| 352 | -// let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)! | ||
| 353 | -// let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up) | ||
| 354 | -// | ||
| 355 | -// return uiImage.jpegData(compressionQuality: 0.8)!; | ||
| 356 | -// } | 365 | + // /// Convert image buffer to jpeg |
| 366 | + // private func ciImageToJpeg(ciImage: CIImage) -> Data { | ||
| 367 | + // | ||
| 368 | + // // let ciImage = CIImage(cvPixelBuffer: latestBuffer) | ||
| 369 | + // let context:CIContext = CIContext.init(options: nil) | ||
| 370 | + // let cgImage:CGImage = context.createCGImage(ciImage, from: ciImage.extent)! | ||
| 371 | + // let uiImage:UIImage = UIImage(cgImage: cgImage, scale: 1, orientation: UIImage.Orientation.up) | ||
| 372 | + // | ||
| 373 | + // return uiImage.jpegData(compressionQuality: 0.8)! | ||
| 374 | + // } | ||
| 357 | 375 | ||
| 358 | /// Rotates images accordingly | 376 | /// Rotates images accordingly |
| 359 | func imageOrientation( | 377 | func imageOrientation( |
| @@ -10,9 +10,7 @@ enum MobileScannerError: Error { | @@ -10,9 +10,7 @@ enum MobileScannerError: Error { | ||
| 10 | case noCamera | 10 | case noCamera |
| 11 | case alreadyStarted | 11 | case alreadyStarted |
| 12 | case alreadyStopped | 12 | case alreadyStopped |
| 13 | - case torchError(_ error: Error) | ||
| 14 | case cameraError(_ error: Error) | 13 | case cameraError(_ error: Error) |
| 15 | - case torchWhenStopped | ||
| 16 | case zoomWhenStopped | 14 | case zoomWhenStopped |
| 17 | case zoomError(_ error: Error) | 15 | case zoomError(_ error: Error) |
| 18 | case analyzerError(_ error: Error) | 16 | case analyzerError(_ error: Error) |
ios/Classes/MobileScannerPlugin.h
deleted
100644 → 0
ios/Classes/MobileScannerPlugin.m
deleted
100644 → 0
| 1 | -#import "MobileScannerPlugin.h" | ||
| 2 | -#if __has_include(<mobile_scanner/mobile_scanner-Swift.h>) | ||
| 3 | -#import <mobile_scanner/mobile_scanner-Swift.h> | ||
| 4 | -#else | ||
| 5 | -// Support project import fallback if the generated compatibility header | ||
| 6 | -// is not copied when this plugin is created as a library. | ||
| 7 | -// https://forums.swift.org/t/swift-static-libraries-dont-copy-generated-objective-c-header/19816 | ||
| 8 | -#import "mobile_scanner-Swift.h" | ||
| 9 | -#endif | ||
| 10 | - | ||
| 11 | -@implementation MobileScannerPlugin | ||
| 12 | -+ (void)registerWithRegistrar:(NSObject<FlutterPluginRegistrar>*)registrar { | ||
| 13 | - [SwiftMobileScannerPlugin registerWithRegistrar:registrar]; | ||
| 14 | -} | ||
| 15 | -@end |
| @@ -4,7 +4,7 @@ import MLKitBarcodeScanning | @@ -4,7 +4,7 @@ import MLKitBarcodeScanning | ||
| 4 | import AVFoundation | 4 | import AVFoundation |
| 5 | import UIKit | 5 | import UIKit |
| 6 | 6 | ||
| 7 | -public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | 7 | +public class MobileScannerPlugin: NSObject, FlutterPlugin { |
| 8 | 8 | ||
| 9 | /// The mobile scanner object that handles all logic | 9 | /// The mobile scanner object that handles all logic |
| 10 | private let mobileScanner: MobileScanner | 10 | private let mobileScanner: MobileScanner |
| @@ -12,10 +12,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -12,10 +12,11 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 12 | /// The handler sends all information via an event channel back to Flutter | 12 | /// The handler sends all information via an event channel back to Flutter |
| 13 | private let barcodeHandler: BarcodeHandler | 13 | private let barcodeHandler: BarcodeHandler |
| 14 | 14 | ||
| 15 | + /// The points for the scan window. | ||
| 15 | static var scanWindow: [CGFloat]? | 16 | static var scanWindow: [CGFloat]? |
| 16 | 17 | ||
| 17 | private static func isBarcodeInScanWindow(barcode: Barcode, imageSize: CGSize) -> Bool { | 18 | private static func isBarcodeInScanWindow(barcode: Barcode, imageSize: CGSize) -> Bool { |
| 18 | - let scanwindow = SwiftMobileScannerPlugin.scanWindow! | 19 | + let scanwindow = MobileScannerPlugin.scanWindow! |
| 19 | let barcodeminX = barcode.cornerPoints![0].cgPointValue.x | 20 | let barcodeminX = barcode.cornerPoints![0].cgPointValue.x |
| 20 | let barcodeminY = barcode.cornerPoints![1].cgPointValue.y | 21 | let barcodeminY = barcode.cornerPoints![1].cgPointValue.y |
| 21 | 22 | ||
| @@ -23,7 +24,6 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -23,7 +24,6 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 23 | let barcodeheight = barcode.cornerPoints![3].cgPointValue.y - barcodeminY | 24 | let barcodeheight = barcode.cornerPoints![3].cgPointValue.y - barcodeminY |
| 24 | let barcodeBox = CGRect(x: barcodeminX, y: barcodeminY, width: barcodewidth, height: barcodeheight) | 25 | let barcodeBox = CGRect(x: barcodeminX, y: barcodeminY, width: barcodewidth, height: barcodeheight) |
| 25 | 26 | ||
| 26 | - | ||
| 27 | let minX = scanwindow[0] * imageSize.width | 27 | let minX = scanwindow[0] * imageSize.width |
| 28 | let minY = scanwindow[1] * imageSize.height | 28 | let minY = scanwindow[1] * imageSize.height |
| 29 | 29 | ||
| @@ -39,8 +39,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -39,8 +39,8 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 39 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in | 39 | self.mobileScanner = MobileScanner(registry: registry, mobileScannerCallback: { barcodes, error, image in |
| 40 | if barcodes != nil { | 40 | if barcodes != nil { |
| 41 | let barcodesMap: [Any?] = barcodes!.compactMap { barcode in | 41 | let barcodesMap: [Any?] = barcodes!.compactMap { barcode in |
| 42 | - if (SwiftMobileScannerPlugin.scanWindow != nil) { | ||
| 43 | - if (SwiftMobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) { | 42 | + if (MobileScannerPlugin.scanWindow != nil) { |
| 43 | + if (MobileScannerPlugin.isBarcodeInScanWindow(barcode: barcode, imageSize: image.size)) { | ||
| 44 | return barcode.data | 44 | return barcode.data |
| 45 | } else { | 45 | } else { |
| 46 | return nil | 46 | return nil |
| @@ -65,10 +65,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -65,10 +65,9 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 65 | } | 65 | } |
| 66 | 66 | ||
| 67 | public static func register(with registrar: FlutterPluginRegistrar) { | 67 | public static func register(with registrar: FlutterPluginRegistrar) { |
| 68 | - let instance = SwiftMobileScannerPlugin(barcodeHandler: BarcodeHandler(registrar: registrar), registry: registrar.textures()) | ||
| 69 | - let methodChannel = FlutterMethodChannel(name: | ||
| 70 | - "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger()) | ||
| 71 | - registrar.addMethodCallDelegate(instance, channel: methodChannel) | 68 | + let channel = FlutterMethodChannel(name: "dev.steenbakker.mobile_scanner/scanner/method", binaryMessenger: registrar.messenger()) |
| 69 | + let instance = MobileScannerPlugin(barcodeHandler: BarcodeHandler(registrar: registrar), registry: registrar.textures()) | ||
| 70 | + registrar.addMethodCallDelegate(instance, channel: channel) | ||
| 72 | } | 71 | } |
| 73 | 72 | ||
| 74 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { | 73 | public func handle(_ call: FlutterMethodCall, result: @escaping FlutterResult) { |
| @@ -96,13 +95,15 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -96,13 +95,15 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 96 | } | 95 | } |
| 97 | } | 96 | } |
| 98 | 97 | ||
| 99 | - /// Parses all parameters and starts the mobileScanner | 98 | + /// Start the mobileScanner. |
| 100 | private func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 99 | private func start(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 101 | let torch: Bool = (call.arguments as! Dictionary<String, Any?>)["torch"] as? Bool ?? false | 100 | let torch: Bool = (call.arguments as! Dictionary<String, Any?>)["torch"] as? Bool ?? false |
| 102 | let facing: Int = (call.arguments as! Dictionary<String, Any?>)["facing"] as? Int ?? 1 | 101 | let facing: Int = (call.arguments as! Dictionary<String, Any?>)["facing"] as? Int ?? 1 |
| 103 | let formats: Array<Int> = (call.arguments as! Dictionary<String, Any?>)["formats"] as? Array ?? [] | 102 | let formats: Array<Int> = (call.arguments as! Dictionary<String, Any?>)["formats"] as? Array ?? [] |
| 104 | let returnImage: Bool = (call.arguments as! Dictionary<String, Any?>)["returnImage"] as? Bool ?? false | 103 | let returnImage: Bool = (call.arguments as! Dictionary<String, Any?>)["returnImage"] as? Bool ?? false |
| 105 | let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0 | 104 | let speed: Int = (call.arguments as! Dictionary<String, Any?>)["speed"] as? Int ?? 0 |
| 105 | + let timeoutMs: Int = (call.arguments as! Dictionary<String, Any?>)["timeout"] as? Int ?? 0 | ||
| 106 | + self.mobileScanner.timeoutSeconds = Double(timeoutMs) / Double(1000) | ||
| 106 | 107 | ||
| 107 | let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} | 108 | let formatList = formats.map { format in return BarcodeFormat(rawValue: format)} |
| 108 | var barcodeOptions: BarcodeScannerOptions? = nil | 109 | var barcodeOptions: BarcodeScannerOptions? = nil |
| @@ -115,13 +116,17 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -115,13 +116,17 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 115 | barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats) | 116 | barcodeOptions = BarcodeScannerOptions(formats: barcodeFormats) |
| 116 | } | 117 | } |
| 117 | 118 | ||
| 118 | - | ||
| 119 | let position = facing == 0 ? AVCaptureDevice.Position.front : .back | 119 | let position = facing == 0 ? AVCaptureDevice.Position.front : .back |
| 120 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! | 120 | let detectionSpeed: DetectionSpeed = DetectionSpeed(rawValue: speed)! |
| 121 | 121 | ||
| 122 | do { | 122 | do { |
| 123 | - try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch ? .on : .off, detectionSpeed: detectionSpeed) { parameters in | ||
| 124 | - result(["textureId": parameters.textureId, "size": ["width": parameters.width, "height": parameters.height], "torchable": parameters.hasTorch]) | 123 | + try mobileScanner.start(barcodeScannerOptions: barcodeOptions, returnImage: returnImage, cameraPosition: position, torch: torch, detectionSpeed: detectionSpeed) { parameters in |
| 124 | + DispatchQueue.main.async { | ||
| 125 | + result([ | ||
| 126 | + "textureId": parameters.textureId, | ||
| 127 | + "size": ["width": parameters.width, "height": parameters.height], | ||
| 128 | + "torchable": parameters.hasTorch]) | ||
| 129 | + } | ||
| 125 | } | 130 | } |
| 126 | } catch MobileScannerError.alreadyStarted { | 131 | } catch MobileScannerError.alreadyStarted { |
| 127 | result(FlutterError(code: "MobileScanner", | 132 | result(FlutterError(code: "MobileScanner", |
| @@ -131,22 +136,18 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -131,22 +136,18 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 131 | result(FlutterError(code: "MobileScanner", | 136 | result(FlutterError(code: "MobileScanner", |
| 132 | message: "No camera found or failed to open camera!", | 137 | message: "No camera found or failed to open camera!", |
| 133 | details: nil)) | 138 | details: nil)) |
| 134 | - } catch MobileScannerError.torchError(let error) { | ||
| 135 | - result(FlutterError(code: "MobileScanner", | ||
| 136 | - message: "Error occured when setting torch!", | ||
| 137 | - details: error)) | ||
| 138 | } catch MobileScannerError.cameraError(let error) { | 139 | } catch MobileScannerError.cameraError(let error) { |
| 139 | result(FlutterError(code: "MobileScanner", | 140 | result(FlutterError(code: "MobileScanner", |
| 140 | message: "Error occured when setting up camera!", | 141 | message: "Error occured when setting up camera!", |
| 141 | details: error)) | 142 | details: error)) |
| 142 | } catch { | 143 | } catch { |
| 143 | result(FlutterError(code: "MobileScanner", | 144 | result(FlutterError(code: "MobileScanner", |
| 144 | - message: "Unknown error occured..", | 145 | + message: "Unknown error occured.", |
| 145 | details: nil)) | 146 | details: nil)) |
| 146 | } | 147 | } |
| 147 | } | 148 | } |
| 148 | 149 | ||
| 149 | - /// Stops the mobileScanner and closes the texture | 150 | + /// Stops the mobileScanner and closes the texture. |
| 150 | private func stop(_ result: @escaping FlutterResult) { | 151 | private func stop(_ result: @escaping FlutterResult) { |
| 151 | do { | 152 | do { |
| 152 | try mobileScanner.stop() | 153 | try mobileScanner.stop() |
| @@ -154,21 +155,19 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -154,21 +155,19 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 154 | result(nil) | 155 | result(nil) |
| 155 | } | 156 | } |
| 156 | 157 | ||
| 157 | - /// Toggles the torch | 158 | + /// Toggles the torch. |
| 158 | private func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 159 | private func toggleTorch(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 159 | do { | 160 | do { |
| 160 | try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off) | 161 | try mobileScanner.toggleTorch(call.arguments as? Int == 1 ? .on : .off) |
| 162 | + result(nil) | ||
| 161 | } catch { | 163 | } catch { |
| 162 | - result(FlutterError(code: "MobileScanner", | ||
| 163 | - message: "Called toggleTorch() while stopped!", | ||
| 164 | - details: nil)) | 164 | + result(FlutterError(code: "MobileScanner", message: error.localizedDescription, details: nil)) |
| 165 | } | 165 | } |
| 166 | - result(nil) | ||
| 167 | } | 166 | } |
| 168 | 167 | ||
| 169 | - /// Toggles the zoomScale | 168 | + /// Sets the zoomScale. |
| 170 | private func setScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 169 | private func setScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 171 | - var scale = call.arguments as? CGFloat | 170 | + let scale = call.arguments as? CGFloat |
| 172 | if (scale == nil) { | 171 | if (scale == nil) { |
| 173 | result(FlutterError(code: "MobileScanner", | 172 | result(FlutterError(code: "MobileScanner", |
| 174 | message: "You must provide a scale when calling setScale!", | 173 | message: "You must provide a scale when calling setScale!", |
| @@ -177,6 +176,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -177,6 +176,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 177 | } | 176 | } |
| 178 | do { | 177 | do { |
| 179 | try mobileScanner.setScale(scale!) | 178 | try mobileScanner.setScale(scale!) |
| 179 | + result(nil) | ||
| 180 | } catch MobileScannerError.zoomWhenStopped { | 180 | } catch MobileScannerError.zoomWhenStopped { |
| 181 | result(FlutterError(code: "MobileScanner", | 181 | result(FlutterError(code: "MobileScanner", |
| 182 | message: "Called setScale() while stopped!", | 182 | message: "Called setScale() while stopped!", |
| @@ -190,13 +190,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -190,13 +190,13 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 190 | message: "Error while zooming.", | 190 | message: "Error while zooming.", |
| 191 | details: nil)) | 191 | details: nil)) |
| 192 | } | 192 | } |
| 193 | - result(nil) | ||
| 194 | } | 193 | } |
| 195 | 194 | ||
| 196 | - /// Reset the zoomScale | 195 | + /// Reset the zoomScale. |
| 197 | private func resetScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 196 | private func resetScale(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 198 | do { | 197 | do { |
| 199 | try mobileScanner.resetScale() | 198 | try mobileScanner.resetScale() |
| 199 | + result(nil) | ||
| 200 | } catch MobileScannerError.zoomWhenStopped { | 200 | } catch MobileScannerError.zoomWhenStopped { |
| 201 | result(FlutterError(code: "MobileScanner", | 201 | result(FlutterError(code: "MobileScanner", |
| 202 | message: "Called resetScale() while stopped!", | 202 | message: "Called resetScale() while stopped!", |
| @@ -210,14 +210,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -210,14 +210,12 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 210 | message: "Error while zooming.", | 210 | message: "Error while zooming.", |
| 211 | details: nil)) | 211 | details: nil)) |
| 212 | } | 212 | } |
| 213 | - result(nil) | ||
| 214 | } | 213 | } |
| 215 | 214 | ||
| 216 | - | ||
| 217 | - /// Toggles the torch | 215 | + /// Updates the scan window rectangle. |
| 218 | func updateScanWindow(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 216 | func updateScanWindow(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 219 | let scanWindowData: Array? = (call.arguments as? [String: Any])?["rect"] as? [CGFloat] | 217 | let scanWindowData: Array? = (call.arguments as? [String: Any])?["rect"] as? [CGFloat] |
| 220 | - SwiftMobileScannerPlugin.scanWindow = scanWindowData | 218 | + MobileScannerPlugin.scanWindow = scanWindowData |
| 221 | 219 | ||
| 222 | result(nil) | 220 | result(nil) |
| 223 | } | 221 | } |
| @@ -236,7 +234,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -236,7 +234,7 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 236 | return CGRect(x: minX, y: minY, width: width, height: height) | 234 | return CGRect(x: minX, y: minY, width: width, height: height) |
| 237 | } | 235 | } |
| 238 | 236 | ||
| 239 | - /// Analyzes a single image | 237 | + /// Analyzes a single image. |
| 240 | private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { | 238 | private func analyzeImage(_ call: FlutterMethodCall, _ result: @escaping FlutterResult) { |
| 241 | let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "") | 239 | let uiImage = UIImage(contentsOfFile: call.arguments as? String ?? "") |
| 242 | 240 | ||
| @@ -248,16 +246,28 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | @@ -248,16 +246,28 @@ public class SwiftMobileScannerPlugin: NSObject, FlutterPlugin { | ||
| 248 | } | 246 | } |
| 249 | 247 | ||
| 250 | mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { [self] barcodes, error in | 248 | mobileScanner.analyzeImage(image: uiImage!, position: AVCaptureDevice.Position.back, callback: { [self] barcodes, error in |
| 251 | - if error == nil && barcodes != nil && !barcodes!.isEmpty { | 249 | + if error != nil { |
| 250 | + barcodeHandler.publishEvent(["name": "error", "message": error?.localizedDescription]) | ||
| 251 | + | ||
| 252 | + DispatchQueue.main.async { | ||
| 253 | + result(false) | ||
| 254 | + } | ||
| 255 | + | ||
| 256 | + return | ||
| 257 | + } | ||
| 258 | + | ||
| 259 | + if (barcodes == nil || barcodes!.isEmpty) { | ||
| 260 | + DispatchQueue.main.async { | ||
| 261 | + result(false) | ||
| 262 | + } | ||
| 263 | + } else { | ||
| 252 | let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data } | 264 | let barcodesMap: [Any?] = barcodes!.compactMap { barcode in barcode.data } |
| 253 | let event: [String: Any?] = ["name": "barcode", "data": barcodesMap] | 265 | let event: [String: Any?] = ["name": "barcode", "data": barcodesMap] |
| 254 | barcodeHandler.publishEvent(event) | 266 | barcodeHandler.publishEvent(event) |
| 267 | + | ||
| 268 | + DispatchQueue.main.async { | ||
| 255 | result(true) | 269 | result(true) |
| 256 | - } else { | ||
| 257 | - if error != nil { | ||
| 258 | - barcodeHandler.publishEvent(["name": "error", "message": error?.localizedDescription]) | ||
| 259 | } | 270 | } |
| 260 | - result(false) | ||
| 261 | } | 271 | } |
| 262 | }) | 272 | }) |
| 263 | } | 273 | } |
| 1 | -// | ||
| 2 | -// Util.swift | ||
| 3 | -// camerax | ||
| 4 | -// | ||
| 5 | -// Created by 闫守旺 on 2021/2/6. | ||
| 6 | -// | ||
| 7 | - | ||
| 8 | import AVFoundation | 1 | import AVFoundation |
| 9 | -import Flutter | ||
| 10 | import Foundation | 2 | import Foundation |
| 11 | import MLKitBarcodeScanning | 3 | import MLKitBarcodeScanning |
| 12 | 4 | ||
| 13 | -extension Error { | ||
| 14 | - func throwNative(_ result: FlutterResult) { | ||
| 15 | - let error = FlutterError(code: localizedDescription, message: nil, details: nil) | ||
| 16 | - result(error) | ||
| 17 | - } | ||
| 18 | -} | ||
| 19 | - | ||
| 20 | extension CVBuffer { | 5 | extension CVBuffer { |
| 21 | var image: UIImage { | 6 | var image: UIImage { |
| 22 | let ciImage = CIImage(cvPixelBuffer: self) | 7 | let ciImage = CIImage(cvPixelBuffer: self) |
| 23 | let cgImage = CIContext().createCGImage(ciImage, from: ciImage.extent) | 8 | let cgImage = CIContext().createCGImage(ciImage, from: ciImage.extent) |
| 24 | - return UIImage(cgImage: cgImage!, scale: 1.0, orientation: UIImage.Orientation.left) | 9 | + return UIImage(cgImage: cgImage!) |
| 25 | } | 10 | } |
| 26 | 11 | ||
| 27 | var image1: UIImage { | 12 | var image1: UIImage { |
| @@ -4,7 +4,7 @@ | @@ -4,7 +4,7 @@ | ||
| 4 | # | 4 | # |
| 5 | Pod::Spec.new do |s| | 5 | Pod::Spec.new do |s| |
| 6 | s.name = 'mobile_scanner' | 6 | s.name = 'mobile_scanner' |
| 7 | - s.version = '3.2.0' | 7 | + s.version = '3.5.5' |
| 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' | 8 | s.summary = 'An universal scanner for Flutter based on MLKit.' |
| 9 | s.description = <<-DESC | 9 | s.description = <<-DESC |
| 10 | An universal scanner for Flutter based on MLKit. | 10 | An universal scanner for Flutter based on MLKit. |
| 1 | +export 'src/enums/address_type.dart'; | ||
| 2 | +export 'src/enums/barcode_format.dart'; | ||
| 3 | +export 'src/enums/barcode_type.dart'; | ||
| 1 | export 'src/enums/camera_facing.dart'; | 4 | export 'src/enums/camera_facing.dart'; |
| 2 | export 'src/enums/detection_speed.dart'; | 5 | export 'src/enums/detection_speed.dart'; |
| 6 | +export 'src/enums/email_type.dart'; | ||
| 7 | +export 'src/enums/encryption_type.dart'; | ||
| 3 | export 'src/enums/mobile_scanner_error_code.dart'; | 8 | export 'src/enums/mobile_scanner_error_code.dart'; |
| 4 | export 'src/enums/mobile_scanner_state.dart'; | 9 | export 'src/enums/mobile_scanner_state.dart'; |
| 5 | -export 'src/enums/ratio.dart'; | 10 | +export 'src/enums/phone_type.dart'; |
| 6 | export 'src/enums/torch_state.dart'; | 11 | export 'src/enums/torch_state.dart'; |
| 7 | export 'src/mobile_scanner.dart'; | 12 | export 'src/mobile_scanner.dart'; |
| 8 | export 'src/mobile_scanner_controller.dart'; | 13 | export 'src/mobile_scanner_controller.dart'; |
| 9 | export 'src/mobile_scanner_exception.dart'; | 14 | export 'src/mobile_scanner_exception.dart'; |
| 15 | +export 'src/objects/address.dart'; | ||
| 10 | export 'src/objects/barcode.dart'; | 16 | export 'src/objects/barcode.dart'; |
| 11 | export 'src/objects/barcode_capture.dart'; | 17 | export 'src/objects/barcode_capture.dart'; |
| 18 | +export 'src/objects/calendar_event.dart'; | ||
| 19 | +export 'src/objects/contact_info.dart'; | ||
| 20 | +export 'src/objects/driver_license.dart'; | ||
| 21 | +export 'src/objects/email.dart'; | ||
| 22 | +export 'src/objects/geo_point.dart'; | ||
| 12 | export 'src/objects/mobile_scanner_arguments.dart'; | 23 | export 'src/objects/mobile_scanner_arguments.dart'; |
| 24 | +export 'src/objects/person_name.dart'; | ||
| 25 | +export 'src/objects/phone.dart'; | ||
| 26 | +export 'src/objects/sms.dart'; | ||
| 27 | +export 'src/objects/url_bookmark.dart'; | ||
| 28 | +export 'src/objects/wifi.dart'; |
| @@ -5,9 +5,8 @@ import 'dart:ui' as ui; | @@ -5,9 +5,8 @@ import 'dart:ui' as ui; | ||
| 5 | import 'package:flutter/services.dart'; | 5 | import 'package:flutter/services.dart'; |
| 6 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; | 6 | import 'package:flutter_web_plugins/flutter_web_plugins.dart'; |
| 7 | import 'package:mobile_scanner/mobile_scanner_web.dart'; | 7 | import 'package:mobile_scanner/mobile_scanner_web.dart'; |
| 8 | -import 'package:mobile_scanner/src/barcode_utility.dart'; | 8 | +import 'package:mobile_scanner/src/enums/barcode_format.dart'; |
| 9 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; | 9 | import 'package:mobile_scanner/src/enums/camera_facing.dart'; |
| 10 | -import 'package:mobile_scanner/src/objects/barcode.dart'; | ||
| 11 | 10 | ||
| 12 | /// This plugin is the web implementation of mobile_scanner. | 11 | /// This plugin is the web implementation of mobile_scanner. |
| 13 | /// It only supports QR codes. | 12 | /// It only supports QR codes. |
| @@ -110,7 +109,7 @@ class MobileScannerWebPlugin { | @@ -110,7 +109,7 @@ class MobileScannerWebPlugin { | ||
| 110 | if (arguments.containsKey('formats')) { | 109 | if (arguments.containsKey('formats')) { |
| 111 | formats = (arguments['formats'] as List) | 110 | formats = (arguments['formats'] as List) |
| 112 | .cast<int>() | 111 | .cast<int>() |
| 113 | - .map((e) => toFormat(e)) | 112 | + .map(BarcodeFormat.fromRawValue) |
| 114 | .toList(); | 113 | .toList(); |
| 115 | } | 114 | } |
| 116 | 115 | ||
| @@ -130,8 +129,6 @@ class MobileScannerWebPlugin { | @@ -130,8 +129,6 @@ class MobileScannerWebPlugin { | ||
| 130 | _barCodeStreamSubscription = | 129 | _barCodeStreamSubscription = |
| 131 | barCodeReader.detectBarcodeContinuously().listen((code) { | 130 | barCodeReader.detectBarcodeContinuously().listen((code) { |
| 132 | if (code != null) { | 131 | if (code != null) { |
| 133 | - final List<Offset>? corners = code.corners; | ||
| 134 | - | ||
| 135 | controller.add({ | 132 | controller.add({ |
| 136 | 'name': 'barcodeWeb', | 133 | 'name': 'barcodeWeb', |
| 137 | 'data': { | 134 | 'data': { |
| @@ -139,9 +136,9 @@ class MobileScannerWebPlugin { | @@ -139,9 +136,9 @@ class MobileScannerWebPlugin { | ||
| 139 | 'rawBytes': code.rawBytes, | 136 | 'rawBytes': code.rawBytes, |
| 140 | 'format': code.format.rawValue, | 137 | 'format': code.format.rawValue, |
| 141 | 'displayValue': code.displayValue, | 138 | 'displayValue': code.displayValue, |
| 142 | - 'type': code.type.index, | ||
| 143 | - if (corners != null && corners.isNotEmpty) | ||
| 144 | - 'corners': corners | 139 | + 'type': code.type.rawValue, |
| 140 | + if (code.corners.isNotEmpty) | ||
| 141 | + 'corners': code.corners | ||
| 145 | .map( | 142 | .map( |
| 146 | (Offset c) => <Object?, Object?>{'x': c.dx, 'y': c.dy}, | 143 | (Offset c) => <Object?, Object?>{'x': c.dx, 'y': c.dy}, |
| 147 | ) | 144 | ) |
| @@ -152,9 +149,10 @@ class MobileScannerWebPlugin { | @@ -152,9 +149,10 @@ class MobileScannerWebPlugin { | ||
| 152 | }); | 149 | }); |
| 153 | 150 | ||
| 154 | final hasTorch = await barCodeReader.hasTorch(); | 151 | final hasTorch = await barCodeReader.hasTorch(); |
| 152 | + final bool? enableTorch = arguments['torch'] as bool?; | ||
| 155 | 153 | ||
| 156 | - if (hasTorch && arguments.containsKey('torch')) { | ||
| 157 | - await barCodeReader.toggleTorch(enabled: arguments['torch'] as bool); | 154 | + if (hasTorch && enableTorch != null) { |
| 155 | + await barCodeReader.toggleTorch(enabled: enableTorch); | ||
| 158 | } | 156 | } |
| 159 | 157 | ||
| 160 | return { | 158 | return { |
lib/src/barcode_utility.dart
deleted
100644 → 0
| 1 | -import 'dart:math' as math; | ||
| 2 | - | ||
| 3 | -import 'package:flutter/material.dart'; | ||
| 4 | -import 'package:mobile_scanner/mobile_scanner.dart'; | ||
| 5 | - | ||
| 6 | -Size toSize(Map data) { | ||
| 7 | - final width = data['width'] as double; | ||
| 8 | - final height = data['height'] as double; | ||
| 9 | - return Size(width, height); | ||
| 10 | -} | ||
| 11 | - | ||
| 12 | -List<Offset>? toCorners(List<Map<Object?, Object?>>? data) { | ||
| 13 | - if (data == null) { | ||
| 14 | - return null; | ||
| 15 | - } | ||
| 16 | - | ||
| 17 | - return List.unmodifiable( | ||
| 18 | - data.map((Map<Object?, Object?> e) { | ||
| 19 | - return Offset(e['x']! as double, e['y']! as double); | ||
| 20 | - }), | ||
| 21 | - ); | ||
| 22 | -} | ||
| 23 | - | ||
| 24 | -BarcodeFormat toFormat(int value) { | ||
| 25 | - switch (value) { | ||
| 26 | - case 0: | ||
| 27 | - return BarcodeFormat.all; | ||
| 28 | - case 1: | ||
| 29 | - return BarcodeFormat.code128; | ||
| 30 | - case 2: | ||
| 31 | - return BarcodeFormat.code39; | ||
| 32 | - case 4: | ||
| 33 | - return BarcodeFormat.code93; | ||
| 34 | - case 8: | ||
| 35 | - return BarcodeFormat.codebar; | ||
| 36 | - case 16: | ||
| 37 | - return BarcodeFormat.dataMatrix; | ||
| 38 | - case 32: | ||
| 39 | - return BarcodeFormat.ean13; | ||
| 40 | - case 64: | ||
| 41 | - return BarcodeFormat.ean8; | ||
| 42 | - case 128: | ||
| 43 | - return BarcodeFormat.itf; | ||
| 44 | - case 256: | ||
| 45 | - return BarcodeFormat.qrCode; | ||
| 46 | - case 512: | ||
| 47 | - return BarcodeFormat.upcA; | ||
| 48 | - case 1024: | ||
| 49 | - return BarcodeFormat.upcE; | ||
| 50 | - case 2048: | ||
| 51 | - return BarcodeFormat.pdf417; | ||
| 52 | - case 4096: | ||
| 53 | - return BarcodeFormat.aztec; | ||
| 54 | - default: | ||
| 55 | - return BarcodeFormat.unknown; | ||
| 56 | - } | ||
| 57 | -} | ||
| 58 | - | ||
| 59 | -CalendarEvent? toCalendarEvent(Map? data) { | ||
| 60 | - if (data != null) { | ||
| 61 | - return CalendarEvent.fromNative(data); | ||
| 62 | - } else { | ||
| 63 | - return null; | ||
| 64 | - } | ||
| 65 | -} | ||
| 66 | - | ||
| 67 | -DateTime? toDateTime(Map<String, dynamic>? data) { | ||
| 68 | - if (data != null) { | ||
| 69 | - final year = data['year'] as int; | ||
| 70 | - final month = data['month'] as int; | ||
| 71 | - final day = data['day'] as int; | ||
| 72 | - final hour = data['hours'] as int; | ||
| 73 | - final minute = data['minutes'] as int; | ||
| 74 | - final second = data['seconds'] as int; | ||
| 75 | - return data['isUtc'] as bool | ||
| 76 | - ? DateTime.utc(year, month, day, hour, minute, second) | ||
| 77 | - : DateTime(year, month, day, hour, minute, second); | ||
| 78 | - } else { | ||
| 79 | - return null; | ||
| 80 | - } | ||
| 81 | -} | ||
| 82 | - | ||
| 83 | -ContactInfo? toContactInfo(Map? data) { | ||
| 84 | - if (data != null) { | ||
| 85 | - return ContactInfo.fromNative(data); | ||
| 86 | - } else { | ||
| 87 | - return null; | ||
| 88 | - } | ||
| 89 | -} | ||
| 90 | - | ||
| 91 | -PersonName? toName(Map? data) { | ||
| 92 | - if (data != null) { | ||
| 93 | - return PersonName.fromNative(data); | ||
| 94 | - } else { | ||
| 95 | - return null; | ||
| 96 | - } | ||
| 97 | -} | ||
| 98 | - | ||
| 99 | -DriverLicense? toDriverLicense(Map? data) { | ||
| 100 | - if (data != null) { | ||
| 101 | - return DriverLicense.fromNative(data); | ||
| 102 | - } else { | ||
| 103 | - return null; | ||
| 104 | - } | ||
| 105 | -} | ||
| 106 | - | ||
| 107 | -Email? toEmail(Map? data) { | ||
| 108 | - if (data != null) { | ||
| 109 | - return Email.fromNative(data); | ||
| 110 | - } else { | ||
| 111 | - return null; | ||
| 112 | - } | ||
| 113 | -} | ||
| 114 | - | ||
| 115 | -GeoPoint? toGeoPoint(Map? data) { | ||
| 116 | - if (data != null) { | ||
| 117 | - return GeoPoint.fromNative(data); | ||
| 118 | - } else { | ||
| 119 | - return null; | ||
| 120 | - } | ||
| 121 | -} | ||
| 122 | - | ||
| 123 | -Phone? toPhone(Map? data) { | ||
| 124 | - if (data != null) { | ||
| 125 | - return Phone.fromNative(data); | ||
| 126 | - } else { | ||
| 127 | - return null; | ||
| 128 | - } | ||
| 129 | -} | ||
| 130 | - | ||
| 131 | -SMS? toSMS(Map? data) { | ||
| 132 | - if (data != null) { | ||
| 133 | - return SMS.fromNative(data); | ||
| 134 | - } else { | ||
| 135 | - return null; | ||
| 136 | - } | ||
| 137 | -} | ||
| 138 | - | ||
| 139 | -UrlBookmark? toUrl(Map? data) { | ||
| 140 | - if (data != null) { | ||
| 141 | - return UrlBookmark.fromNative(data); | ||
| 142 | - } else { | ||
| 143 | - return null; | ||
| 144 | - } | ||
| 145 | -} | ||
| 146 | - | ||
| 147 | -WiFi? toWiFi(Map? data) { | ||
| 148 | - if (data != null) { | ||
| 149 | - return WiFi.fromNative(data); | ||
| 150 | - } else { | ||
| 151 | - return null; | ||
| 152 | - } | ||
| 153 | -} | ||
| 154 | - | ||
| 155 | -Size applyBoxFit(BoxFit fit, Size input, Size output) { | ||
| 156 | - if (input.height <= 0.0 || | ||
| 157 | - input.width <= 0.0 || | ||
| 158 | - output.height <= 0.0 || | ||
| 159 | - output.width <= 0.0) { | ||
| 160 | - return Size.zero; | ||
| 161 | - } | ||
| 162 | - | ||
| 163 | - Size destination; | ||
| 164 | - | ||
| 165 | - final inputAspectRatio = input.width / input.height; | ||
| 166 | - final outputAspectRatio = output.width / output.height; | ||
| 167 | - | ||
| 168 | - switch (fit) { | ||
| 169 | - case BoxFit.fill: | ||
| 170 | - destination = output; | ||
| 171 | - break; | ||
| 172 | - case BoxFit.contain: | ||
| 173 | - if (outputAspectRatio > inputAspectRatio) { | ||
| 174 | - destination = Size( | ||
| 175 | - input.width * output.height / input.height, | ||
| 176 | - output.height, | ||
| 177 | - ); | ||
| 178 | - } else { | ||
| 179 | - destination = Size( | ||
| 180 | - output.width, | ||
| 181 | - input.height * output.width / input.width, | ||
| 182 | - ); | ||
| 183 | - } | ||
| 184 | - break; | ||
| 185 | - | ||
| 186 | - case BoxFit.cover: | ||
| 187 | - if (outputAspectRatio > inputAspectRatio) { | ||
| 188 | - destination = Size( | ||
| 189 | - output.width, | ||
| 190 | - input.height * (output.width / input.width), | ||
| 191 | - ); | ||
| 192 | - } else { | ||
| 193 | - destination = Size( | ||
| 194 | - input.width * (output.height / input.height), | ||
| 195 | - output.height, | ||
| 196 | - ); | ||
| 197 | - } | ||
| 198 | - break; | ||
| 199 | - case BoxFit.fitWidth: | ||
| 200 | - destination = Size( | ||
| 201 | - output.width, | ||
| 202 | - input.height * (output.width / input.width), | ||
| 203 | - ); | ||
| 204 | - break; | ||
| 205 | - case BoxFit.fitHeight: | ||
| 206 | - destination = Size( | ||
| 207 | - input.width * (output.height / input.height), | ||
| 208 | - output.height, | ||
| 209 | - ); | ||
| 210 | - break; | ||
| 211 | - case BoxFit.none: | ||
| 212 | - destination = Size( | ||
| 213 | - math.min(input.width, output.width), | ||
| 214 | - math.min(input.height, output.height), | ||
| 215 | - ); | ||
| 216 | - break; | ||
| 217 | - case BoxFit.scaleDown: | ||
| 218 | - destination = input; | ||
| 219 | - if (destination.height > output.height) { | ||
| 220 | - destination = Size(output.height * inputAspectRatio, output.height); | ||
| 221 | - } | ||
| 222 | - if (destination.width > output.width) { | ||
| 223 | - destination = Size(output.width, output.width / inputAspectRatio); | ||
| 224 | - } | ||
| 225 | - break; | ||
| 226 | - } | ||
| 227 | - | ||
| 228 | - return destination; | ||
| 229 | -} |
lib/src/enums/address_type.dart
0 → 100644
| 1 | +/// Address type constants. | ||
| 2 | +enum AddressType { | ||
| 3 | + /// Unknown address type. | ||
| 4 | + unknown(0), | ||
| 5 | + | ||
| 6 | + /// Work address. | ||
| 7 | + work(1), | ||
| 8 | + | ||
| 9 | + /// Home address. | ||
| 10 | + home(2); | ||
| 11 | + | ||
| 12 | + const AddressType(this.rawValue); | ||
| 13 | + | ||
| 14 | + factory AddressType.fromRawValue(int value) { | ||
| 15 | + switch (value) { | ||
| 16 | + case 0: | ||
| 17 | + return AddressType.unknown; | ||
| 18 | + case 1: | ||
| 19 | + return AddressType.work; | ||
| 20 | + case 2: | ||
| 21 | + return AddressType.home; | ||
| 22 | + default: | ||
| 23 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /// The raw address type value. | ||
| 28 | + final int rawValue; | ||
| 29 | +} |
lib/src/enums/barcode_format.dart
0 → 100644
| 1 | +/// This enum defines the different barcode formats. | ||
| 2 | +enum BarcodeFormat { | ||
| 3 | + /// A barcode format that represents all unknown formats. | ||
| 4 | + unknown(-1), | ||
| 5 | + | ||
| 6 | + /// A barcode format that represents all known formats. | ||
| 7 | + all(0), | ||
| 8 | + | ||
| 9 | + /// Barcode format constant for Code 128. | ||
| 10 | + code128(1), | ||
| 11 | + | ||
| 12 | + /// Barcode format constant for Code 39. | ||
| 13 | + code39(2), | ||
| 14 | + | ||
| 15 | + /// Barcode format constant for Code 93. | ||
| 16 | + code93(4), | ||
| 17 | + | ||
| 18 | + /// Barcode format constant for Codabar. | ||
| 19 | + codabar(8), | ||
| 20 | + | ||
| 21 | + /// Barcode format constant for Data Matrix. | ||
| 22 | + dataMatrix(16), | ||
| 23 | + | ||
| 24 | + /// Barcode format constant for EAN-13. | ||
| 25 | + ean13(32), | ||
| 26 | + | ||
| 27 | + /// Barcode format constant for EAN-8. | ||
| 28 | + ean8(64), | ||
| 29 | + | ||
| 30 | + /// Barcode format constant for ITF (Interleaved Two-of-Five). | ||
| 31 | + itf(128), | ||
| 32 | + | ||
| 33 | + /// Barcode format constant for QR Codes. | ||
| 34 | + qrCode(256), | ||
| 35 | + | ||
| 36 | + /// Barcode format constant for UPC-A. | ||
| 37 | + upcA(512), | ||
| 38 | + | ||
| 39 | + /// Barcode format constant for UPC-E. | ||
| 40 | + upcE(1024), | ||
| 41 | + | ||
| 42 | + /// Barcode format constant for PDF-417. | ||
| 43 | + pdf417(2048), | ||
| 44 | + | ||
| 45 | + /// Barcode format constant for AZTEC. | ||
| 46 | + aztec(4096); | ||
| 47 | + | ||
| 48 | + /// This constant represents the old value for [BarcodeFormat.codabar]. | ||
| 49 | + /// | ||
| 50 | + /// Prefer using the new [BarcodeFormat.codabar] constant, | ||
| 51 | + /// as the `codebar` value will be removed in a future release. | ||
| 52 | + static const BarcodeFormat codebar = codabar; | ||
| 53 | + | ||
| 54 | + const BarcodeFormat(this.rawValue); | ||
| 55 | + | ||
| 56 | + factory BarcodeFormat.fromRawValue(int value) { | ||
| 57 | + switch (value) { | ||
| 58 | + case -1: | ||
| 59 | + return BarcodeFormat.unknown; | ||
| 60 | + case 0: | ||
| 61 | + return BarcodeFormat.all; | ||
| 62 | + case 1: | ||
| 63 | + return BarcodeFormat.code128; | ||
| 64 | + case 2: | ||
| 65 | + return BarcodeFormat.code39; | ||
| 66 | + case 4: | ||
| 67 | + return BarcodeFormat.code93; | ||
| 68 | + case 8: | ||
| 69 | + return BarcodeFormat.codebar; | ||
| 70 | + case 16: | ||
| 71 | + return BarcodeFormat.dataMatrix; | ||
| 72 | + case 32: | ||
| 73 | + return BarcodeFormat.ean13; | ||
| 74 | + case 64: | ||
| 75 | + return BarcodeFormat.ean8; | ||
| 76 | + case 128: | ||
| 77 | + return BarcodeFormat.itf; | ||
| 78 | + case 256: | ||
| 79 | + return BarcodeFormat.qrCode; | ||
| 80 | + case 512: | ||
| 81 | + return BarcodeFormat.upcA; | ||
| 82 | + case 1024: | ||
| 83 | + return BarcodeFormat.upcE; | ||
| 84 | + case 2048: | ||
| 85 | + return BarcodeFormat.pdf417; | ||
| 86 | + case 4096: | ||
| 87 | + return BarcodeFormat.aztec; | ||
| 88 | + default: | ||
| 89 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 90 | + } | ||
| 91 | + } | ||
| 92 | + | ||
| 93 | + /// The raw value of the barcode format. | ||
| 94 | + final int rawValue; | ||
| 95 | +} |
lib/src/enums/barcode_type.dart
0 → 100644
| 1 | +/// Barcode value type constants. | ||
| 2 | +enum BarcodeType { | ||
| 3 | + /// An unknown barcode type. | ||
| 4 | + unknown(0), | ||
| 5 | + | ||
| 6 | + /// Barcode value type constant for contact information. | ||
| 7 | + contactInfo(1), | ||
| 8 | + | ||
| 9 | + /// Barcode value type constant for email message details. | ||
| 10 | + email(2), | ||
| 11 | + | ||
| 12 | + /// Barcode value type constant for ISBNs. | ||
| 13 | + isbn(3), | ||
| 14 | + | ||
| 15 | + /// Barcode value type constant for phone numbers. | ||
| 16 | + phone(4), | ||
| 17 | + | ||
| 18 | + /// Barcode value type constant for product codes. | ||
| 19 | + product(5), | ||
| 20 | + | ||
| 21 | + /// Barcode value type constant for SMS details. | ||
| 22 | + sms(6), | ||
| 23 | + | ||
| 24 | + /// Barcode value type constant for plain text. | ||
| 25 | + text(7), | ||
| 26 | + | ||
| 27 | + /// Barcode value type constant for URLs or bookmarks. | ||
| 28 | + url(8), | ||
| 29 | + | ||
| 30 | + /// Barcode value type constant for WiFi access point details. | ||
| 31 | + wifi(9), | ||
| 32 | + | ||
| 33 | + /// Barcode value type constant for geographic coordinates. | ||
| 34 | + geo(10), | ||
| 35 | + | ||
| 36 | + /// Barcode value type constant for calendar events. | ||
| 37 | + calendarEvent(11), | ||
| 38 | + | ||
| 39 | + /// Barcode value type constant for driver license data. | ||
| 40 | + driverLicense(12); | ||
| 41 | + | ||
| 42 | + const BarcodeType(this.rawValue); | ||
| 43 | + | ||
| 44 | + factory BarcodeType.fromRawValue(int value) { | ||
| 45 | + switch (value) { | ||
| 46 | + case 0: | ||
| 47 | + return BarcodeType.unknown; | ||
| 48 | + case 1: | ||
| 49 | + return BarcodeType.contactInfo; | ||
| 50 | + case 2: | ||
| 51 | + return BarcodeType.email; | ||
| 52 | + case 3: | ||
| 53 | + return BarcodeType.isbn; | ||
| 54 | + case 4: | ||
| 55 | + return BarcodeType.phone; | ||
| 56 | + case 5: | ||
| 57 | + return BarcodeType.product; | ||
| 58 | + case 6: | ||
| 59 | + return BarcodeType.sms; | ||
| 60 | + case 7: | ||
| 61 | + return BarcodeType.text; | ||
| 62 | + case 8: | ||
| 63 | + return BarcodeType.url; | ||
| 64 | + case 9: | ||
| 65 | + return BarcodeType.wifi; | ||
| 66 | + case 10: | ||
| 67 | + return BarcodeType.geo; | ||
| 68 | + case 11: | ||
| 69 | + return BarcodeType.calendarEvent; | ||
| 70 | + case 12: | ||
| 71 | + return BarcodeType.driverLicense; | ||
| 72 | + default: | ||
| 73 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | + /// The raw barcode type value. | ||
| 78 | + final int rawValue; | ||
| 79 | +} |
| 1 | /// The facing of a camera. | 1 | /// The facing of a camera. |
| 2 | enum CameraFacing { | 2 | enum CameraFacing { |
| 3 | /// Front facing camera. | 3 | /// Front facing camera. |
| 4 | - front, | 4 | + front(0), |
| 5 | 5 | ||
| 6 | /// Back facing camera. | 6 | /// Back facing camera. |
| 7 | - back, | 7 | + back(1); |
| 8 | + | ||
| 9 | + const CameraFacing(this.rawValue); | ||
| 10 | + | ||
| 11 | + factory CameraFacing.fromRawValue(int value) { | ||
| 12 | + switch (value) { | ||
| 13 | + case 0: | ||
| 14 | + return CameraFacing.front; | ||
| 15 | + case 1: | ||
| 16 | + return CameraFacing.back; | ||
| 17 | + default: | ||
| 18 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + /// The raw value for the camera facing direction. | ||
| 23 | + final int rawValue; | ||
| 8 | } | 24 | } |
| @@ -3,18 +3,34 @@ enum DetectionSpeed { | @@ -3,18 +3,34 @@ enum DetectionSpeed { | ||
| 3 | /// The scanner will only scan a barcode once, and never again until another | 3 | /// The scanner will only scan a barcode once, and never again until another |
| 4 | /// barcode has been scanned. | 4 | /// barcode has been scanned. |
| 5 | /// | 5 | /// |
| 6 | - /// NOTE: This mode does analyze every frame in order to check if the value | ||
| 7 | - /// has changed. | ||
| 8 | - noDuplicates, | 6 | + /// Bear in mind that this mode analyzes every frame, |
| 7 | + /// in order to check if the value has changed. | ||
| 8 | + noDuplicates(0), | ||
| 9 | 9 | ||
| 10 | - /// The barcode scanner will scan one barcode, and wait 250 Miliseconds before | ||
| 11 | - /// scanning again. This will prevent memory issues on older devices. | ||
| 12 | - /// | ||
| 13 | - /// You can change the timeout duration with [detectionTimeout] parameter. | ||
| 14 | - normal, | 10 | + /// The barcode scanner will scan barcodes, |
| 11 | + /// while respecting the configured scan timeout between individual scans. | ||
| 12 | + normal(1), | ||
| 15 | 13 | ||
| 16 | - /// Let the scanner detect barcodes without restriction. | 14 | + /// The barcode scanner will scan barcodes, without any restrictions. |
| 17 | /// | 15 | /// |
| 18 | - /// NOTE: This can cause memory issues with older devices. | ||
| 19 | - unrestricted, | 16 | + /// Bear in mind that this mode can cause memory issues on older devices. |
| 17 | + unrestricted(2); | ||
| 18 | + | ||
| 19 | + const DetectionSpeed(this.rawValue); | ||
| 20 | + | ||
| 21 | + factory DetectionSpeed.fromRawValue(int value) { | ||
| 22 | + switch (value) { | ||
| 23 | + case 0: | ||
| 24 | + return DetectionSpeed.noDuplicates; | ||
| 25 | + case 1: | ||
| 26 | + return DetectionSpeed.normal; | ||
| 27 | + case 2: | ||
| 28 | + return DetectionSpeed.unrestricted; | ||
| 29 | + default: | ||
| 30 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 31 | + } | ||
| 32 | + } | ||
| 33 | + | ||
| 34 | + /// The raw value for the detection speed. | ||
| 35 | + final int rawValue; | ||
| 20 | } | 36 | } |
lib/src/enums/email_type.dart
0 → 100644
| 1 | +/// Email format type constants. | ||
| 2 | +enum EmailType { | ||
| 3 | + /// Unknown email type. | ||
| 4 | + unknown(0), | ||
| 5 | + | ||
| 6 | + /// Work email. | ||
| 7 | + work(1), | ||
| 8 | + | ||
| 9 | + /// Home email. | ||
| 10 | + home(2); | ||
| 11 | + | ||
| 12 | + const EmailType(this.rawValue); | ||
| 13 | + | ||
| 14 | + factory EmailType.fromRawValue(int value) { | ||
| 15 | + switch (value) { | ||
| 16 | + case 0: | ||
| 17 | + return EmailType.unknown; | ||
| 18 | + case 1: | ||
| 19 | + return EmailType.work; | ||
| 20 | + case 2: | ||
| 21 | + return EmailType.home; | ||
| 22 | + default: | ||
| 23 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /// The raw email type value. | ||
| 28 | + final int rawValue; | ||
| 29 | +} |
lib/src/enums/encryption_type.dart
0 → 100644
| 1 | +/// Wifi encryption type constants. | ||
| 2 | +enum EncryptionType { | ||
| 3 | + /// Unknown encryption type. | ||
| 4 | + none(0), | ||
| 5 | + | ||
| 6 | + /// Not encrypted. | ||
| 7 | + open(1), | ||
| 8 | + | ||
| 9 | + /// WPA level encryption. | ||
| 10 | + wpa(2), | ||
| 11 | + | ||
| 12 | + /// WEP level encryption. | ||
| 13 | + wep(3); | ||
| 14 | + | ||
| 15 | + const EncryptionType(this.rawValue); | ||
| 16 | + | ||
| 17 | + factory EncryptionType.fromRawValue(int value) { | ||
| 18 | + switch (value) { | ||
| 19 | + case 0: | ||
| 20 | + return EncryptionType.none; | ||
| 21 | + case 1: | ||
| 22 | + return EncryptionType.open; | ||
| 23 | + case 2: | ||
| 24 | + return EncryptionType.wpa; | ||
| 25 | + case 3: | ||
| 26 | + return EncryptionType.wep; | ||
| 27 | + default: | ||
| 28 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 29 | + } | ||
| 30 | + } | ||
| 31 | + | ||
| 32 | + /// The raw value for the encryption type. | ||
| 33 | + final int rawValue; | ||
| 34 | +} |
| 1 | /// The authorization state of the scanner. | 1 | /// The authorization state of the scanner. |
| 2 | enum MobileScannerState { | 2 | enum MobileScannerState { |
| 3 | /// The scanner has not yet requested the required permissions. | 3 | /// The scanner has not yet requested the required permissions. |
| 4 | - undetermined, | 4 | + undetermined(0), |
| 5 | 5 | ||
| 6 | /// The scanner has the required permissions. | 6 | /// The scanner has the required permissions. |
| 7 | - authorized, | 7 | + authorized(1), |
| 8 | 8 | ||
| 9 | /// The user denied the required permissions. | 9 | /// The user denied the required permissions. |
| 10 | - denied | 10 | + denied(2); |
| 11 | + | ||
| 12 | + const MobileScannerState(this.rawValue); | ||
| 13 | + | ||
| 14 | + factory MobileScannerState.fromRawValue(int value) { | ||
| 15 | + switch (value) { | ||
| 16 | + case 0: | ||
| 17 | + return MobileScannerState.undetermined; | ||
| 18 | + case 1: | ||
| 19 | + return MobileScannerState.authorized; | ||
| 20 | + case 2: | ||
| 21 | + return MobileScannerState.denied; | ||
| 22 | + default: | ||
| 23 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 24 | + } | ||
| 25 | + } | ||
| 26 | + | ||
| 27 | + /// The raw value for the state. | ||
| 28 | + final int rawValue; | ||
| 11 | } | 29 | } |
lib/src/enums/phone_type.dart
0 → 100644
| 1 | +/// Phone number format type constants. | ||
| 2 | +enum PhoneType { | ||
| 3 | + /// Unknown phone type. | ||
| 4 | + unknown(0), | ||
| 5 | + | ||
| 6 | + /// Work phone. | ||
| 7 | + work(1), | ||
| 8 | + | ||
| 9 | + /// Home phone. | ||
| 10 | + home(2), | ||
| 11 | + | ||
| 12 | + /// Fax machine. | ||
| 13 | + fax(3), | ||
| 14 | + | ||
| 15 | + /// Mobile phone. | ||
| 16 | + mobile(4); | ||
| 17 | + | ||
| 18 | + const PhoneType(this.rawValue); | ||
| 19 | + | ||
| 20 | + factory PhoneType.fromRawValue(int value) { | ||
| 21 | + switch (value) { | ||
| 22 | + case 0: | ||
| 23 | + return PhoneType.unknown; | ||
| 24 | + case 1: | ||
| 25 | + return PhoneType.work; | ||
| 26 | + case 2: | ||
| 27 | + return PhoneType.home; | ||
| 28 | + case 3: | ||
| 29 | + return PhoneType.fax; | ||
| 30 | + case 4: | ||
| 31 | + return PhoneType.mobile; | ||
| 32 | + default: | ||
| 33 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 34 | + } | ||
| 35 | + } | ||
| 36 | + | ||
| 37 | + /// The raw phone type value. | ||
| 38 | + final int rawValue; | ||
| 39 | +} |
lib/src/enums/ratio.dart
deleted
100644 → 0
| 1 | -/// The state of torch. | 1 | +/// The state of the flashlight. |
| 2 | enum TorchState { | 2 | enum TorchState { |
| 3 | - /// Torch is off. | ||
| 4 | - off, | 3 | + /// The flashlight is off. |
| 4 | + off(0), | ||
| 5 | 5 | ||
| 6 | - /// Torch is on. | ||
| 7 | - on, | 6 | + /// The flashlight is on. |
| 7 | + on(1); | ||
| 8 | + | ||
| 9 | + const TorchState(this.rawValue); | ||
| 10 | + | ||
| 11 | + factory TorchState.fromRawValue(int value) { | ||
| 12 | + switch (value) { | ||
| 13 | + case 0: | ||
| 14 | + return TorchState.off; | ||
| 15 | + case 1: | ||
| 16 | + return TorchState.on; | ||
| 17 | + default: | ||
| 18 | + throw ArgumentError.value(value, 'value', 'Invalid raw value.'); | ||
| 19 | + } | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + /// The raw value for the torch state. | ||
| 23 | + final int rawValue; | ||
| 8 | } | 24 | } |
| @@ -2,10 +2,13 @@ import 'dart:async'; | @@ -2,10 +2,13 @@ import 'dart:async'; | ||
| 2 | 2 | ||
| 3 | import 'package:flutter/foundation.dart'; | 3 | import 'package:flutter/foundation.dart'; |
| 4 | import 'package:flutter/material.dart'; | 4 | import 'package:flutter/material.dart'; |
| 5 | +import 'package:flutter/services.dart'; | ||
| 6 | +import 'package:mobile_scanner/src/enums/mobile_scanner_error_code.dart'; | ||
| 5 | import 'package:mobile_scanner/src/mobile_scanner_controller.dart'; | 7 | import 'package:mobile_scanner/src/mobile_scanner_controller.dart'; |
| 6 | import 'package:mobile_scanner/src/mobile_scanner_exception.dart'; | 8 | import 'package:mobile_scanner/src/mobile_scanner_exception.dart'; |
| 7 | import 'package:mobile_scanner/src/objects/barcode_capture.dart'; | 9 | import 'package:mobile_scanner/src/objects/barcode_capture.dart'; |
| 8 | import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart'; | 10 | import 'package:mobile_scanner/src/objects/mobile_scanner_arguments.dart'; |
| 11 | +import 'package:mobile_scanner/src/scan_window_calculation.dart'; | ||
| 9 | 12 | ||
| 10 | /// The function signature for the error builder. | 13 | /// The function signature for the error builder. |
| 11 | typedef MobileScannerErrorBuilder = Widget Function( | 14 | typedef MobileScannerErrorBuilder = Widget Function( |
| @@ -63,7 +66,7 @@ class MobileScanner extends StatefulWidget { | @@ -63,7 +66,7 @@ class MobileScanner extends StatefulWidget { | ||
| 63 | final bool startDelay; | 66 | final bool startDelay; |
| 64 | 67 | ||
| 65 | /// The overlay which will be painted above the scanner when has started successful. | 68 | /// The overlay which will be painted above the scanner when has started successful. |
| 66 | - /// Will no be pointed when an error occurs or the scanner hasn't be started yet. | 69 | + /// Will no be pointed when an error occurs or the scanner hasn't been started yet. |
| 67 | final Widget? overlay; | 70 | final Widget? overlay; |
| 68 | 71 | ||
| 69 | /// Create a new [MobileScanner] using the provided [controller] | 72 | /// Create a new [MobileScanner] using the provided [controller] |
| @@ -137,11 +140,31 @@ class _MobileScannerState extends State<MobileScanner> | @@ -137,11 +140,31 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 137 | widget.onStart?.call(arguments); | 140 | widget.onStart?.call(arguments); |
| 138 | widget.onScannerStarted?.call(arguments); | 141 | widget.onScannerStarted?.call(arguments); |
| 139 | }).catchError((error) { | 142 | }).catchError((error) { |
| 140 | - if (mounted) { | ||
| 141 | - setState(() { | ||
| 142 | - _startException = error as MobileScannerException; | ||
| 143 | - }); | 143 | + if (!mounted) { |
| 144 | + return; | ||
| 145 | + } | ||
| 146 | + | ||
| 147 | + if (error is MobileScannerException) { | ||
| 148 | + _startException = error; | ||
| 149 | + } else if (error is PlatformException) { | ||
| 150 | + _startException = MobileScannerException( | ||
| 151 | + errorCode: MobileScannerErrorCode.genericError, | ||
| 152 | + errorDetails: MobileScannerErrorDetails( | ||
| 153 | + code: error.code, | ||
| 154 | + message: error.message, | ||
| 155 | + details: error.details, | ||
| 156 | + ), | ||
| 157 | + ); | ||
| 158 | + } else { | ||
| 159 | + _startException = MobileScannerException( | ||
| 160 | + errorCode: MobileScannerErrorCode.genericError, | ||
| 161 | + errorDetails: MobileScannerErrorDetails( | ||
| 162 | + details: error, | ||
| 163 | + ), | ||
| 164 | + ); | ||
| 144 | } | 165 | } |
| 166 | + | ||
| 167 | + setState(() {}); | ||
| 145 | }); | 168 | }); |
| 146 | } | 169 | } |
| 147 | 170 | ||
| @@ -175,75 +198,6 @@ class _MobileScannerState extends State<MobileScanner> | @@ -175,75 +198,6 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 175 | } | 198 | } |
| 176 | } | 199 | } |
| 177 | 200 | ||
| 178 | - /// the [scanWindow] rect will be relative and scaled to the [widgetSize] not the texture. so it is possible, | ||
| 179 | - /// depending on the [fit], for the [scanWindow] to partially or not at all overlap the [textureSize] | ||
| 180 | - /// | ||
| 181 | - /// since when using a [BoxFit] the content will always be centered on its parent. we can convert the rect | ||
| 182 | - /// to be relative to the texture. | ||
| 183 | - /// | ||
| 184 | - /// since the textures size and the actuall image (on the texture size) might not be the same, we also need to | ||
| 185 | - /// calculate the scanWindow in terms of percentages of the texture, not pixels. | ||
| 186 | - Rect calculateScanWindowRelativeToTextureInPercentage( | ||
| 187 | - BoxFit fit, | ||
| 188 | - Rect scanWindow, | ||
| 189 | - Size textureSize, | ||
| 190 | - Size widgetSize, | ||
| 191 | - ) { | ||
| 192 | - double fittedTextureWidth; | ||
| 193 | - double fittedTextureHeight; | ||
| 194 | - | ||
| 195 | - switch (fit) { | ||
| 196 | - case BoxFit.contain: | ||
| 197 | - final widthRatio = widgetSize.width / textureSize.width; | ||
| 198 | - final heightRatio = widgetSize.height / textureSize.height; | ||
| 199 | - final scale = widthRatio < heightRatio ? widthRatio : heightRatio; | ||
| 200 | - fittedTextureWidth = textureSize.width * scale; | ||
| 201 | - fittedTextureHeight = textureSize.height * scale; | ||
| 202 | - break; | ||
| 203 | - | ||
| 204 | - case BoxFit.cover: | ||
| 205 | - final widthRatio = widgetSize.width / textureSize.width; | ||
| 206 | - final heightRatio = widgetSize.height / textureSize.height; | ||
| 207 | - final scale = widthRatio > heightRatio ? widthRatio : heightRatio; | ||
| 208 | - fittedTextureWidth = textureSize.width * scale; | ||
| 209 | - fittedTextureHeight = textureSize.height * scale; | ||
| 210 | - break; | ||
| 211 | - | ||
| 212 | - case BoxFit.fill: | ||
| 213 | - fittedTextureWidth = widgetSize.width; | ||
| 214 | - fittedTextureHeight = widgetSize.height; | ||
| 215 | - break; | ||
| 216 | - | ||
| 217 | - case BoxFit.fitHeight: | ||
| 218 | - final ratio = widgetSize.height / textureSize.height; | ||
| 219 | - fittedTextureWidth = textureSize.width * ratio; | ||
| 220 | - fittedTextureHeight = widgetSize.height; | ||
| 221 | - break; | ||
| 222 | - | ||
| 223 | - case BoxFit.fitWidth: | ||
| 224 | - final ratio = widgetSize.width / textureSize.width; | ||
| 225 | - fittedTextureWidth = widgetSize.width; | ||
| 226 | - fittedTextureHeight = textureSize.height * ratio; | ||
| 227 | - break; | ||
| 228 | - | ||
| 229 | - case BoxFit.none: | ||
| 230 | - case BoxFit.scaleDown: | ||
| 231 | - fittedTextureWidth = textureSize.width; | ||
| 232 | - fittedTextureHeight = textureSize.height; | ||
| 233 | - break; | ||
| 234 | - } | ||
| 235 | - | ||
| 236 | - final offsetX = (widgetSize.width - fittedTextureWidth) / 2; | ||
| 237 | - final offsetY = (widgetSize.height - fittedTextureHeight) / 2; | ||
| 238 | - | ||
| 239 | - final left = (scanWindow.left - offsetX) / fittedTextureWidth; | ||
| 240 | - final top = (scanWindow.top - offsetY) / fittedTextureHeight; | ||
| 241 | - final right = (scanWindow.right - offsetX) / fittedTextureWidth; | ||
| 242 | - final bottom = (scanWindow.bottom - offsetY) / fittedTextureHeight; | ||
| 243 | - | ||
| 244 | - return Rect.fromLTRB(left, top, right, bottom); | ||
| 245 | - } | ||
| 246 | - | ||
| 247 | Rect? scanWindow; | 201 | Rect? scanWindow; |
| 248 | 202 | ||
| 249 | @override | 203 | @override |
| @@ -261,8 +215,8 @@ class _MobileScannerState extends State<MobileScanner> | @@ -261,8 +215,8 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 261 | scanWindow = calculateScanWindowRelativeToTextureInPercentage( | 215 | scanWindow = calculateScanWindowRelativeToTextureInPercentage( |
| 262 | widget.fit, | 216 | widget.fit, |
| 263 | widget.scanWindow!, | 217 | widget.scanWindow!, |
| 264 | - value.size, | ||
| 265 | - Size(constraints.maxWidth, constraints.maxHeight), | 218 | + textureSize: value.size, |
| 219 | + widgetSize: constraints.biggest, | ||
| 266 | ); | 220 | ); |
| 267 | 221 | ||
| 268 | _controller.updateScanWindow(scanWindow); | 222 | _controller.updateScanWindow(scanWindow); |
| @@ -271,12 +225,22 @@ class _MobileScannerState extends State<MobileScanner> | @@ -271,12 +225,22 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 271 | return Stack( | 225 | return Stack( |
| 272 | alignment: Alignment.center, | 226 | alignment: Alignment.center, |
| 273 | children: [ | 227 | children: [ |
| 274 | - _scanner(value.size, value.webId, value.textureId), | 228 | + _scanner( |
| 229 | + value.size, | ||
| 230 | + value.webId, | ||
| 231 | + value.textureId, | ||
| 232 | + value.nrOfCameras, | ||
| 233 | + ), | ||
| 275 | widget.overlay!, | 234 | widget.overlay!, |
| 276 | ], | 235 | ], |
| 277 | ); | 236 | ); |
| 278 | } else { | 237 | } else { |
| 279 | - return _scanner(value.size, value.webId, value.textureId); | 238 | + return _scanner( |
| 239 | + value.size, | ||
| 240 | + value.webId, | ||
| 241 | + value.textureId, | ||
| 242 | + value.nrOfCameras, | ||
| 243 | + ); | ||
| 280 | } | 244 | } |
| 281 | }, | 245 | }, |
| 282 | ); | 246 | ); |
| @@ -284,7 +248,7 @@ class _MobileScannerState extends State<MobileScanner> | @@ -284,7 +248,7 @@ class _MobileScannerState extends State<MobileScanner> | ||
| 284 | ); | 248 | ); |
| 285 | } | 249 | } |
| 286 | 250 | ||
| 287 | - Widget _scanner(Size size, String? webId, int? textureId) { | 251 | + Widget _scanner(Size size, String? webId, int? textureId, int? nrOfCameras) { |
| 288 | return ClipRect( | 252 | return ClipRect( |
| 289 | child: LayoutBuilder( | 253 | child: LayoutBuilder( |
| 290 | builder: (_, constraints) { | 254 | builder: (_, constraints) { |
| @@ -6,7 +6,6 @@ import 'package:flutter/cupertino.dart'; | @@ -6,7 +6,6 @@ import 'package:flutter/cupertino.dart'; | ||
| 6 | import 'package:flutter/foundation.dart'; | 6 | import 'package:flutter/foundation.dart'; |
| 7 | import 'package:flutter/services.dart'; | 7 | import 'package:flutter/services.dart'; |
| 8 | import 'package:mobile_scanner/mobile_scanner.dart'; | 8 | import 'package:mobile_scanner/mobile_scanner.dart'; |
| 9 | -import 'package:mobile_scanner/src/barcode_utility.dart'; | ||
| 10 | 9 | ||
| 11 | /// The [MobileScannerController] holds all the logic of this plugin, | 10 | /// The [MobileScannerController] holds all the logic of this plugin, |
| 12 | /// where as the [MobileScanner] class is the frontend of this plugin. | 11 | /// where as the [MobileScanner] class is the frontend of this plugin. |
| @@ -23,6 +22,8 @@ class MobileScannerController { | @@ -23,6 +22,8 @@ class MobileScannerController { | ||
| 23 | ) | 22 | ) |
| 24 | this.onPermissionSet, | 23 | this.onPermissionSet, |
| 25 | this.autoStart = true, | 24 | this.autoStart = true, |
| 25 | + this.cameraResolution, | ||
| 26 | + this.useNewCameraSelector = false, | ||
| 26 | }); | 27 | }); |
| 27 | 28 | ||
| 28 | /// Select which camera should be used. | 29 | /// Select which camera should be used. |
| @@ -48,19 +49,42 @@ class MobileScannerController { | @@ -48,19 +49,42 @@ class MobileScannerController { | ||
| 48 | /// WARNING: DetectionSpeed.unrestricted can cause memory issues on some devices | 49 | /// WARNING: DetectionSpeed.unrestricted can cause memory issues on some devices |
| 49 | final DetectionSpeed detectionSpeed; | 50 | final DetectionSpeed detectionSpeed; |
| 50 | 51 | ||
| 51 | - /// Sets the timeout of scanner. | ||
| 52 | - /// The timeout is set in miliseconds. | 52 | + /// Sets the timeout, in milliseconds, of the scanner. |
| 53 | /// | 53 | /// |
| 54 | - /// NOTE: The timeout only works if the [detectionSpeed] is set to | ||
| 55 | - /// [DetectionSpeed.normal] (which is the default value). | 54 | + /// This timeout is ignored if the [detectionSpeed] |
| 55 | + /// is not set to [DetectionSpeed.normal]. | ||
| 56 | + /// | ||
| 57 | + /// By default this is set to `250` milliseconds, | ||
| 58 | + /// which prevents memory issues on older devices. | ||
| 56 | final int detectionTimeoutMs; | 59 | final int detectionTimeoutMs; |
| 57 | 60 | ||
| 58 | /// Automatically start the mobileScanner on initialization. | 61 | /// Automatically start the mobileScanner on initialization. |
| 59 | final bool autoStart; | 62 | final bool autoStart; |
| 60 | 63 | ||
| 64 | + /// The desired resolution for the camera. | ||
| 65 | + /// | ||
| 66 | + /// When this value is provided, the camera will try to match this resolution, | ||
| 67 | + /// or fallback to the closest available resolution. | ||
| 68 | + /// When this is null, Android defaults to a resolution of 640x480. | ||
| 69 | + /// | ||
| 70 | + /// Bear in mind that changing the resolution has an effect on the aspect ratio. | ||
| 71 | + /// | ||
| 72 | + /// When the camera orientation changes, | ||
| 73 | + /// the resolution will be flipped to match the new dimensions of the display. | ||
| 74 | + /// | ||
| 75 | + /// Currently only supported on Android. | ||
| 76 | + final Size? cameraResolution; | ||
| 77 | + | ||
| 78 | + /// Use the new resolution selector. Warning: not fully tested, may produce | ||
| 79 | + /// unwanted/zoomed images. | ||
| 80 | + /// | ||
| 81 | + /// Only supported on Android | ||
| 82 | + final bool useNewCameraSelector; | ||
| 83 | + | ||
| 61 | /// Sets the barcode stream | 84 | /// Sets the barcode stream |
| 62 | final StreamController<BarcodeCapture> _barcodesController = | 85 | final StreamController<BarcodeCapture> _barcodesController = |
| 63 | StreamController.broadcast(); | 86 | StreamController.broadcast(); |
| 87 | + | ||
| 64 | Stream<BarcodeCapture> get barcodes => _barcodesController.stream; | 88 | Stream<BarcodeCapture> get barcodes => _barcodesController.stream; |
| 65 | 89 | ||
| 66 | static const MethodChannel _methodChannel = | 90 | static const MethodChannel _methodChannel = |
| @@ -114,10 +138,12 @@ class MobileScannerController { | @@ -114,10 +138,12 @@ class MobileScannerController { | ||
| 114 | final Map<String, dynamic> arguments = {}; | 138 | final Map<String, dynamic> arguments = {}; |
| 115 | 139 | ||
| 116 | cameraFacingState.value = cameraFacingOverride ?? facing; | 140 | cameraFacingState.value = cameraFacingOverride ?? facing; |
| 117 | - arguments['facing'] = cameraFacingState.value.index; | 141 | + arguments['facing'] = cameraFacingState.value.rawValue; |
| 118 | arguments['torch'] = torchEnabled; | 142 | arguments['torch'] = torchEnabled; |
| 119 | - arguments['speed'] = detectionSpeed.index; | 143 | + arguments['speed'] = detectionSpeed.rawValue; |
| 120 | arguments['timeout'] = detectionTimeoutMs; | 144 | arguments['timeout'] = detectionTimeoutMs; |
| 145 | + arguments['returnImage'] = returnImage; | ||
| 146 | + arguments['useNewCameraSelector'] = useNewCameraSelector; | ||
| 121 | 147 | ||
| 122 | /* if (scanWindow != null) { | 148 | /* if (scanWindow != null) { |
| 123 | arguments['scanWindow'] = [ | 149 | arguments['scanWindow'] = [ |
| @@ -129,13 +155,18 @@ class MobileScannerController { | @@ -129,13 +155,18 @@ class MobileScannerController { | ||
| 129 | } */ | 155 | } */ |
| 130 | 156 | ||
| 131 | if (formats != null) { | 157 | if (formats != null) { |
| 132 | - if (kIsWeb || Platform.isIOS || Platform.isMacOS) { | 158 | + if (kIsWeb || Platform.isIOS || Platform.isMacOS || Platform.isAndroid) { |
| 133 | arguments['formats'] = formats!.map((e) => e.rawValue).toList(); | 159 | arguments['formats'] = formats!.map((e) => e.rawValue).toList(); |
| 134 | - } else if (Platform.isAndroid) { | ||
| 135 | - arguments['formats'] = formats!.map((e) => e.index).toList(); | ||
| 136 | } | 160 | } |
| 137 | } | 161 | } |
| 138 | - arguments['returnImage'] = returnImage; | 162 | + |
| 163 | + if (cameraResolution != null) { | ||
| 164 | + arguments['cameraResolution'] = <int>[ | ||
| 165 | + cameraResolution!.width.toInt(), | ||
| 166 | + cameraResolution!.height.toInt(), | ||
| 167 | + ]; | ||
| 168 | + } | ||
| 169 | + | ||
| 139 | return arguments; | 170 | return arguments; |
| 140 | } | 171 | } |
| 141 | 172 | ||
| @@ -163,35 +194,53 @@ class MobileScannerController { | @@ -163,35 +194,53 @@ class MobileScannerController { | ||
| 163 | 194 | ||
| 164 | // Check authorization status | 195 | // Check authorization status |
| 165 | if (!kIsWeb) { | 196 | if (!kIsWeb) { |
| 166 | - final MobileScannerState state = MobileScannerState | ||
| 167 | - .values[await _methodChannel.invokeMethod('state') as int? ?? 0]; | ||
| 168 | - switch (state) { | ||
| 169 | - case MobileScannerState.undetermined: | ||
| 170 | - bool result = false; | 197 | + final MobileScannerState state; |
| 171 | 198 | ||
| 172 | try { | 199 | try { |
| 173 | - result = | ||
| 174 | - await _methodChannel.invokeMethod('request') as bool? ?? false; | ||
| 175 | - } catch (error) { | 200 | + state = MobileScannerState.fromRawValue( |
| 201 | + await _methodChannel.invokeMethod('state') as int? ?? 0, | ||
| 202 | + ); | ||
| 203 | + } on PlatformException catch (error) { | ||
| 176 | isStarting = false; | 204 | isStarting = false; |
| 177 | - throw const MobileScannerException( | 205 | + |
| 206 | + throw MobileScannerException( | ||
| 178 | errorCode: MobileScannerErrorCode.genericError, | 207 | errorCode: MobileScannerErrorCode.genericError, |
| 208 | + errorDetails: MobileScannerErrorDetails( | ||
| 209 | + code: error.code, | ||
| 210 | + details: error.details as Object?, | ||
| 211 | + message: error.message, | ||
| 212 | + ), | ||
| 179 | ); | 213 | ); |
| 180 | } | 214 | } |
| 181 | 215 | ||
| 182 | - if (!result) { | 216 | + switch (state) { |
| 217 | + // Android does not have an undetermined permission state. | ||
| 218 | + // So if the permission state is denied, just request it now. | ||
| 219 | + case MobileScannerState.undetermined: | ||
| 220 | + case MobileScannerState.denied: | ||
| 221 | + try { | ||
| 222 | + final bool granted = | ||
| 223 | + await _methodChannel.invokeMethod('request') as bool? ?? false; | ||
| 224 | + | ||
| 225 | + if (!granted) { | ||
| 183 | isStarting = false; | 226 | isStarting = false; |
| 184 | throw const MobileScannerException( | 227 | throw const MobileScannerException( |
| 185 | errorCode: MobileScannerErrorCode.permissionDenied, | 228 | errorCode: MobileScannerErrorCode.permissionDenied, |
| 186 | ); | 229 | ); |
| 187 | } | 230 | } |
| 188 | - | ||
| 189 | - break; | ||
| 190 | - case MobileScannerState.denied: | 231 | + } on PlatformException catch (error) { |
| 191 | isStarting = false; | 232 | isStarting = false; |
| 192 | - throw const MobileScannerException( | ||
| 193 | - errorCode: MobileScannerErrorCode.permissionDenied, | 233 | + throw MobileScannerException( |
| 234 | + errorCode: MobileScannerErrorCode.genericError, | ||
| 235 | + errorDetails: MobileScannerErrorDetails( | ||
| 236 | + code: error.code, | ||
| 237 | + details: error.details as Object?, | ||
| 238 | + message: error.message, | ||
| 239 | + ), | ||
| 194 | ); | 240 | ); |
| 241 | + } | ||
| 242 | + | ||
| 243 | + break; | ||
| 195 | case MobileScannerState.authorized: | 244 | case MobileScannerState.authorized: |
| 196 | break; | 245 | break; |
| 197 | } | 246 | } |
| @@ -243,18 +292,28 @@ class MobileScannerController { | @@ -243,18 +292,28 @@ class MobileScannerController { | ||
| 243 | 292 | ||
| 244 | final hasTorch = startResult['torchable'] as bool? ?? false; | 293 | final hasTorch = startResult['torchable'] as bool? ?? false; |
| 245 | hasTorchState.value = hasTorch; | 294 | hasTorchState.value = hasTorch; |
| 246 | - if (hasTorch && torchEnabled) { | ||
| 247 | - torchState.value = TorchState.on; | 295 | + |
| 296 | + final Size size; | ||
| 297 | + | ||
| 298 | + if (kIsWeb) { | ||
| 299 | + size = Size( | ||
| 300 | + startResult['videoWidth'] as double? ?? 0, | ||
| 301 | + startResult['videoHeight'] as double? ?? 0, | ||
| 302 | + ); | ||
| 303 | + } else { | ||
| 304 | + final Map<Object?, Object?>? sizeInfo = | ||
| 305 | + startResult['size'] as Map<Object?, Object?>?; | ||
| 306 | + | ||
| 307 | + size = Size( | ||
| 308 | + sizeInfo?['width'] as double? ?? 0, | ||
| 309 | + sizeInfo?['height'] as double? ?? 0, | ||
| 310 | + ); | ||
| 248 | } | 311 | } |
| 249 | 312 | ||
| 250 | isStarting = false; | 313 | isStarting = false; |
| 251 | return startArguments.value = MobileScannerArguments( | 314 | return startArguments.value = MobileScannerArguments( |
| 252 | - size: kIsWeb | ||
| 253 | - ? Size( | ||
| 254 | - startResult['videoWidth'] as double? ?? 0, | ||
| 255 | - startResult['videoHeight'] as double? ?? 0, | ||
| 256 | - ) | ||
| 257 | - : toSize(startResult['size'] as Map? ?? {}), | 315 | + nrOfCameras: startResult['nrOfCameras'] as int?, |
| 316 | + size: size, | ||
| 258 | hasTorch: hasTorch, | 317 | hasTorch: hasTorch, |
| 259 | textureId: kIsWeb ? null : startResult['textureId'] as int?, | 318 | textureId: kIsWeb ? null : startResult['textureId'] as int?, |
| 260 | webId: kIsWeb ? startResult['ViewID'] as String? : null, | 319 | webId: kIsWeb ? startResult['ViewID'] as String? : null, |
| @@ -263,11 +322,11 @@ class MobileScannerController { | @@ -263,11 +322,11 @@ class MobileScannerController { | ||
| 263 | 322 | ||
| 264 | /// Stops the camera, but does not dispose this controller. | 323 | /// Stops the camera, but does not dispose this controller. |
| 265 | Future<void> stop() async { | 324 | Future<void> stop() async { |
| 266 | - try { | ||
| 267 | await _methodChannel.invokeMethod('stop'); | 325 | await _methodChannel.invokeMethod('stop'); |
| 268 | - } catch (e) { | ||
| 269 | - debugPrint('$e'); | ||
| 270 | - } | 326 | + |
| 327 | + // After the camera stopped, set the torch state to off, | ||
| 328 | + // as the torch state callback is never called when the camera is stopped. | ||
| 329 | + torchState.value = TorchState.off; | ||
| 271 | } | 330 | } |
| 272 | 331 | ||
| 273 | /// Switches the torch on or off. | 332 | /// Switches the torch on or off. |
| @@ -282,14 +341,16 @@ class MobileScannerController { | @@ -282,14 +341,16 @@ class MobileScannerController { | ||
| 282 | throw const MobileScannerException( | 341 | throw const MobileScannerException( |
| 283 | errorCode: MobileScannerErrorCode.controllerUninitialized, | 342 | errorCode: MobileScannerErrorCode.controllerUninitialized, |
| 284 | ); | 343 | ); |
| 285 | - } else if (!hasTorch) { | 344 | + } |
| 345 | + | ||
| 346 | + if (!hasTorch) { | ||
| 286 | return; | 347 | return; |
| 287 | } | 348 | } |
| 288 | 349 | ||
| 289 | - torchState.value = | 350 | + final TorchState newState = |
| 290 | torchState.value == TorchState.off ? TorchState.on : TorchState.off; | 351 | torchState.value == TorchState.off ? TorchState.on : TorchState.off; |
| 291 | 352 | ||
| 292 | - await _methodChannel.invokeMethod('torch', torchState.value.index); | 353 | + await _methodChannel.invokeMethod('torch', newState.rawValue); |
| 293 | } | 354 | } |
| 294 | 355 | ||
| 295 | /// Changes the state of the camera (front or back). | 356 | /// Changes the state of the camera (front or back). |
| @@ -384,6 +445,9 @@ class MobileScannerController { | @@ -384,6 +445,9 @@ class MobileScannerController { | ||
| 384 | barcodes: [ | 445 | barcodes: [ |
| 385 | Barcode( | 446 | Barcode( |
| 386 | rawValue: (data as Map)['payload'] as String?, | 447 | rawValue: (data as Map)['payload'] as String?, |
| 448 | + format: BarcodeFormat.fromRawValue( | ||
| 449 | + data['symbology'] as int? ?? -1, | ||
| 450 | + ), | ||
| 387 | ), | 451 | ), |
| 388 | ], | 452 | ], |
| 389 | ), | 453 | ), |
| @@ -391,6 +455,8 @@ class MobileScannerController { | @@ -391,6 +455,8 @@ class MobileScannerController { | ||
| 391 | break; | 455 | break; |
| 392 | case 'barcodeWeb': | 456 | case 'barcodeWeb': |
| 393 | final barcode = data as Map?; | 457 | final barcode = data as Map?; |
| 458 | + final corners = barcode?['corners'] as List<Object?>? ?? <Object?>[]; | ||
| 459 | + | ||
| 394 | _barcodesController.add( | 460 | _barcodesController.add( |
| 395 | BarcodeCapture( | 461 | BarcodeCapture( |
| 396 | raw: data, | 462 | raw: data, |
| @@ -399,10 +465,15 @@ class MobileScannerController { | @@ -399,10 +465,15 @@ class MobileScannerController { | ||
| 399 | Barcode( | 465 | Barcode( |
| 400 | rawValue: barcode['rawValue'] as String?, | 466 | rawValue: barcode['rawValue'] as String?, |
| 401 | rawBytes: barcode['rawBytes'] as Uint8List?, | 467 | rawBytes: barcode['rawBytes'] as Uint8List?, |
| 402 | - format: toFormat(barcode['format'] as int), | ||
| 403 | - corners: toCorners( | ||
| 404 | - (barcode['corners'] as List<Object?>? ?? []) | ||
| 405 | - .cast<Map<Object?, Object?>>(), | 468 | + format: BarcodeFormat.fromRawValue( |
| 469 | + barcode['format'] as int? ?? -1, | ||
| 470 | + ), | ||
| 471 | + corners: List.unmodifiable( | ||
| 472 | + corners.cast<Map<Object?, Object?>>().map( | ||
| 473 | + (Map<Object?, Object?> e) { | ||
| 474 | + return Offset(e['x']! as double, e['y']! as double); | ||
| 475 | + }, | ||
| 476 | + ), | ||
| 406 | ), | 477 | ), |
| 407 | ), | 478 | ), |
| 408 | ], | 479 | ], |
lib/src/objects/address.dart
0 → 100644
| 1 | +import 'package:mobile_scanner/src/enums/address_type.dart'; | ||
| 2 | + | ||
| 3 | +/// An address. | ||
| 4 | +class Address { | ||
| 5 | + /// Creates a new [Address] instance. | ||
| 6 | + const Address({ | ||
| 7 | + this.addressLines = const <String>[], | ||
| 8 | + this.type = AddressType.unknown, | ||
| 9 | + }); | ||
| 10 | + | ||
| 11 | + /// Creates a new [Address] instance from a map. | ||
| 12 | + factory Address.fromNative(Map<Object?, Object?> data) { | ||
| 13 | + final List<Object?>? addressLines = data['addressLines'] as List<Object?>?; | ||
| 14 | + final AddressType type = AddressType.fromRawValue( | ||
| 15 | + data['type'] as int? ?? 0, | ||
| 16 | + ); | ||
| 17 | + | ||
| 18 | + if (addressLines == null) { | ||
| 19 | + return Address(type: type); | ||
| 20 | + } | ||
| 21 | + | ||
| 22 | + return Address( | ||
| 23 | + addressLines: List.unmodifiable(addressLines.cast<String>()), | ||
| 24 | + type: type, | ||
| 25 | + ); | ||
| 26 | + } | ||
| 27 | + | ||
| 28 | + /// The address lines that represent this address. | ||
| 29 | + final List<String> addressLines; | ||
| 30 | + | ||
| 31 | + /// Gets type of the address. | ||
| 32 | + final AddressType type; | ||
| 33 | +} |
-
Please register or login to post a comment