add qr code scanning from gallery using zxing2

This commit is contained in:
a5xwin
2025-08-20 14:28:41 +05:30
parent 33b1a7e4f8
commit 56af818482
5 changed files with 104 additions and 3 deletions

View File

@@ -519,5 +519,12 @@
"algorithm": "Algorithm",
"type": "Type",
"period": "Period",
"digits": "Digits"
"digits": "Digits",
"importFromGallery": "Import from gallery",
"errorCouldNotReadImage": "Could not read the selected image file.",
"errorInvalidQRCode": "Invalid QR Code",
"errorInvalidQRCodeBody": "The scanned QR code is not a valid 2FA account.",
"errorNoQRCode": "No QR code found",
"errorGenericTitle": "An Error Occurred",
"errorGenericBody": "An unexpected error occurred while importing."
}

View File

@@ -35,18 +35,22 @@ import 'package:ente_auth/ui/settings_page.dart';
import 'package:ente_auth/ui/sort_option_menu.dart';
import 'package:ente_auth/utils/dialog_util.dart';
import 'package:ente_auth/utils/platform_util.dart';
import 'package:ente_auth/utils/toast_util.dart';
import 'package:ente_auth/utils/totp_util.dart';
import 'package:ente_events/event_bus.dart';
import 'package:ente_lock_screen/lock_screen_settings.dart';
import 'package:ente_lock_screen/ui/app_lock.dart';
import 'package:ente_ui/pages/base_home_page.dart';
import 'package:file_picker/file_picker.dart';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_speed_dial/flutter_speed_dial.dart';
import 'package:flutter_staggered_grid_view/flutter_staggered_grid_view.dart';
import 'package:image/image.dart' as img;
import 'package:logging/logging.dart';
import 'package:move_to_background/move_to_background.dart';
import 'package:zxing2/qrcode.dart';
class HomePage extends BaseHomePage {
const HomePage({super.key});
@@ -62,6 +66,7 @@ class _HomePageState extends State<HomePage> {
);
bool _hasLoaded = false;
bool _isSettingsOpen = false;
bool _isImportingFromGallery = false;
final Logger _logger = Logger("HomePage");
final scaffoldKey = GlobalKey<ScaffoldState>();
@@ -288,6 +293,70 @@ class _HomePageState extends State<HomePage> {
}
}
Future<void> _importFromGallery() async {
final l10n = AppLocalizations.of(context);
if (_isImportingFromGallery) {
return;
}
setState(() {
_isImportingFromGallery = true;
});
try {
final FilePickerResult? result = await FilePicker.platform.pickFiles(
type: FileType.image,
);
if (result == null || result.files.single.path == null) {
return;
}
final String imagePath = result.files.single.path!;
final rawImage = await File(imagePath).readAsBytes();
final image = img.decodeImage(rawImage);
if (image == null) {
await showErrorDialog(context, l10n.error, l10n.errorCouldNotReadImage);
return;
}
final source = RGBLuminanceSource(
image.width, image.height,
image.getBytes(order: img.ChannelOrder.rgba).buffer.asInt32List(),
);
final bitmap = BinaryBitmap(HybridBinarizer(source));
final reader = QRCodeReader();
final Result decodeResult = reader.decode(bitmap);
final String code = decodeResult.text;
try{
final newCode = Code.fromOTPAuthUrl(code);
await CodeStore.instance.addCode(newCode, shouldSync: false);
}
catch (e){
await showErrorDialog(
context, l10n.errorInvalidQRCode, l10n.errorInvalidQRCodeBody,
);
}
}
on ReaderException {
showToast(context, l10n.errorNoQRCode);
}
catch (e) {
await showErrorDialog(
context, l10n.errorGenericTitle, l10n.errorGenericBody,
);
}
finally {
setState(() {
_isImportingFromGallery = false;
});
}
}
Future<void> _redirectToScannerPage() async {
final Code? code = await Navigator.of(context).push(
MaterialPageRoute(
@@ -745,6 +814,13 @@ class _HomePageState extends State<HomePage> {
labelWidget: SpeedDialLabelWidget(context.l10n.enterDetailsManually),
onTap: _redirectToManualEntryPage,
),
SpeedDialChild(
child: const Icon(Icons.image),
backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor,
foregroundColor: Theme.of(context).colorScheme.fabForegroundColor,
labelWidget: SpeedDialLabelWidget(context.l10n.importFromGallery),
onTap: _importFromGallery,
),
],
);
}

View File

@@ -201,6 +201,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "1.4.0"
charcode:
dependency: transitive
description:
name: charcode
sha256: fb0f1107cac15a5ea6ef0a6ef71a807b9e4267c713bb93e00e92d737cc8dbd8a
url: "https://pub.dev"
source: hosted
version: "1.4.0"
checked_yaml:
dependency: transitive
description:
@@ -951,7 +959,7 @@ packages:
source: hosted
version: "0.1.0"
image:
dependency: transitive
dependency: "direct main"
description:
name: image
sha256: "4e973fcf4caae1a4be2fa0a13157aa38a8f9cb049db6529aa00b4d71abc4d928"
@@ -2021,6 +2029,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "3.1.3"
zxing2:
dependency: "direct main"
description:
name: zxing2
sha256: "2677c49a3b9ca9457cb1d294fd4bd5041cac6aab8cdb07b216ba4e98945c684f"
url: "https://pub.dev"
source: hosted
version: "0.2.4"
sdks:
dart: ">=3.7.2 <4.0.0"
flutter: ">=3.29.0"

View File

@@ -84,6 +84,7 @@ dependencies:
google_nav_bar: ^5.0.5 #supported
gradient_borders: ^1.0.0
http: ^1.1.0
image: ^4.5.4
intl: ^0.20.2
io: ^1.0.4
json_annotation: ^4.5.0
@@ -130,6 +131,7 @@ dependencies:
win32: ^5.1.1
window_manager: ^0.5.0
xdg_directories: ^1.0.4
zxing2: ^0.2.4
dev_dependencies:
build_runner: ^2.1.11