Julian Steenbakker

Merge branch 'master' into feature/scan-window

# Conflicts:
#	lib/src/mobile_scanner.dart
1 name: code analysis & formatting 1 name: code analysis & formatting
2 2
3 -on: [push, pull_request] 3 +on:
  4 + push:
  5 + branches:
  6 + - master
  7 + pull_request:
  8 + types: [ opened, labeled, unlabeled, synchronize ]
4 9
5 jobs: 10 jobs:
6 analysis: 11 analysis:
7 runs-on: ubuntu-latest 12 runs-on: ubuntu-latest
8 steps: 13 steps:
9 - - uses: actions/checkout@v3  
10 - - uses: actions/setup-java@v3 14 + - uses: actions/checkout@v3.0.2
  15 + - uses: actions/setup-java@v3.4.1
11 with: 16 with:
12 - distribution: 'temurin'  
13 - java-version: '11'  
14 - - uses: subosito/flutter-action@v2.4.0 17 + java-version: 11
  18 + distribution: temurin
  19 + - uses: subosito/flutter-action@v2.6.1
  20 + with:
  21 + cache: true
15 - name: Version 22 - name: Version
16 run: flutter doctor -v 23 run: flutter doctor -v
17 - name: Install dependencies 24 - name: Install dependencies
@@ -21,11 +28,13 @@ jobs: @@ -21,11 +28,13 @@ jobs:
21 formatting: 28 formatting:
22 runs-on: ubuntu-latest 29 runs-on: ubuntu-latest
23 steps: 30 steps:
24 - - uses: actions/checkout@v3  
25 - - uses: actions/setup-java@v3 31 + - uses: actions/checkout@v3.0.2
  32 + - uses: actions/setup-java@v3.4.1
  33 + with:
  34 + java-version: 11
  35 + distribution: temurin
  36 + - uses: subosito/flutter-action@v2.6.1
26 with: 37 with:
27 - distribution: 'temurin'  
28 - java-version: '11'  
29 - - uses: subosito/flutter-action@v2.4.0 38 + cache: true
30 - name: Format 39 - name: Format
31 - run: flutter format -n --set-exit-if-changed .  
  40 + run: flutter format -n --set-exit-if-changed .
@@ -2,7 +2,7 @@ group 'dev.steenbakker.mobile_scanner' @@ -2,7 +2,7 @@ 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.6.21' 5 + ext.kotlin_version = '1.7.10'
6 repositories { 6 repositories {
7 google() 7 google()
8 mavenCentral() 8 mavenCentral()
@@ -52,8 +52,8 @@ dependencies { @@ -52,8 +52,8 @@ dependencies {
52 // Use this dependency to use the dynamically downloaded model in Google Play Services 52 // Use this dependency to use the dynamically downloaded model in Google Play Services
53 // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.0.0' 53 // implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.0.0'
54 54
55 - implementation "androidx.camera:camera-camera2:1.2.0-alpha01"  
56 - implementation 'androidx.camera:camera-lifecycle:1.2.0-alpha01' 55 + implementation "androidx.camera:camera-camera2:1.2.0-alpha04"
  56 + implementation 'androidx.camera:camera-lifecycle:1.2.0-alpha04'
57 57
58 // // The following line is optional, as the core library is included indirectly by camera-camera2 58 // // The following line is optional, as the core library is included indirectly by camera-camera2
59 // implementation "androidx.camera:camera-core:1.1.0-alpha11" 59 // implementation "androidx.camera:camera-core:1.1.0-alpha11"
1 buildscript { 1 buildscript {
2 - ext.kotlin_version = '1.6.21' 2 + ext.kotlin_version = '1.7.10'
3 repositories { 3 repositories {
4 google() 4 google()
5 mavenCentral() 5 mavenCentral()
@@ -133,9 +133,9 @@ class _BarcodeScannerWithControllerState @@ -133,9 +133,9 @@ class _BarcodeScannerWithControllerState
133 icon: const Icon(Icons.image), 133 icon: const Icon(Icons.image),
134 iconSize: 32.0, 134 iconSize: 32.0,
135 onPressed: () async { 135 onPressed: () async {
136 - final ImagePicker _picker = ImagePicker(); 136 + final ImagePicker picker = ImagePicker();
137 // Pick an image 137 // Pick an image
138 - final XFile? image = await _picker.pickImage( 138 + final XFile? image = await picker.pickImage(
139 source: ImageSource.gallery, 139 source: ImageSource.gallery,
140 ); 140 );
141 if (image != null) { 141 if (image != null) {
@@ -16,7 +16,7 @@ dependencies: @@ -16,7 +16,7 @@ dependencies:
16 dev_dependencies: 16 dev_dependencies:
17 flutter_test: 17 flutter_test:
18 sdk: flutter 18 sdk: flutter
19 - lint: ^1.8.2 19 + lint: ^1.10.0
20 20
21 flutter: 21 flutter:
22 uses-material-design: true 22 uses-material-design: true
@@ -37,13 +37,12 @@ class MobileScanner extends StatefulWidget { @@ -37,13 +37,12 @@ class MobileScanner extends StatefulWidget {
37 37
38 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized. 38 /// Create a [MobileScanner] with a [controller], the [controller] must has been initialized.
39 const MobileScanner({ 39 const MobileScanner({
40 - Key? key, 40 + super.key,
41 required this.onDetect, 41 required this.onDetect,
42 this.controller, 42 this.controller,
43 this.fit = BoxFit.cover, 43 this.fit = BoxFit.cover,
44 this.allowDuplicates = false, 44 this.allowDuplicates = false,
45 - this.scanWindow,  
46 - }) : super(key: key); 45 + });
47 46
48 @override 47 @override
49 State<MobileScanner> createState() => _MobileScannerState(); 48 State<MobileScanner> createState() => _MobileScannerState();
@@ -58,13 +57,14 @@ class _MobileScannerState extends State<MobileScanner> @@ -58,13 +57,14 @@ class _MobileScannerState extends State<MobileScanner>
58 super.initState(); 57 super.initState();
59 WidgetsBinding.instance.addObserver(this); 58 WidgetsBinding.instance.addObserver(this);
60 controller = widget.controller ?? MobileScannerController(); 59 controller = widget.controller ?? MobileScannerController();
  60 + if (!controller.isStarting) controller.start();
61 } 61 }
62 62
63 @override 63 @override
64 void didChangeAppLifecycleState(AppLifecycleState state) { 64 void didChangeAppLifecycleState(AppLifecycleState state) {
65 switch (state) { 65 switch (state) {
66 case AppLifecycleState.resumed: 66 case AppLifecycleState.resumed:
67 - if (!controller.isStarting) controller.start(); 67 + if (!controller.isStarting && controller.autoResume) controller.start();
68 break; 68 break;
69 case AppLifecycleState.inactive: 69 case AppLifecycleState.inactive:
70 case AppLifecycleState.paused: 70 case AppLifecycleState.paused:
@@ -134,7 +134,7 @@ class _MobileScannerState extends State<MobileScanner> @@ -134,7 +134,7 @@ class _MobileScannerState extends State<MobileScanner>
134 builder: (context, value, child) { 134 builder: (context, value, child) {
135 value = value as MobileScannerArguments?; 135 value = value as MobileScannerArguments?;
136 if (value == null) { 136 if (value == null) {
137 - return Container(color: Colors.black); 137 + return const ColoredBox(color: Colors.black);
138 } else { 138 } else {
139 if (widget.scanWindow != null) { 139 if (widget.scanWindow != null) {
140 final window = calculateScanWindowRelativeToTextureInPercentage( 140 final window = calculateScanWindowRelativeToTextureInPercentage(
@@ -35,7 +35,8 @@ class MobileScannerController { @@ -35,7 +35,8 @@ class MobileScannerController {
35 EventChannel eventChannel = 35 EventChannel eventChannel =
36 const EventChannel('dev.steenbakker.mobile_scanner/scanner/event'); 36 const EventChannel('dev.steenbakker.mobile_scanner/scanner/event');
37 37
38 - int? _controllerHashcode; 38 + //Must be static to keep the same value on new instances
  39 + static int? _controllerHashcode;
39 StreamSubscription? events; 40 StreamSubscription? events;
40 41
41 final ValueNotifier<MobileScannerArguments?> args = ValueNotifier(null); 42 final ValueNotifier<MobileScannerArguments?> args = ValueNotifier(null);
@@ -53,6 +54,9 @@ class MobileScannerController { @@ -53,6 +54,9 @@ class MobileScannerController {
53 bool hasTorch = false; 54 bool hasTorch = false;
54 late StreamController<Barcode> barcodesController; 55 late StreamController<Barcode> barcodesController;
55 56
  57 + /// Whether to automatically resume the camera when the application is resumed
  58 + bool autoResume;
  59 +
56 Stream<Barcode> get barcodes => barcodesController.stream; 60 Stream<Barcode> get barcodes => barcodesController.stream;
57 61
58 MobileScannerController({ 62 MobileScannerController({
@@ -60,6 +64,7 @@ class MobileScannerController { @@ -60,6 +64,7 @@ class MobileScannerController {
60 this.ratio, 64 this.ratio,
61 this.torchEnabled, 65 this.torchEnabled,
62 this.formats, 66 this.formats,
  67 + this.autoResume = true,
63 }) { 68 }) {
64 // In case a new instance is created before calling dispose() 69 // In case a new instance is created before calling dispose()
65 if (_controllerHashcode != null) { 70 if (_controllerHashcode != null) {
@@ -75,8 +80,6 @@ class MobileScannerController { @@ -75,8 +80,6 @@ class MobileScannerController {
75 // onCancel: () => setAnalyzeMode(AnalyzeMode.none.index), 80 // onCancel: () => setAnalyzeMode(AnalyzeMode.none.index),
76 ); 81 );
77 82
78 - start();  
79 -  
80 // Listen to events from the platform specific code 83 // Listen to events from the platform specific code
81 events = eventChannel 84 events = eventChannel
82 .receiveBroadcastStream() 85 .receiveBroadcastStream()
@@ -88,22 +91,22 @@ class MobileScannerController { @@ -88,22 +91,22 @@ class MobileScannerController {
88 final data = event['data']; 91 final data = event['data'];
89 switch (name) { 92 switch (name) {
90 case 'torchState': 93 case 'torchState':
91 - final state = TorchState.values[data as int]; 94 + final state = TorchState.values[data as int? ?? 0];
92 torchState.value = state; 95 torchState.value = state;
93 break; 96 break;
94 case 'barcode': 97 case 'barcode':
95 - final barcode = Barcode.fromNative(data as Map); 98 + final barcode = Barcode.fromNative(data as Map? ?? {});
96 barcodesController.add(barcode); 99 barcodesController.add(barcode);
97 break; 100 break;
98 case 'barcodeMac': 101 case 'barcodeMac':
99 barcodesController.add( 102 barcodesController.add(
100 Barcode( 103 Barcode(
101 - rawValue: (data as Map)['payload'] as String, 104 + rawValue: (data as Map)['payload'] as String?,
102 ), 105 ),
103 ); 106 );
104 break; 107 break;
105 case 'barcodeWeb': 108 case 'barcodeWeb':
106 - barcodesController.add(Barcode(rawValue: data as String)); 109 + barcodesController.add(Barcode(rawValue: data as String?));
107 break; 110 break;
108 default: 111 default:
109 throw UnimplementedError(); 112 throw UnimplementedError();
@@ -134,11 +137,11 @@ class MobileScannerController { @@ -134,11 +137,11 @@ class MobileScannerController {
134 // Check authorization status 137 // Check authorization status
135 if (!kIsWeb) { 138 if (!kIsWeb) {
136 MobileScannerState state = MobileScannerState 139 MobileScannerState state = MobileScannerState
137 - .values[await methodChannel.invokeMethod('state') as int]; 140 + .values[await methodChannel.invokeMethod('state') as int? ?? 0];
138 switch (state) { 141 switch (state) {
139 case MobileScannerState.undetermined: 142 case MobileScannerState.undetermined:
140 final bool result = 143 final bool result =
141 - await methodChannel.invokeMethod('request') as bool; 144 + await methodChannel.invokeMethod('request') as bool? ?? false;
142 state = result 145 state = result
143 ? MobileScannerState.authorized 146 ? MobileScannerState.authorized
144 : MobileScannerState.denied; 147 : MobileScannerState.denied;
@@ -194,21 +197,21 @@ class MobileScannerController { @@ -194,21 +197,21 @@ class MobileScannerController {
194 throw PlatformException(code: 'INITIALIZATION ERROR'); 197 throw PlatformException(code: 'INITIALIZATION ERROR');
195 } 198 }
196 199
197 - hasTorch = startResult['torchable'] as bool; 200 + hasTorch = startResult['torchable'] as bool? ?? false;
198 201
199 if (kIsWeb) { 202 if (kIsWeb) {
200 args.value = MobileScannerArguments( 203 args.value = MobileScannerArguments(
201 webId: startResult['ViewID'] as String?, 204 webId: startResult['ViewID'] as String?,
202 size: Size( 205 size: Size(
203 - startResult['videoWidth'] as double,  
204 - startResult['videoHeight'] as double, 206 + startResult['videoWidth'] as double? ?? 0,
  207 + startResult['videoHeight'] as double? ?? 0,
205 ), 208 ),
206 hasTorch: hasTorch, 209 hasTorch: hasTorch,
207 ); 210 );
208 } else { 211 } else {
209 args.value = MobileScannerArguments( 212 args.value = MobileScannerArguments(
210 - textureId: startResult['textureId'] as int,  
211 - size: toSize(startResult['size'] as Map), 213 + textureId: startResult['textureId'] as int?,
  214 + size: toSize(startResult['size'] as Map? ?? {}),
212 hasTorch: hasTorch, 215 hasTorch: hasTorch,
213 ); 216 );
214 } 217 }
@@ -17,7 +17,7 @@ dependencies: @@ -17,7 +17,7 @@ dependencies:
17 dev_dependencies: 17 dev_dependencies:
18 flutter_test: 18 flutter_test:
19 sdk: flutter 19 sdk: flutter
20 - lint: ^1.8.2 20 + lint: ^1.10.0
21 21
22 flutter: 22 flutter:
23 plugin: 23 plugin: