From 71641593b81a2a45c504bfe697f1d90f22549e6d Mon Sep 17 00:00:00 2001 From: Aline Bonnet Date: Tue, 23 Jul 2024 19:00:46 +0200 Subject: [PATCH 1/5] feat: transfert photos from phone gallery to application --- android/app/src/main/AndroidManifest.xml | 18 +++++- lib/main.dart | 1 + lib/page/homepage.dart | 70 +++++++++++++++++++++--- lib/service/routing.dart | 7 +-- pubspec.yaml | 2 + 5 files changed, 85 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f2ca8b1..8793f07 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,14 @@ - - " + + + + + + @@ -27,6 +34,13 @@ + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index bd3bab8..0a956fb 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -29,6 +29,7 @@ import 'package:native_exif/native_exif.dart'; import 'package:app_settings/app_settings.dart'; import 'package:flutter_exif_plugin/flutter_exif_plugin.dart'; import 'package:sensors/sensors.dart'; +import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'component/loader.dart'; import 'service/api/api.dart'; import 'constant.dart'; diff --git a/lib/page/homepage.dart b/lib/page/homepage.dart index 800bde5..07f5c7c 100644 --- a/lib/page/homepage.dart +++ b/lib/page/homepage.dart @@ -11,11 +11,63 @@ class _HomePageState extends State { late bool isLoading; GeoVisioCollections? geoVisionCollections; + late StreamSubscription _intentSub; + List? _sharedFiles; + @override void initState() { super.initState(); isLoading = true; getCollections(); + listenSendingIntent(); + } + + @override + void dispose() { + _intentSub.cancel(); + super.dispose(); + } + + void listenSendingIntent() { + // Listen to media sharing coming from outside the app while the app is in the memory. + _intentSub = ReceiveSharingIntent.instance.getMediaStream().listen((value) { + _sharedFiles = value; + if (_sharedFiles != null && _sharedFiles!.isNotEmpty) { + final fileList = sharedFilesToImages(_sharedFiles!); + GetIt.instance() + .pushTo(Routes.newSequenceSend, arguments: fileList); + } + }, onError: (err) { + print("getIntentDataStream error: $err"); + }); + + // Get the media sharing coming from outside the app while the app is closed. + ReceiveSharingIntent.instance.getInitialMedia().then((value) { + _sharedFiles = value; + // Tell the library that we are done processing the intent. + ReceiveSharingIntent.instance.reset(); + if (_sharedFiles != null && _sharedFiles!.isNotEmpty) { + final fileList = sharedFilesToImages(_sharedFiles!); + GetIt.instance() + .pushTo(Routes.newSequenceSend, arguments: fileList); + } + }); + } + + List sharedFilesToImages(List list) { + return list + .where((element) => isImage(element)) + .map((item) => File(item.path)) + .toList(); + } + + bool isImage(SharedMediaFile file) { + final fileExtension = file.path.split('.').last.toLowerCase(); + return (fileExtension == 'jpg' || + fileExtension == 'jpeg' || + fileExtension == 'png' || + fileExtension == 'gif' || + fileExtension == 'bmp'); } Future getCollections() async { @@ -39,9 +91,9 @@ class _HomePageState extends State { if (!await PermissionHelper.isPermissionGranted()) { await PermissionHelper.askMissingPermission(); } - await availableCameras().then( - (availableCameras) => GetIt.instance().pushTo(Routes.newSequenceCapture, arguments: availableCameras) - ); + await availableCameras().then((availableCameras) => + GetIt.instance() + .pushTo(Routes.newSequenceCapture, arguments: availableCameras)); } Widget displayBody(isLoading) { @@ -76,7 +128,8 @@ class _HomePageState extends State { child: Semantics( header: true, child: Text(AppLocalizations.of(context)!.yourSequence, - style: GoogleFonts.nunito(fontSize: 25, fontWeight: FontWeight.w400)), + style: GoogleFonts.nunito( + fontSize: 25, fontWeight: FontWeight.w400)), ), ), Expanded( @@ -106,7 +159,8 @@ class CollectionListView extends StatelessWidget { Widget build(BuildContext context) { return ListView.builder( itemCount: collections.length, - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + physics: + const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), itemBuilder: (BuildContext context, int index) { return CollectionPreview(collections[index]); }, @@ -147,7 +201,8 @@ class NoElementView extends StatelessWidget { Center( child: Text( AppLocalizations.of(context)!.emptyError, - style: GoogleFonts.nunito(fontSize: 18, color: Colors.grey, fontWeight: FontWeight.w400), + style: GoogleFonts.nunito( + fontSize: 18, color: Colors.grey, fontWeight: FontWeight.w400), ), ) ], @@ -168,7 +223,8 @@ class UnknownErrorView extends StatelessWidget { Center( child: Text( AppLocalizations.of(context)!.unknownError, - style: GoogleFonts.nunito(fontSize: 20, color: Colors.red, fontWeight: FontWeight.w400), + style: GoogleFonts.nunito( + fontSize: 20, color: Colors.red, fontWeight: FontWeight.w400), ), ) ], diff --git a/lib/service/routing.dart b/lib/service/routing.dart index 2d7ace3..1c47999 100644 --- a/lib/service/routing.dart +++ b/lib/service/routing.dart @@ -13,7 +13,7 @@ class Routes extends Equatable { newSequenceCapture, newSequenceSend, instance, - newSequenceUpload + newSequenceUpload, ]; } @@ -24,11 +24,10 @@ class NavigationService { } dynamic pushReplacementTo(String route, {dynamic arguments}) { - return navigatorkey.currentState?.pushReplacementNamed(route, arguments: arguments); + return navigatorkey.currentState + ?.pushReplacementNamed(route, arguments: arguments); } - - dynamic goBack() { return navigatorkey.currentState?.pop(); } diff --git a/pubspec.yaml b/pubspec.yaml index 79d44f8..c6d2735 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -55,6 +55,8 @@ dependencies: app_settings: ^5.1.1 flutter_exif_plugin: ^1.1.0 sensors: ^2.0.3 + android_intent: ^2.0.2 + receive_sharing_intent: ^1.8.0 dev_dependencies: flutter_test: From b5062af6bc4a1cb2e0fcbef7960681c5991fb16c Mon Sep 17 00:00:00 2001 From: Aline Bonnet Date: Sun, 18 Aug 2024 16:32:09 +0200 Subject: [PATCH 2/5] feat: send only selected pictures --- lib/page/collection_creation_page.dart | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/page/collection_creation_page.dart b/lib/page/collection_creation_page.dart index 7125dea..b960b79 100644 --- a/lib/page/collection_creation_page.dart +++ b/lib/page/collection_creation_page.dart @@ -23,6 +23,8 @@ class CollectionCreationPageState extends State { ]); } + final Map _selectedFiles = {}; + @override void initState() { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, @@ -31,12 +33,19 @@ class CollectionCreationPageState extends State { DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); + for (var file in widget.imgList) { + _selectedFiles[file] = true; + } super.initState(); } void goToInstancePage() { + final list = _selectedFiles.entries + .where((entry) => entry.value) + .map((entry) => entry.key) + .toList(); GetIt.instance() - .pushTo(Routes.instance, arguments: widget.imgList); + .pushTo(Routes.instance, arguments: list); } @override @@ -66,14 +75,8 @@ class CollectionCreationPageState extends State { spacing: 8, runSpacing: 8, children: [ - ...widget.imgList - .map((item) => Container( - height: 100, - child: Image.file( - item, - fit: BoxFit.cover, - ))) - .toList() + for (var file in _selectedFiles.keys) + PictureItem(file), ], )))), Padding( @@ -91,4 +94,30 @@ class CollectionCreationPageState extends State { ), )); } + + Widget PictureItem(File file) { + return Stack( + alignment: Alignment.bottomRight, + children: [ + Container( + height: 80, + child: Image.file( + file, + fit: BoxFit.cover, + )), + SizedBox( + height: 24.0, + width: 24.0, + child: Checkbox( + value: _selectedFiles[file], + onChanged: (value) { + setState(() { + _selectedFiles[file] = value!; + }); + }, + ), + ) + ], + ); + } } From 5d3b2c729922e323ae64dc6e2c58235f569ecfdb Mon Sep 17 00:00:00 2001 From: Aline Bonnet Date: Tue, 23 Jul 2024 19:00:46 +0200 Subject: [PATCH 3/5] feat: transfert photos from phone gallery to application --- android/app/src/main/AndroidManifest.xml | 18 +++++- lib/main.dart | 1 + lib/page/homepage.dart | 70 +++++++++++++++++++++--- lib/service/routing.dart | 7 +-- pubspec.yaml | 3 + 5 files changed, 86 insertions(+), 13 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index f2ca8b1..8793f07 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,8 +1,14 @@ - - " + + + + + + @@ -27,6 +34,13 @@ + + + + + + + diff --git a/lib/main.dart b/lib/main.dart index a5e4207..571e741 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -24,6 +24,7 @@ import 'package:native_exif/native_exif.dart'; import 'package:flutter_exif_plugin/flutter_exif_plugin.dart'; import 'package:wakelock_plus/wakelock_plus.dart'; import 'package:sensors/sensors.dart'; +import 'package:receive_sharing_intent/receive_sharing_intent.dart'; import 'component/loader.dart'; import 'service/api/api.dart'; import 'constant.dart'; diff --git a/lib/page/homepage.dart b/lib/page/homepage.dart index 800bde5..07f5c7c 100644 --- a/lib/page/homepage.dart +++ b/lib/page/homepage.dart @@ -11,11 +11,63 @@ class _HomePageState extends State { late bool isLoading; GeoVisioCollections? geoVisionCollections; + late StreamSubscription _intentSub; + List? _sharedFiles; + @override void initState() { super.initState(); isLoading = true; getCollections(); + listenSendingIntent(); + } + + @override + void dispose() { + _intentSub.cancel(); + super.dispose(); + } + + void listenSendingIntent() { + // Listen to media sharing coming from outside the app while the app is in the memory. + _intentSub = ReceiveSharingIntent.instance.getMediaStream().listen((value) { + _sharedFiles = value; + if (_sharedFiles != null && _sharedFiles!.isNotEmpty) { + final fileList = sharedFilesToImages(_sharedFiles!); + GetIt.instance() + .pushTo(Routes.newSequenceSend, arguments: fileList); + } + }, onError: (err) { + print("getIntentDataStream error: $err"); + }); + + // Get the media sharing coming from outside the app while the app is closed. + ReceiveSharingIntent.instance.getInitialMedia().then((value) { + _sharedFiles = value; + // Tell the library that we are done processing the intent. + ReceiveSharingIntent.instance.reset(); + if (_sharedFiles != null && _sharedFiles!.isNotEmpty) { + final fileList = sharedFilesToImages(_sharedFiles!); + GetIt.instance() + .pushTo(Routes.newSequenceSend, arguments: fileList); + } + }); + } + + List sharedFilesToImages(List list) { + return list + .where((element) => isImage(element)) + .map((item) => File(item.path)) + .toList(); + } + + bool isImage(SharedMediaFile file) { + final fileExtension = file.path.split('.').last.toLowerCase(); + return (fileExtension == 'jpg' || + fileExtension == 'jpeg' || + fileExtension == 'png' || + fileExtension == 'gif' || + fileExtension == 'bmp'); } Future getCollections() async { @@ -39,9 +91,9 @@ class _HomePageState extends State { if (!await PermissionHelper.isPermissionGranted()) { await PermissionHelper.askMissingPermission(); } - await availableCameras().then( - (availableCameras) => GetIt.instance().pushTo(Routes.newSequenceCapture, arguments: availableCameras) - ); + await availableCameras().then((availableCameras) => + GetIt.instance() + .pushTo(Routes.newSequenceCapture, arguments: availableCameras)); } Widget displayBody(isLoading) { @@ -76,7 +128,8 @@ class _HomePageState extends State { child: Semantics( header: true, child: Text(AppLocalizations.of(context)!.yourSequence, - style: GoogleFonts.nunito(fontSize: 25, fontWeight: FontWeight.w400)), + style: GoogleFonts.nunito( + fontSize: 25, fontWeight: FontWeight.w400)), ), ), Expanded( @@ -106,7 +159,8 @@ class CollectionListView extends StatelessWidget { Widget build(BuildContext context) { return ListView.builder( itemCount: collections.length, - physics: const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), + physics: + const BouncingScrollPhysics(parent: AlwaysScrollableScrollPhysics()), itemBuilder: (BuildContext context, int index) { return CollectionPreview(collections[index]); }, @@ -147,7 +201,8 @@ class NoElementView extends StatelessWidget { Center( child: Text( AppLocalizations.of(context)!.emptyError, - style: GoogleFonts.nunito(fontSize: 18, color: Colors.grey, fontWeight: FontWeight.w400), + style: GoogleFonts.nunito( + fontSize: 18, color: Colors.grey, fontWeight: FontWeight.w400), ), ) ], @@ -168,7 +223,8 @@ class UnknownErrorView extends StatelessWidget { Center( child: Text( AppLocalizations.of(context)!.unknownError, - style: GoogleFonts.nunito(fontSize: 20, color: Colors.red, fontWeight: FontWeight.w400), + style: GoogleFonts.nunito( + fontSize: 20, color: Colors.red, fontWeight: FontWeight.w400), ), ) ], diff --git a/lib/service/routing.dart b/lib/service/routing.dart index 2d7ace3..1c47999 100644 --- a/lib/service/routing.dart +++ b/lib/service/routing.dart @@ -13,7 +13,7 @@ class Routes extends Equatable { newSequenceCapture, newSequenceSend, instance, - newSequenceUpload + newSequenceUpload, ]; } @@ -24,11 +24,10 @@ class NavigationService { } dynamic pushReplacementTo(String route, {dynamic arguments}) { - return navigatorkey.currentState?.pushReplacementNamed(route, arguments: arguments); + return navigatorkey.currentState + ?.pushReplacementNamed(route, arguments: arguments); } - - dynamic goBack() { return navigatorkey.currentState?.pop(); } diff --git a/pubspec.yaml b/pubspec.yaml index 1728041..0b539b6 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -52,6 +52,9 @@ dependencies: flutter_exif_plugin: ^1.1.0 sensors: ^2.0.3 wakelock_plus: ^1.2.7 + android_intent: ^2.0.2 + receive_sharing_intent: ^1.8.0 + dev_dependencies: flutter_test: From 29b03ae69134675ae0f5eb47cd89e7fe73f85a9b Mon Sep 17 00:00:00 2001 From: Aline Bonnet Date: Sun, 18 Aug 2024 16:32:09 +0200 Subject: [PATCH 4/5] feat: send only selected pictures --- lib/page/collection_creation_page.dart | 47 +++++++++++++++++++++----- 1 file changed, 38 insertions(+), 9 deletions(-) diff --git a/lib/page/collection_creation_page.dart b/lib/page/collection_creation_page.dart index 7125dea..b960b79 100644 --- a/lib/page/collection_creation_page.dart +++ b/lib/page/collection_creation_page.dart @@ -23,6 +23,8 @@ class CollectionCreationPageState extends State { ]); } + final Map _selectedFiles = {}; + @override void initState() { SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, @@ -31,12 +33,19 @@ class CollectionCreationPageState extends State { DeviceOrientation.portraitUp, DeviceOrientation.portraitDown, ]); + for (var file in widget.imgList) { + _selectedFiles[file] = true; + } super.initState(); } void goToInstancePage() { + final list = _selectedFiles.entries + .where((entry) => entry.value) + .map((entry) => entry.key) + .toList(); GetIt.instance() - .pushTo(Routes.instance, arguments: widget.imgList); + .pushTo(Routes.instance, arguments: list); } @override @@ -66,14 +75,8 @@ class CollectionCreationPageState extends State { spacing: 8, runSpacing: 8, children: [ - ...widget.imgList - .map((item) => Container( - height: 100, - child: Image.file( - item, - fit: BoxFit.cover, - ))) - .toList() + for (var file in _selectedFiles.keys) + PictureItem(file), ], )))), Padding( @@ -91,4 +94,30 @@ class CollectionCreationPageState extends State { ), )); } + + Widget PictureItem(File file) { + return Stack( + alignment: Alignment.bottomRight, + children: [ + Container( + height: 80, + child: Image.file( + file, + fit: BoxFit.cover, + )), + SizedBox( + height: 24.0, + width: 24.0, + child: Checkbox( + value: _selectedFiles[file], + onChanged: (value) { + setState(() { + _selectedFiles[file] = value!; + }); + }, + ), + ) + ], + ); + } } From dc038ed210cfe285e75ed3bb3307013d16caa309 Mon Sep 17 00:00:00 2001 From: Jean-Baptiste <87148630+Jean-BaptisteC@users.noreply.github.com> Date: Wed, 21 Aug 2024 09:56:59 +0200 Subject: [PATCH 5/5] chore: Bump Github actions --- .github/workflows/android.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index ab872a8..3071363 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -15,7 +15,7 @@ jobs: - uses: subosito/flutter-action@v2 with: channel: 'stable' - - uses: actions/setup-java@v3 + - uses: actions/setup-java@v4 with: java-version: '17' distribution: 'adopt'