Julian Steenbakker
Committed by GitHub

Merge branch 'master' into dependabot/gradle/android/androidx.camera-camera-camera2-1.2.1

... ... @@ -12,4 +12,4 @@ jobs:
assign-author:
runs-on: ubuntu-latest
steps:
- uses: toshimaru/auto-author-assign@v1.6.1
- uses: toshimaru/auto-author-assign@v1.6.2
... ...
... ... @@ -11,7 +11,7 @@ jobs:
analysis:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.2.0
- uses: actions/checkout@v3.3.0
- uses: actions/setup-java@v3.9.0
with:
java-version: 11
... ... @@ -28,7 +28,7 @@ jobs:
formatting:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3.2.0
- uses: actions/checkout@v3.3.0
- uses: actions/setup-java@v3.9.0
with:
java-version: 11
... ...
... ... @@ -7,7 +7,7 @@
release-please:
runs-on: ubuntu-latest
steps:
- uses: GoogleCloudPlatform/release-please-action@v3.7.1
- uses: GoogleCloudPlatform/release-please-action@v3.7.3
with:
token: ${{ secrets.GITHUB_TOKEN }}
release-type: simple
... ...
## Next
Improvements:
* [Web] Automatically inject js libraries
## 3.0.0-beta.4
Fixes:
* Fixes a permission bug on Android where denying the permission would cause an infinite loop of permission requests.
... ...
... ... @@ -53,13 +53,6 @@ Ensure that you granted camera permission in XCode -> Signing & Capabilities:
<img width="696" alt="Screenshot of XCode where Camera is checked" src="https://user-images.githubusercontent.com/24459435/193464115-d76f81d0-6355-4cb2-8bee-538e413a3ad0.png">
### Web
Add this to `web/index.html`:
```html
<script type="text/javascript" src="https://unpkg.com/@zxing/library@0.19.1"></script>
```
## Usage
Import `package:mobile_scanner/mobile_scanner.dart`, and use the widget with or without the controller.
... ...
... ... @@ -9,7 +9,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:7.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
... ... @@ -54,5 +54,5 @@ dependencies {
// implementation 'com.google.android.gms:play-services-mlkit-barcode-scanning:18.1.0'
implementation 'androidx.camera:camera-camera2:1.2.1'
implementation 'androidx.camera:camera-lifecycle:1.2.0'
implementation 'androidx.camera:camera-lifecycle:1.2.1'
}
... ...
... ... @@ -6,7 +6,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:7.3.1'
classpath 'com.android.tools.build:gradle:7.4.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
... ...
... ... @@ -28,19 +28,23 @@ class _BarcodeListScannerWithControllerState
bool isStarted = true;
void _startOrStop() {
try {
if (isStarted) {
controller.stop();
} else {
controller.start().catchError((error) {
if (mounted) {
setState(() {});
controller.start();
}
});
}
setState(() {
isStarted = !isStarted;
});
} on Exception catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Something went wrong! $e'),
backgroundColor: Colors.red,
),
);
}
}
@override
... ... @@ -121,7 +125,7 @@ class _BarcodeListScannerWithControllerState
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -28,19 +28,23 @@ class _BarcodeScannerWithControllerState
bool isStarted = true;
void _startOrStop() {
try {
if (isStarted) {
controller.stop();
} else {
controller.start().catchError((error) {
if (mounted) {
setState(() {});
controller.start();
}
});
}
setState(() {
isStarted = !isStarted;
});
} on Exception catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Something went wrong! $e'),
backgroundColor: Colors.red,
),
);
}
}
@override
... ... @@ -127,7 +131,7 @@ class _BarcodeScannerWithControllerState
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -30,19 +30,23 @@ class _BarcodeScannerReturningImageState
bool isStarted = true;
void _startOrStop() {
try {
if (isStarted) {
controller.stop();
} else {
controller.start().catchError((error) {
if (mounted) {
setState(() {});
controller.start();
}
});
}
setState(() {
isStarted = !isStarted;
});
} on Exception catch (e) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text('Something went wrong! $e'),
backgroundColor: Colors.red,
),
);
}
}
@override
... ... @@ -141,7 +145,7 @@ class _BarcodeScannerReturningImageState
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -82,7 +82,7 @@ class _BarcodeScannerWithScanWindowState
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -54,7 +54,7 @@ class _BarcodeScannerWithoutControllerState
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -109,7 +109,7 @@ class _BarcodeScannerWithZoomState extends State<BarcodeScannerWithZoom>
overflow: TextOverflow.fade,
style: Theme.of(context)
.textTheme
.headline4!
.headlineMedium!
.copyWith(color: Colors.white),
),
),
... ...
... ... @@ -28,8 +28,6 @@
<title>example</title>
<link rel="manifest" href="manifest.json">
<script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
<script type="text/javascript" src="https://unpkg.com/@zxing/library@0.19.1"></script>
</head>
<body>
<!-- This script installs service_worker.js to provide PWA functionality to
... ...
... ... @@ -8,6 +8,7 @@ import 'package:mobile_scanner/mobile_scanner_web.dart';
import 'package:mobile_scanner/src/barcode_utility.dart';
import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/objects/barcode.dart';
import 'package:mobile_scanner/src/web/utils.dart';
/// This plugin is the web implementation of mobile_scanner.
/// It only supports QR codes.
... ... @@ -25,6 +26,8 @@ class MobileScannerWebPlugin {
);
final MobileScannerWebPlugin instance = MobileScannerWebPlugin();
injectJSLibraries(barCodeReader.jsLibraries);
channel.setMethodCallHandler(instance.handleMethodCall);
event.setController(instance.controller);
}
... ...
... ... @@ -7,6 +7,24 @@ import 'package:mobile_scanner/src/enums/camera_facing.dart';
import 'package:mobile_scanner/src/objects/barcode.dart';
import 'package:mobile_scanner/src/web/media.dart';
class JsLibrary {
/// The name of global variable where library is stored.
/// Used to properly import the library if [usesRequireJs] flag is true
final String contextName;
final String url;
/// If js code checks for 'define' variable.
/// E.g. if at the beginning you see code like
/// if (typeof define === "function" && define.amd)
final bool usesRequireJs;
const JsLibrary({
required this.contextName,
required this.url,
required this.usesRequireJs,
});
}
abstract class WebBarcodeReaderBase {
/// Timer used to capture frames to be analyzed
Duration frameInterval = const Duration(milliseconds: 200);
... ... @@ -21,6 +39,9 @@ abstract class WebBarcodeReaderBase {
int get videoWidth;
int get videoHeight;
/// JS libraries to be injected into html page.
List<JsLibrary> get jsLibraries;
/// Starts streaming video
Future<void> start({
required CameraFacing cameraFacing,
... ...
... ... @@ -20,11 +20,14 @@ class Code {
external Uint8ClampedList get binaryData;
}
const jsqrLibrary = JsLibrary(
contextName: 'jsQR',
url: 'https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js',
usesRequireJs: true,
);
/// Barcode reader that uses jsQR library.
/// jsQR supports only QR codes format.
///
/// Include jsQR to your index.html file:
/// <script src="https://cdn.jsdelivr.net/npm/jsqr@1.4.0/dist/jsQR.min.js"></script>
class JsQrCodeReader extends WebBarcodeReaderBase
with InternalStreamCreation, InternalTorchDetection {
JsQrCodeReader({required super.videoContainer});
... ... @@ -33,6 +36,9 @@ class JsQrCodeReader extends WebBarcodeReaderBase
bool get isStarted => localMediaStream != null;
@override
List<JsLibrary> get jsLibraries => [jsqrLibrary];
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
... ...
import 'dart:async';
import 'dart:html' as html;
import 'dart:js' show context;
import 'package:js/js.dart';
import 'package:mobile_scanner/src/web/base.dart';
Future<void> loadScript(JsLibrary library) async {
// ignore: avoid_dynamic_calls
if (library.usesRequireJs && context['define']?['amd'] != null) {
// see https://github.com/dart-lang/sdk/issues/33979
return loadScriptUsingRequireJS(library.contextName, library.url);
} else {
return loadScriptUsingScriptTag(library.url);
}
}
Future<void> loadScriptUsingScriptTag(String url) {
final script = html.ScriptElement()
..async = true
..defer = false
..crossOrigin = 'anonymous'
..type = 'text/javascript'
// ignore: unsafe_html
..src = url;
html.document.head!.append(script);
return script.onLoad.first;
}
Future<void> loadScriptUsingRequireJS(String packageName, String url) {
final Completer completer = Completer();
final String eventName = '_${packageName}Loaded';
context.callMethod(
'addEventListener',
[eventName, allowInterop((_) => completer.complete())],
);
final script = html.ScriptElement()
..type = 'text/javascript'
..async = false
..defer = false
..text = '''
require(["$url"], (package) => {
window.$packageName = package;
const event = new Event("$eventName");
dispatchEvent(event);
})
''';
html.document.head!.append(script);
return completer.future;
}
/// Injects JS [libraries]
///
/// Returns a [Future] that resolves when all of the `script` tags `onLoad` events trigger.
Future<void> injectJSLibraries(List<JsLibrary> libraries) {
final List<Future<void>> loading = [];
for (final library in libraries) {
final future = loadScript(library);
loading.add(future);
}
return Future.wait(loading);
}
... ...
... ... @@ -168,10 +168,13 @@ extension JsZXingBrowserMultiFormatReaderExt
external MediaStream? stream;
}
const zxingJsLibrary = JsLibrary(
contextName: 'ZXing',
url: 'https://unpkg.com/@zxing/library@0.19.1',
usesRequireJs: true,
);
/// Barcode reader that uses zxing-js library.
///
/// Include zxing-js to your index.html file:
/// <script type="text/javascript" src="https://unpkg.com/@zxing/library@0.19.1"></script>
class ZXingBarcodeReader extends WebBarcodeReaderBase
with InternalStreamCreation, InternalTorchDetection {
JsZXingBrowserMultiFormatReader? _reader;
... ... @@ -182,6 +185,9 @@ class ZXingBarcodeReader extends WebBarcodeReaderBase
bool get isStarted => localMediaStream != null;
@override
List<JsLibrary> get jsLibraries => [zxingJsLibrary];
@override
Future<void> start({
required CameraFacing cameraFacing,
List<BarcodeFormat>? formats,
... ...