Skip to content

Commit

Permalink
- Added callback for qrcode read timeout
Browse files Browse the repository at this point in the history
- Added callback for qrcode read error
- Added flag to decide whether camera capture should be stopped if reading time runs out
- Added field to set qrcode read timeout
- increase lib version
  • Loading branch information
rsantos13 committed Dec 14, 2022
1 parent 5138431 commit b387b1b
Show file tree
Hide file tree
Showing 8 changed files with 132 additions and 35 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -90,5 +90,6 @@ public void onSuccess(List<Barcode> firebaseVisionBarcodes) {
@Override
public void onFailure(@NonNull Exception e) {
Log.w(TAG, "Barcode Reading Failure: ", e);
communicator.qrReadError(e);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -123,6 +123,7 @@ public void onMethodCall(MethodCall methodCall, Result result) {
Integer targetHeight = methodCall.argument("targetHeight");
Integer cameraDirection = methodCall.argument("cameraDirection");
List<String> formatStrings = methodCall.argument("formats");
Boolean shouldStopCameraOnReadTimeout = methodCall.argument("shouldStopCameraOnReadTimeout");

if (targetWidth == null || targetHeight == null) {
result.error("INVALID_ARGUMENT", "Missing a required argument", "Expecting targetWidth, targetHeight, and optionally heartbeatTimeout");
Expand All @@ -139,7 +140,8 @@ public void onMethodCall(MethodCall methodCall, Result result) {
try {
reader.start(
lastHeartbeatTimeout == null ? 0 : lastHeartbeatTimeout,
cameraDirection == null ? 0 : cameraDirection
cameraDirection == null ? 0 : cameraDirection,
shouldStopCameraOnReadTimeout != null && shouldStopCameraOnReadTimeout
);
} catch (IOException e) {
e.printStackTrace();
Expand Down Expand Up @@ -186,6 +188,16 @@ public void qrRead(String data) {
channel.invokeMethod("qrRead", data);
}

@Override
public void qrReadTimeout() {
channel.invokeMethod("qrReadTimeout", null);
}

@Override
public void qrReadError(Throwable error) {
channel.invokeMethod("qrReadError", stackTraceAsString(error.getStackTrace()));
}

@Override
public void started() {
Map<String, Object> response = new HashMap<>();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,14 @@ class QrReader {
private final Activity context;
private final QRReaderStartedCallback startedCallback;
private Heartbeat heartbeat;
private final QrReaderCallbacks communicator;

QrReader(int width, int height, Activity context, BarcodeScannerOptions options,
final QRReaderStartedCallback startedCallback, final QrReaderCallbacks communicator,
final SurfaceTexture texture) {
this.context = context;
this.startedCallback = startedCallback;
this.communicator = communicator;

if (android.os.Build.VERSION.SDK_INT >= 23) {
Log.i(TAG, "Using new camera API.");
Expand All @@ -35,27 +37,28 @@ class QrReader {
}
}

void start(final int heartBeatTimeout, final int cameraDirection) throws IOException, NoPermissionException, Exception {
void start(final int heartBeatTimeout, final int cameraDirection, final boolean shouldStopCameraOnReadTimeout) throws IOException, NoPermissionException, Exception {
if (!hasCameraHardware(context)) {
throw new Exception(Exception.Reason.noHardware);
}

if (!checkCameraPermission(context)) {
throw new NoPermissionException();
} else {
continueStarting(heartBeatTimeout, cameraDirection);
continueStarting(heartBeatTimeout, cameraDirection, shouldStopCameraOnReadTimeout);
}
}

private void continueStarting(int heartBeatTimeout, final int cameraDirection) throws IOException {
private void continueStarting(int heartBeatTimeout, final int cameraDirection, final boolean shouldStopCameraOnReadTimeout) throws IOException {
try {
if (heartBeatTimeout > 0) {
if (heartbeat != null) {
heartbeat.stop();
}
heartbeat = new Heartbeat(heartBeatTimeout, new Runnable() {
@Override
public void run() {
heartbeat = new Heartbeat(heartBeatTimeout, () -> {
Log.i(TAG, "Stopping camera from Heartbeat.");
communicator.qrReadTimeout();
if (shouldStopCameraOnReadTimeout) {
stop();
}
});
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,6 @@

public interface QrReaderCallbacks {
void qrRead(String data);
void qrReadTimeout();
void qrReadError(Throwable error);
}
61 changes: 46 additions & 15 deletions example/lib/main.dart
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,9 @@ class _MyAppState extends State<MyApp> {
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("Back"),
Switch(value: dirState, onChanged: (val) => setState(() => dirState = val)),
Switch(
value: dirState,
onChanged: (val) => setState(() => dirState = val)),
Text("Front"),
],
),
Expand All @@ -60,26 +62,41 @@ class _MyAppState extends State<MyApp> {
width: 300.0,
height: 600.0,
child: QrCamera(
onError: (context, error) => Text(
error.toString(),
style: TextStyle(color: Colors.red),
shouldStopCameraOnReadTimeout: false,
qrCodeReadTimeout: 5000,
qrReadTimeoutCallback: () {
debugPrint('Qrcode read timeout');
},
qrReadErrorCallback: (error) {
debugPrint('QrCode read error: $error');
},
onError: (context, error) => buildPortrait(
child: Center(
child: Text(
error.toString(),
style: TextStyle(color: Colors.red),
),
),
),
notStartedBuilder: (context) => buildPortrait(
child: Center(
child: Text("Camera Loading ..."),
),
),
offscreenBuilder: (context) => buildPortrait(
child: Center(
child: Text("Camera Paused."),
),
),
cameraDirection: dirState ? CameraDirection.FRONT : CameraDirection.BACK,
cameraDirection: dirState
? CameraDirection.FRONT
: CameraDirection.BACK,
qrCodeCallback: (code) {
setState(() {
qr = code;
});
},
child: Container(
decoration: BoxDecoration(
color: Colors.transparent,
border: Border.all(
color: Colors.orange,
width: 10.0,
style: BorderStyle.solid,
),
),
),
child: buildPortrait(),
),
),
)
Expand All @@ -100,4 +117,18 @@ class _MyAppState extends State<MyApp> {
}),
);
}

Widget buildPortrait({Widget child}) {
return Container(
child: child,
decoration: BoxDecoration(
color: Colors.black54,
border: Border.all(
color: Colors.orange,
width: 10.0,
style: BorderStyle.solid,
),
),
);
}
}
12 changes: 12 additions & 0 deletions lib/src/qr_camera.dart
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,11 @@ class QrCamera extends StatefulWidget {
WidgetBuilder? offscreenBuilder,
ErrorCallback? onError,
this.cameraDirection = CameraDirection.BACK,
this.shouldStopCameraOnReadTimeout = false,
this.formats,
this.qrCodeReadTimeout = 0,
this.qrReadErrorCallback,
this.qrReadTimeoutCallback,
}) : notStartedBuilder = notStartedBuilder ?? _defaultNotStartedBuilder,
offscreenBuilder =
offscreenBuilder ?? notStartedBuilder ?? _defaultOffscreenBuilder,
Expand All @@ -35,12 +39,16 @@ class QrCamera extends StatefulWidget {

final BoxFit fit;
final ValueChanged<String?> qrCodeCallback;
final ValueChanged<String?>? qrReadErrorCallback;
final VoidCallback? qrReadTimeoutCallback;
final Widget? child;
final WidgetBuilder notStartedBuilder;
final WidgetBuilder offscreenBuilder;
final ErrorCallback onError;
final List<BarcodeFormats>? formats;
final CameraDirection cameraDirection;
final int qrCodeReadTimeout;
final bool shouldStopCameraOnReadTimeout;

static toggleFlash() {
QrMobileVision.toggleFlash();
Expand Down Expand Up @@ -109,6 +117,10 @@ class QrCameraState extends State<QrCamera> with WidgetsBindingObserver {
qrCodeHandler: widget.qrCodeCallback,
formats: widget.formats,
cameraDirection: widget.cameraDirection,
qrReadTimeoutHandler: widget.qrReadTimeoutCallback,
qrCodeReadErrorHandler: widget.qrReadErrorCallback,
qrCodeReadTimeout: widget.qrCodeReadTimeout,
shouldStopCameraOnReadTimeout: widget.shouldStopCameraOnReadTimeout,
);
}

Expand Down
21 changes: 21 additions & 0 deletions lib/src/qr_channel_reader.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,17 @@ class QrChannelReader {
qrCodeHandler!(call.arguments);
}
break;
case 'qrReadTimeout':
if (qrCodeReadTimeoutHandler != null) {
qrCodeReadTimeoutHandler!.call();
}
break;
case 'qrReadError':
if (qrCodeErrorHandler != null) {
assert(call.arguments is String);
qrCodeErrorHandler!(call.arguments);
}
break;
default:
print("QrChannelHandler: unknown method call received at "
"${call.method}");
Expand All @@ -22,6 +33,16 @@ class QrChannelReader {
this.qrCodeHandler = qrch;
}

void setQrCodeErrorHandler(ValueChanged<String?>? qrch) {
this.qrCodeErrorHandler = qrch;
}

void setQrCodeReadTimeoutHandler(VoidCallback? qrch) {
this.qrCodeReadTimeoutHandler = qrch;
}

MethodChannel channel;
ValueChanged<String?>? qrCodeHandler;
ValueChanged<String?>? qrCodeErrorHandler;
VoidCallback? qrCodeReadTimeoutHandler;
}
41 changes: 28 additions & 13 deletions lib/src/qr_mobile_vision.dart
Original file line number Diff line number Diff line change
Expand Up @@ -10,39 +10,51 @@ import 'package:qr_mobile_vision/src/preview_details.dart';
import 'package:qr_mobile_vision/src/qr_channel_reader.dart';

class QrMobileVision {
static const MethodChannel _channel = const MethodChannel('com.github.rmtmckenzie/qr_mobile_vision');
static const MethodChannel _channel =
const MethodChannel('com.github.rmtmckenzie/qr_mobile_vision');
static QrChannelReader channelReader = QrChannelReader(_channel);

//Set target size before starting
static Future<PreviewDetails> start({
required int width,
required int height,
required ValueChanged<String?> qrCodeHandler,
CameraDirection cameraDirection = CameraDirection.BACK,
List<BarcodeFormats>? formats = defaultBarcodeFormats,
}) async {
static Future<PreviewDetails> start(
{required int width,
required int height,
required ValueChanged<String?> qrCodeHandler,
CameraDirection cameraDirection = CameraDirection.BACK,
List<BarcodeFormats>? formats = defaultBarcodeFormats,
VoidCallback? qrReadTimeoutHandler,
ValueChanged<String?>? qrCodeReadErrorHandler,
int qrCodeReadTimeout = 0,
bool shouldStopCameraOnReadTimeout = false}) async {
final _formats = formats ?? defaultBarcodeFormats;
assert(_formats.length > 0);

List<String> formatStrings = _formats.map((format) => format.toString().split('.')[1]).toList(growable: false);
List<String> formatStrings = _formats
.map((format) => format.toString().split('.')[1])
.toList(growable: false);

final deviceInfoFut = Platform.isAndroid ? DeviceInfoPlugin().androidInfo : Future.value(null);
final deviceInfoFut = Platform.isAndroid
? DeviceInfoPlugin().androidInfo
: Future.value(null);

channelReader.setQrCodeHandler(qrCodeHandler);
channelReader.setQrCodeReadTimeoutHandler(qrReadTimeoutHandler);
channelReader.setQrCodeErrorHandler(qrCodeReadErrorHandler);
final details = (await _channel.invokeMapMethod<String, dynamic>('start', {
'targetWidth': width,
'targetHeight': height,
'heartbeatTimeout': 0,
'heartbeatTimeout': qrCodeReadTimeout,
'cameraDirection': (cameraDirection == CameraDirection.FRONT ? 0 : 1),
'formats': formatStrings,
'shouldStopCameraOnReadTimeout': shouldStopCameraOnReadTimeout,
}))!;

int? textureId = details["textureId"];
num? orientation = details["surfaceOrientation"];
num surfaceHeight = details["surfaceHeight"];
num surfaceWidth = details["surfaceWidth"];

final deets = await NativePreviewDetails(surfaceWidth, surfaceHeight, orientation, textureId);
final deets = await NativePreviewDetails(
surfaceWidth, surfaceHeight, orientation, textureId);
final devInfo = await deviceInfoFut;

return PreviewDetails(deets, devInfo?.version.sdkInt ?? -1);
Expand All @@ -54,6 +66,8 @@ class QrMobileVision {

static Future stop() {
channelReader.setQrCodeHandler(null);
channelReader.setQrCodeReadTimeoutHandler(null);
channelReader.setQrCodeErrorHandler(null);
return _channel.invokeMethod('stop').catchError(print);
}

Expand All @@ -62,6 +76,7 @@ class QrMobileVision {
}

static Future<List<List<int>>?> getSupportedSizes() {
return _channel.invokeMethod('getSupportedSizes').catchError(print) as Future<List<List<int>>?>;
return _channel.invokeMethod('getSupportedSizes').catchError(print)
as Future<List<List<int>>?>;
}
}

0 comments on commit b387b1b

Please sign in to comment.