add file selection button

still janky, it should show snackbar messages on errors but doesn't. ffs
This commit is contained in:
sneurlax 2024-07-18 23:01:28 -05:00 committed by julian-CStack
parent 8bf8a90404
commit 7477922382

View file

@ -10,11 +10,13 @@
import 'dart:async'; import 'dart:async';
import 'dart:convert'; import 'dart:convert';
import 'dart:io';
import 'package:camera_linux/camera_linux.dart'; import 'package:camera_linux/camera_linux.dart';
import 'package:cw_core/monero_transaction_priority.dart'; import 'package:cw_core/monero_transaction_priority.dart';
import 'package:decimal/decimal.dart'; import 'package:decimal/decimal.dart';
import 'package:dropdown_button2/dropdown_button2.dart'; import 'package:dropdown_button2/dropdown_button2.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.dart'; import 'package:flutter/services.dart';
import 'package:flutter_riverpod/flutter_riverpod.dart'; import 'package:flutter_riverpod/flutter_riverpod.dart';
@ -171,7 +173,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
}); });
} }
Future<void> _scanQr() async { Future<void> scanWebcam() async {
try { try {
await showDialog( await showDialog(
context: context, context: context,
@ -180,34 +182,19 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
walletId: widget.walletId, walletId: widget.walletId,
onQrCodeDetected: (qrCodeData) { onQrCodeDetected: (qrCodeData) {
try { try {
var results = AddressUtils.parseUri(qrCodeData); _processQrCodeData(qrCodeData);
if (results.isNotEmpty && results["scheme"] == coin.uriScheme) {
_address = (results["address"] ?? "").trim();
sendToController.text = _address!;
if (results["amount"] != null) {
final Amount amount =
Decimal.parse(results["amount"]!).toAmount(
fractionDigits: coin.fractionDigits,
);
cryptoAmountController.text =
ref.read(pAmountFormatter(coin)).format(
amount,
withUnitName: false,
);
ref.read(pSendAmount.notifier).state = amount;
}
_setValidAddressProviders(_address);
setState(() {
_addressToggleFlag = sendToController.text.isNotEmpty;
});
}
} catch (e, s) { } catch (e, s) {
Logging.instance.log("Error processing QR code data: $e\n$s", Logging.instance.log("Error processing QR code data: $e\n$s",
level: LogLevel.Error); level: LogLevel.Error);
} }
}, },
onSnackbar: (message) {
ScaffoldMessenger.of(context).showSnackBar(
SnackBar(
content: Text(message),
),
);
},
); );
}, },
); );
@ -780,6 +767,35 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
} }
} }
void _processQrCodeData(String qrCodeData) {
try {
var results = AddressUtils.parseUri(qrCodeData);
if (results.isNotEmpty && results["scheme"] == coin.uriScheme) {
_address = (results["address"] ?? "").trim();
sendToController.text = _address!;
if (results["amount"] != null) {
final Amount amount = Decimal.parse(results["amount"]!).toAmount(
fractionDigits: coin.fractionDigits,
);
cryptoAmountController.text = ref.read(pAmountFormatter(coin)).format(
amount,
withUnitName: false,
);
ref.read(pSendAmount.notifier).state = amount;
}
_setValidAddressProviders(_address);
setState(() {
_addressToggleFlag = sendToController.text.isNotEmpty;
});
}
} catch (e, s) {
Logging.instance
.log("Error processing QR code data: $e\n$s", level: LogLevel.Error);
}
}
void _setValidAddressProviders(String? address) { void _setValidAddressProviders(String? address) {
if (isPaynymSend) { if (isPaynymSend) {
ref.read(pValidSendToAddress.notifier).state = true; ref.read(pValidSendToAddress.notifier).state = true;
@ -1565,7 +1581,7 @@ class _DesktopSendState extends ConsumerState<DesktopSend> {
key: const Key( key: const Key(
"sendViewScanQrButtonKey", "sendViewScanQrButtonKey",
), ),
onTap: _scanQr, onTap: scanWebcam,
child: const QrCodeIcon(), child: const QrCodeIcon(),
), ),
], ],
@ -1982,10 +1998,12 @@ String formatAddress(String epicAddress) {
class QrCodeScannerDialog extends StatefulWidget { class QrCodeScannerDialog extends StatefulWidget {
final String walletId; final String walletId;
final Function(String) onQrCodeDetected; final Function(String) onQrCodeDetected;
final Function(String) onSnackbar;
QrCodeScannerDialog({ QrCodeScannerDialog({
required this.walletId, required this.walletId,
required this.onQrCodeDetected, required this.onQrCodeDetected,
required this.onSnackbar,
}); });
@override @override
@ -1994,10 +2012,9 @@ class QrCodeScannerDialog extends StatefulWidget {
class _QrCodeScannerDialogState extends State<QrCodeScannerDialog> { class _QrCodeScannerDialogState extends State<QrCodeScannerDialog> {
final _cameraLinuxPlugin = CameraLinux(); final _cameraLinuxPlugin = CameraLinux();
late String _base64Image;
bool _isCameraOpen = false; bool _isCameraOpen = false;
Image? _image; Image? _image;
String? _qrCodeData; bool _isScanning = false;
@override @override
void initState() { void initState() {
@ -2016,27 +2033,89 @@ class _QrCodeScannerDialogState extends State<QrCodeScannerDialog> {
await _cameraLinuxPlugin.initializeCamera(); await _cameraLinuxPlugin.initializeCamera();
Logging.instance Logging.instance
.log("Camera initialized successfully", level: LogLevel.Info); .log("Camera initialized successfully", level: LogLevel.Info);
if (mounted) {
setState(() {
_isCameraOpen = true;
_isScanning = true;
});
}
_captureAndScanImage(); _captureAndScanImage();
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("Failed to initialize camera: $e\n$s", level: LogLevel.Error); .log("Failed to initialize camera: $e\n$s", level: LogLevel.Error);
if (mounted) {
widget.onSnackbar("Failed to initialize camera. Please try again.");
}
}
}
Future<void> _stopCamera() async {
try {
_cameraLinuxPlugin.stopCamera();
Logging.instance.log("Camera stopped successfully", level: LogLevel.Info);
} catch (e, s) {
Logging.instance
.log("Failed to stop camera: $e\n$s", level: LogLevel.Error);
} finally {
if (mounted) {
setState(() {
_isScanning = false;
_isCameraOpen = false;
});
}
} }
} }
Future<void> _captureAndScanImage() async { Future<void> _captureAndScanImage() async {
while (_qrCodeData == null && mounted) { while (_isCameraOpen && _isScanning) {
try { try {
_base64Image = await _cameraLinuxPlugin.captureImage(); final base64Image = await _cameraLinuxPlugin.captureImage();
Logging.instance final img.Image? image = img.decodeImage(base64Decode(base64Image));
.log("Image captured successfully", level: LogLevel.Info);
_isCameraOpen = true;
var image = img.decodeImage(base64Decode(_base64Image));
if (image == null) { if (image == null) {
throw Exception("Failed to decode image"); Logging.instance.log("Failed to decode image", level: LogLevel.Info);
await Future.delayed(const Duration(milliseconds: 250));
continue;
} }
LuminanceSource source = RGBLuminanceSource( if (mounted) {
setState(() {
_image = Image.memory(
base64Decode(base64Image),
fit: BoxFit.cover,
);
});
}
final String? scanResult = await _scanImage(image);
if (scanResult != null && scanResult.isNotEmpty) {
widget.onQrCodeDetected(scanResult);
if (mounted) {
Navigator.of(context).pop();
}
break;
} else {
Logging.instance
.log("No QR code found in the image", level: LogLevel.Info);
if (mounted) {
widget.onSnackbar("No QR code found in the image.");
}
}
await Future.delayed(const Duration(milliseconds: 250));
} catch (e, s) {
Logging.instance.log("Failed to capture and scan image: $e\n$s",
level: LogLevel.Error);
if (mounted) {
widget.onSnackbar(
"Error capturing or scanning the image. Please try again.");
}
}
}
}
Future<String?> _scanImage(img.Image image) async {
try {
final LuminanceSource source = RGBLuminanceSource(
image.width, image.width,
image.height, image.height,
image image
@ -2045,45 +2124,19 @@ class _QrCodeScannerDialogState extends State<QrCodeScannerDialog> {
.buffer .buffer
.asInt32List(), .asInt32List(),
); );
var bitmap = BinaryBitmap(GlobalHistogramBinarizer(source)); final BinaryBitmap bitmap =
_image = Image.memory(Uint8List.fromList(img.encodePng(image))); BinaryBitmap(GlobalHistogramBinarizer(source));
var reader = QRCodeReader();
try { final QRCodeReader reader = QRCodeReader();
var qrDecode = reader.decode(bitmap); final qrDecode = reader.decode(bitmap);
_qrCodeData = qrDecode.text; if (qrDecode.text.isEmpty) {
Logging.instance.log("QR code decoded successfully: $_qrCodeData", return null;
level: LogLevel.Info);
widget.onQrCodeDetected(_qrCodeData!);
Navigator.of(context).pop();
break; // Exit the loop once QR code is detected
} catch (e) {
Logging.instance
.log("Failed to decode QR code: $e", level: LogLevel.Error);
setState(() {
// Update the dialog with the new image.
});
} }
return qrDecode.text;
await Future.delayed(
const Duration(milliseconds: 1000)); // Add delay here.
} catch (e, s) {
Logging.instance.log("Failed to capture and scan image: $e\n$s",
level: LogLevel.Error);
}
}
}
void _stopCamera() {
try {
_cameraLinuxPlugin.stopCamera();
Logging.instance.log("Camera stopped successfully", level: LogLevel.Info);
setState(() {
_isCameraOpen = false;
});
} catch (e, s) { } catch (e, s) {
Logging.instance Logging.instance
.log("Failed to stop camera: $e\n$s", level: LogLevel.Error); .log("Failed to decode QR code: $e\n$s", level: LogLevel.Error);
return null;
} }
} }
@ -2120,15 +2173,64 @@ class _QrCodeScannerDialogState extends State<QrCodeScannerDialog> {
), ),
), ),
Padding( Padding(
padding: const EdgeInsets.only(bottom: 16), padding: const EdgeInsets.all(16),
child: PrimaryButton( child: Row(
children: [
Expanded(child: Container()),
// "Select file" button.
SecondaryButton(
buttonHeight: ButtonHeight.l,
label: "Select file",
width: 200,
onPressed: () async {
final result = await FilePicker.platform.pickFiles(
type: FileType.custom,
allowedExtensions: ["png", "jpg", "jpeg"],
);
if (result == null || result.files.single.path == null) {
widget.onSnackbar("No file selected.");
return;
}
final filePath = result.files.single.path!;
try {
final img.Image? image =
img.decodeImage(File(filePath).readAsBytesSync());
if (image == null) {
widget.onSnackbar(
"Failed to decode image. Please select a valid image file.");
return;
}
final String? scanResult = await _scanImage(image);
if (scanResult != null && scanResult.isNotEmpty) {
widget.onQrCodeDetected(scanResult);
Navigator.of(context).pop();
} else {
widget.onSnackbar("No QR code found in the image.");
}
} catch (e, s) {
Logging.instance.log("Failed to decode image: $e\n$s",
level: LogLevel.Error);
widget.onSnackbar(
"Error processing the image. Please try again.");
}
},
),
const SizedBox(width: 16),
// Close button.
PrimaryButton(
buttonHeight: ButtonHeight.l, buttonHeight: ButtonHeight.l,
label: "Close", label: "Close",
width: 272.5,
onPressed: () { onPressed: () {
_stopCamera(); _stopCamera();
Navigator.of(context).pop(); Navigator.of(context).pop();
}, },
), ),
],
),
), ),
], ],
), ),