From 38ea2248b84df867c7a7ff43caaf7585533e6b90 Mon Sep 17 00:00:00 2001 From: Neeraj Gupta <254676+ua741@users.noreply.github.com> Date: Thu, 21 Aug 2025 15:16:21 +0530 Subject: [PATCH] Show option on mobile only --- mobile/apps/auth/lib/ui/home_page.dart | 31 ++--- mobile/apps/auth/lib/ui/qr_test_page.dart | 46 +++++++ .../auth/plugins/qr/analysis_options.yaml | 2 +- .../qr/example/ios/Runner/AppDelegate.swift | 15 +++ .../auth/plugins/qr/example/lib/main.dart | 124 ++++++++++++++++++ .../apps/auth/plugins/qr/example/pubspec.yaml | 45 +++++++ mobile/apps/auth/plugins/qr/lib/ente_qr.dart | 6 +- .../qr/lib/ente_qr_method_channel.dart | 3 +- .../auth/plugins/qr/test/ente_qr_test.dart | 3 +- 9 files changed, 253 insertions(+), 22 deletions(-) create mode 100644 mobile/apps/auth/lib/ui/qr_test_page.dart create mode 100644 mobile/apps/auth/plugins/qr/example/ios/Runner/AppDelegate.swift create mode 100644 mobile/apps/auth/plugins/qr/example/lib/main.dart create mode 100644 mobile/apps/auth/plugins/qr/example/pubspec.yaml diff --git a/mobile/apps/auth/lib/ui/home_page.dart b/mobile/apps/auth/lib/ui/home_page.dart index 999379ee86..33fdf5c4e5 100644 --- a/mobile/apps/auth/lib/ui/home_page.dart +++ b/mobile/apps/auth/lib/ui/home_page.dart @@ -875,21 +875,22 @@ class _HomePageState extends State { 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, - ), - SpeedDialChild( - child: const Icon(Icons.photo_library), - backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor, - foregroundColor: Theme.of(context).colorScheme.fabForegroundColor, - labelWidget: - const SpeedDialLabelWidget("Import from Gallery (Native)"), - onTap: _importFromGalleryNative, - ), + if (PlatformUtil.isMobile()) + 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, + ), + if (PlatformUtil.isMobile()) + SpeedDialChild( + child: const Icon(Icons.photo_library), + backgroundColor: Theme.of(context).colorScheme.fabBackgroundColor, + foregroundColor: Theme.of(context).colorScheme.fabForegroundColor, + labelWidget: const SpeedDialLabelWidget("Scan image (Native)"), + onTap: _importFromGalleryNative, + ), ], ); } diff --git a/mobile/apps/auth/lib/ui/qr_test_page.dart b/mobile/apps/auth/lib/ui/qr_test_page.dart new file mode 100644 index 0000000000..dbd6656783 --- /dev/null +++ b/mobile/apps/auth/lib/ui/qr_test_page.dart @@ -0,0 +1,46 @@ +import 'package:flutter/material.dart'; +import 'package:ente_qr/ente_qr.dart'; + +class QrTestPage extends StatefulWidget { + @override + _QrTestPageState createState() => _QrTestPageState(); +} + +class _QrTestPageState extends State { + String _platformVersion = 'Unknown'; + final _enteQr = EnteQr(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + Future initPlatformState() async { + String platformVersion; + try { + platformVersion = + await _enteQr.getPlatformVersion() ?? 'Unknown platform version'; + } catch (e) { + platformVersion = 'Failed to get platform version: $e'; + } + + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + @override + Widget build(BuildContext context) { + return Scaffold( + appBar: AppBar( + title: const Text('QR Plugin Test'), + ), + body: Center( + child: Text('Running on: $_platformVersion\n'), + ), + ); + } +} diff --git a/mobile/apps/auth/plugins/qr/analysis_options.yaml b/mobile/apps/auth/plugins/qr/analysis_options.yaml index ec06379686..f04c6cf0f3 100644 --- a/mobile/apps/auth/plugins/qr/analysis_options.yaml +++ b/mobile/apps/auth/plugins/qr/analysis_options.yaml @@ -1 +1 @@ -include: ../../../../analysis_options.yaml +include: ../../analysis_options.yaml diff --git a/mobile/apps/auth/plugins/qr/example/ios/Runner/AppDelegate.swift b/mobile/apps/auth/plugins/qr/example/ios/Runner/AppDelegate.swift new file mode 100644 index 0000000000..964982012b --- /dev/null +++ b/mobile/apps/auth/plugins/qr/example/ios/Runner/AppDelegate.swift @@ -0,0 +1,15 @@ +import Flutter +import UIKit + +import ente_qr + +@UIApplicationMain +@objc class AppDelegate: FlutterAppDelegate { + override func application( + _ application: UIApplication, + didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? + ) -> Bool { + GeneratedPluginRegistrant.register(with: self) + return super.application(application, didFinishLaunchingWithOptions: launchOptions) + } +} diff --git a/mobile/apps/auth/plugins/qr/example/lib/main.dart b/mobile/apps/auth/plugins/qr/example/lib/main.dart new file mode 100644 index 0000000000..cb25f66dc8 --- /dev/null +++ b/mobile/apps/auth/plugins/qr/example/lib/main.dart @@ -0,0 +1,124 @@ +import 'dart:async'; + +import 'package:ente_qr/ente_qr.dart'; +import 'package:file_picker/file_picker.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; + +void main() { + runApp(const MyApp()); +} + +class MyApp extends StatefulWidget { + const MyApp({super.key}); + + @override + State createState() => _MyAppState(); +} + +class _MyAppState extends State { + String _platformVersion = 'Unknown'; + String _qrResult = 'No QR code scanned yet'; + final _enteQrPlugin = EnteQr(); + + @override + void initState() { + super.initState(); + initPlatformState(); + } + + // Platform messages are asynchronous, so we initialize in an async method. + Future initPlatformState() async { + String platformVersion; + // Platform messages may fail, so we use a try/catch PlatformException. + // We also handle the message potentially returning null. + try { + platformVersion = await _enteQrPlugin.getPlatformVersion() ?? + 'Unknown platform version'; + } on PlatformException { + platformVersion = 'Failed to get platform version.'; + } + + // If the widget was removed from the tree while the asynchronous platform + // message was in flight, we want to discard the reply rather than calling + // setState to update our non-existent appearance. + if (!mounted) return; + + setState(() { + _platformVersion = platformVersion; + }); + } + + Future _pickImageAndScanQr() async { + try { + final FilePickerResult? result = await FilePicker.platform.pickFiles( + type: FileType.image, + allowMultiple: false, + ); + + if (result != null && result.files.single.path != null) { + final String imagePath = result.files.single.path!; + + setState(() { + _qrResult = 'Scanning QR code...'; + }); + + final QrScanResult qrResult = + await _enteQrPlugin.scanQrFromImage(imagePath); + + setState(() { + if (qrResult.success) { + _qrResult = 'QR Code found: ${qrResult.content}'; + } else { + _qrResult = 'Error: ${qrResult.error}'; + } + }); + } else { + setState(() { + _qrResult = 'No image selected'; + }); + } + } catch (e) { + setState(() { + _qrResult = 'Error picking file: $e'; + }); + } + } + + @override + Widget build(BuildContext context) { + return MaterialApp( + home: Scaffold( + appBar: AppBar( + title: const Text('Ente QR Plugin Example'), + ), + body: Padding( + padding: const EdgeInsets.all(16.0), + child: Column( + crossAxisAlignment: CrossAxisAlignment.stretch, + children: [ + Text('Running on: $_platformVersion\n'), + const SizedBox(height: 20), + ElevatedButton( + onPressed: _pickImageAndScanQr, + child: const Text('Pick Image and Scan QR Code'), + ), + const SizedBox(height: 20), + Container( + padding: const EdgeInsets.all(16), + decoration: BoxDecoration( + border: Border.all(color: Colors.grey), + borderRadius: BorderRadius.circular(8), + ), + child: Text( + _qrResult, + style: const TextStyle(fontSize: 16), + ), + ), + ], + ), + ), + ), + ); + } +} diff --git a/mobile/apps/auth/plugins/qr/example/pubspec.yaml b/mobile/apps/auth/plugins/qr/example/pubspec.yaml new file mode 100644 index 0000000000..8eee59207d --- /dev/null +++ b/mobile/apps/auth/plugins/qr/example/pubspec.yaml @@ -0,0 +1,45 @@ +name: ente_qr_example +description: "Demonstrates how to use the ente_qr plugin." +publish_to: 'none' # Remove this line if you wish to publish to pub.dev + +environment: + sdk: '>=3.4.3 <4.0.0' + +dependencies: + flutter: + sdk: flutter + + ente_qr: + # When depending on this package from a real application you should use: + # ente_qr: ^x.y.z + # See https://dart.dev/tools/pub/dependencies#version-constraints + # The example app is bundled with the plugin so we use a path dependency on + # the parent directory to use the current plugin's version. + path: ../ + + # The following adds the Cupertino Icons font to your application. + # Use with the CupertinoIcons class for iOS style icons. + cupertino_icons: ^1.0.6 + file_picker: ^5.5.0 + +dev_dependencies: + flutter_test: + sdk: flutter + + # The "flutter_lints" package below contains a set of recommended lints to + # encourage good coding practices. The lint set provided by the package is + # activated in the `analysis_options.yaml` file located at the root of your + # package. See that file for information about deactivating specific lint + # rules and activating additional ones. + flutter_lints: ^3.0.0 + +# For information on the generic Dart part of this file, see the +# following page: https://dart.dev/tools/pub/pubspec + +# The following section is specific to Flutter packages. +flutter: + + # The following line ensures that the Material Icons font is + # included with your application, so that you can use the icons in + # the material Icons class. + uses-material-design: true diff --git a/mobile/apps/auth/plugins/qr/lib/ente_qr.dart b/mobile/apps/auth/plugins/qr/lib/ente_qr.dart index d58544c930..0488d6aeec 100644 --- a/mobile/apps/auth/plugins/qr/lib/ente_qr.dart +++ b/mobile/apps/auth/plugins/qr/lib/ente_qr.dart @@ -8,12 +8,12 @@ class EnteQr { } /// Scans a QR code from an image file at the given path. - /// + /// /// [imagePath] - The file path to the image containing the QR code - /// + /// /// Returns a [QrScanResult] containing either the QR code content on success /// or an error message on failure. - /// + /// /// Example: /// ```dart /// final qr = EnteQr(); diff --git a/mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart b/mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart index d8542a7309..825466405a 100644 --- a/mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart +++ b/mobile/apps/auth/plugins/qr/lib/ente_qr_method_channel.dart @@ -28,7 +28,8 @@ class MethodChannelEnteQr extends EnteQrPlatform { } // Convert to Map safely - final Map resultMap = Map.from(result as Map); + final Map resultMap = + Map.from(result as Map); final bool success = resultMap['success'] as bool? ?? false; if (success) { diff --git a/mobile/apps/auth/plugins/qr/test/ente_qr_test.dart b/mobile/apps/auth/plugins/qr/test/ente_qr_test.dart index 665f225ef4..15e343ba72 100644 --- a/mobile/apps/auth/plugins/qr/test/ente_qr_test.dart +++ b/mobile/apps/auth/plugins/qr/test/ente_qr_test.dart @@ -7,12 +7,11 @@ import 'package:plugin_platform_interface/plugin_platform_interface.dart'; class MockEnteQrPlatform with MockPlatformInterfaceMixin implements EnteQrPlatform { - @override Future getPlatformVersion() => Future.value('42'); @override - Future scanQrFromImage(String imagePath) => + Future scanQrFromImage(String imagePath) => Future.value(QrScanResult.error('Mock implementation')); }