diff --git a/packages/smooth_app/ios/Podfile.lock b/packages/smooth_app/ios/Podfile.lock index 7b08af22f57..068c0add517 100644 --- a/packages/smooth_app/ios/Podfile.lock +++ b/packages/smooth_app/ios/Podfile.lock @@ -12,9 +12,11 @@ PODS: - device_info_plus (0.0.1): - Flutter - Flutter (1.0.0) + - flutter_custom_tabs (0.0.1): + - Flutter - flutter_email_sender (0.0.1): - Flutter - - flutter_image_compress (1.0.0): + - flutter_image_compress_common (1.0.0): - Flutter - Mantle - SDWebImage @@ -65,15 +67,18 @@ PODS: - iso_countries (0.0.1): - Flutter - KeychainAccess (4.2.2) - - libwebp (1.2.4): - - libwebp/demux (= 1.2.4) - - libwebp/mux (= 1.2.4) - - libwebp/webp (= 1.2.4) - - libwebp/demux (1.2.4): + - libwebp (1.3.1): + - libwebp/demux (= 1.3.1) + - libwebp/mux (= 1.3.1) + - libwebp/sharpyuv (= 1.3.1) + - libwebp/webp (= 1.3.1) + - libwebp/demux (1.3.1): - libwebp/webp - - libwebp/mux (1.2.4): + - libwebp/mux (1.3.1): - libwebp/demux - - libwebp/webp (1.2.4) + - libwebp/sharpyuv (1.3.1) + - libwebp/webp (1.3.1): + - libwebp/sharpyuv - Mantle (2.2.0): - Mantle/extobjc (= 2.2.0) - Mantle/extobjc (2.2.0) @@ -109,7 +114,7 @@ PODS: - path_provider_foundation (0.0.1): - Flutter - FlutterMacOS - - permission_handler_apple (9.0.4): + - permission_handler_apple (9.1.1): - Flutter - PromisesObjC (2.2.0) - qr_code_scanner (0.2.0): @@ -120,12 +125,16 @@ PODS: - Realm/Headers (10.37.2) - RealmSwift (10.37.2): - Realm (= 10.37.2) - - SDWebImage (5.15.5): - - SDWebImage/Core (= 5.15.5) - - SDWebImage/Core (5.15.5) - - SDWebImageWebPCoder (0.11.0): + - rive_common (0.0.1): + - Flutter + - SDWebImage (5.17.0): + - SDWebImage/Core (= 5.17.0) + - SDWebImage/Core (5.17.0) + - SDWebImageWebPCoder (0.13.0): - libwebp (~> 1.0) - - SDWebImage/Core (~> 5.15) + - SDWebImage/Core (~> 5.17) + - sensors_plus (0.0.1): + - Flutter - Sentry/HybridSDK (7.31.5) - sentry_flutter (0.0.1): - Flutter @@ -151,8 +160,9 @@ DEPENDENCIES: - data_importer (from `.symlinks/plugins/data_importer/ios`) - device_info_plus (from `.symlinks/plugins/device_info_plus/ios`) - Flutter (from `Flutter`) + - flutter_custom_tabs (from `.symlinks/plugins/flutter_custom_tabs/ios`) - flutter_email_sender (from `.symlinks/plugins/flutter_email_sender/ios`) - - flutter_image_compress (from `.symlinks/plugins/flutter_image_compress/ios`) + - flutter_image_compress_common (from `.symlinks/plugins/flutter_image_compress_common/ios`) - flutter_native_splash (from `.symlinks/plugins/flutter_native_splash/ios`) - flutter_secure_storage (from `.symlinks/plugins/flutter_secure_storage/ios`) - image_picker_ios (from `.symlinks/plugins/image_picker_ios/ios`) @@ -161,12 +171,14 @@ DEPENDENCIES: - iso_countries (from `.symlinks/plugins/iso_countries/ios`) - mobile_scanner (from `.symlinks/plugins/mobile_scanner/ios`) - package_info_plus (from `.symlinks/plugins/package_info_plus/ios`) - - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/ios`) + - path_provider_foundation (from `.symlinks/plugins/path_provider_foundation/darwin`) - permission_handler_apple (from `.symlinks/plugins/permission_handler_apple/ios`) - qr_code_scanner (from `.symlinks/plugins/qr_code_scanner/ios`) + - rive_common (from `.symlinks/plugins/rive_common/ios`) + - sensors_plus (from `.symlinks/plugins/sensors_plus/ios`) - sentry_flutter (from `.symlinks/plugins/sentry_flutter/ios`) - share_plus (from `.symlinks/plugins/share_plus/ios`) - - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/ios`) + - shared_preferences_foundation (from `.symlinks/plugins/shared_preferences_foundation/darwin`) - sqflite (from `.symlinks/plugins/sqflite/ios`) - url_launcher_ios (from `.symlinks/plugins/url_launcher_ios/ios`) - webview_flutter_wkwebview (from `.symlinks/plugins/webview_flutter_wkwebview/ios`) @@ -209,10 +221,12 @@ EXTERNAL SOURCES: :path: ".symlinks/plugins/device_info_plus/ios" Flutter: :path: Flutter + flutter_custom_tabs: + :path: ".symlinks/plugins/flutter_custom_tabs/ios" flutter_email_sender: :path: ".symlinks/plugins/flutter_email_sender/ios" - flutter_image_compress: - :path: ".symlinks/plugins/flutter_image_compress/ios" + flutter_image_compress_common: + :path: ".symlinks/plugins/flutter_image_compress_common/ios" flutter_native_splash: :path: ".symlinks/plugins/flutter_native_splash/ios" flutter_secure_storage: @@ -230,17 +244,21 @@ EXTERNAL SOURCES: package_info_plus: :path: ".symlinks/plugins/package_info_plus/ios" path_provider_foundation: - :path: ".symlinks/plugins/path_provider_foundation/ios" + :path: ".symlinks/plugins/path_provider_foundation/darwin" permission_handler_apple: :path: ".symlinks/plugins/permission_handler_apple/ios" qr_code_scanner: :path: ".symlinks/plugins/qr_code_scanner/ios" + rive_common: + :path: ".symlinks/plugins/rive_common/ios" + sensors_plus: + :path: ".symlinks/plugins/sensors_plus/ios" sentry_flutter: :path: ".symlinks/plugins/sentry_flutter/ios" share_plus: :path: ".symlinks/plugins/share_plus/ios" shared_preferences_foundation: - :path: ".symlinks/plugins/shared_preferences_foundation/ios" + :path: ".symlinks/plugins/shared_preferences_foundation/darwin" sqflite: :path: ".symlinks/plugins/sqflite/ios" url_launcher_ios: @@ -255,8 +273,9 @@ SPEC CHECKSUMS: data_importer: ab8c74aaf553878170aed03c03626d5820c5cb1f device_info_plus: e5c5da33f982a436e103237c0c85f9031142abed Flutter: f04841e97a9d0b0a8025694d0796dd46242b2854 + flutter_custom_tabs: 7a10a08686955cb748e5d26e0ae586d30689bf89 flutter_email_sender: 02d7443217d8c41483223627972bfdc09f74276b - flutter_image_compress: 5a5e9aee05b6553048b8df1c3bc456d0afaac433 + flutter_image_compress_common: ec1d45c362c9d30a3f6a0426c297f47c52007e3e flutter_native_splash: 52501b97d1c0a5f898d687f1646226c1f93c56ef flutter_secure_storage: 7953c38a04c3fdbb00571bcd87d8e3b5ceb9daec FMDB: 2ce00b547f966261cd18927a3ddb07cb6f3db82a @@ -266,12 +285,12 @@ SPEC CHECKSUMS: GoogleUtilities: c2bdc4cf2ce786c4d2e6b3bcfd599a25ca78f06f GoogleUtilitiesComponents: 679b2c881db3b615a2777504623df6122dd20afe GTMSessionFetcher: 3a63d75eecd6aa32c2fc79f578064e1214dfdec2 - image_picker_ios: b786a5dcf033a8336a657191401bfdf12017dabb + image_picker_ios: 4a8aadfbb6dc30ad5141a2ce3832af9214a705b5 in_app_review: 4a97249f7a2f539a0f294c2d9196b7fe35e49541 - integration_test: a1e7d09bd98eca2fc37aefd79d4f41ad37bdbbe5 + integration_test: 13825b8a9334a850581300559b8839134b124670 iso_countries: eb09d40f388e4c65e291e0bb36a701dfe7de6c74 KeychainAccess: c0c4f7f38f6fc7bbe58f5702e25f7bd2f65abf51 - libwebp: f62cb61d0a484ba548448a4bd52aabf150ff6eef + libwebp: 33dc822fbbf4503668d09f7885bbfedc76c45e96 Mantle: c5aa8794a29a022dfbbfc9799af95f477a69b62d MLImage: 7bb7c4264164ade9bf64f679b40fb29c8f33ee9b MLKitBarcodeScanning: 04e264482c5f3810cb89ebc134ef6b61e67db505 @@ -281,22 +300,24 @@ SPEC CHECKSUMS: MTBBarcodeScanner: f453b33c4b7dfe545d8c6484ed744d55671788cb nanopb: b552cce312b6c8484180ef47159bc0f65a1f0431 package_info_plus: 6c92f08e1f853dc01228d6f553146438dafcd14e - path_provider_foundation: c68054786f1b4f3343858c1e1d0caaded73f0be9 - permission_handler_apple: 44366e37eaf29454a1e7b1b7d736c2cceaeb17ce + path_provider_foundation: 29f094ae23ebbca9d3d0cec13889cd9060c0e943 + permission_handler_apple: e76247795d700c14ea09e3a2d8855d41ee80a2e6 PromisesObjC: 09985d6d70fbe7878040aa746d78236e6946d2ef qr_code_scanner: bb67d64904c3b9658ada8c402e8b4d406d5d796e Realm: 09e17879e909fafc5336864bff22272de9bd11bf RealmSwift: adaf8f6fd925b41447089fcfab71b9b7cdc3982d - SDWebImage: fd7e1a22f00303e058058278639bf6196ee431fe - SDWebImageWebPCoder: 295a6573c512f54ad2dd58098e64e17dcf008499 + rive_common: 8a159d68033a8b073e5853acc50f03aa486a2888 + SDWebImage: 750adf017a315a280c60fde706ab1e552a3ae4e9 + SDWebImageWebPCoder: af09429398d99d524cae2fe00f6f0f6e491ed102 + sensors_plus: bda64198ccc7d3ccbb49e6ae17d92f67687bee20 Sentry: 4c9babff9034785067c896fd580b1f7de44da020 sentry_flutter: 1346a880b24c0240807b53b10cf50ddad40f504e share_plus: 056a1e8ac890df3e33cb503afffaf1e9b4fbae68 - shared_preferences_foundation: 986fc17f3d3251412d18b0265f9c64113a8c2472 + shared_preferences_foundation: 5b919d13b803cadd15ed2dc053125c68730e5126 sqflite: 6d358c025f5b867b29ed92fc697fd34924e11904 - url_launcher_ios: ae1517e5e344f5544fb090b079e11f399dfbe4d2 + url_launcher_ios: 08a3dfac5fb39e8759aeb0abbd5d9480f30fc8b4 webview_flutter_wkwebview: b7e70ef1ddded7e69c796c7390ee74180182971f PODFILE CHECKSUM: aa97d8b016d7264ad0a1ef5c44765133ae787bc4 -COCOAPODS: 1.12.0 +COCOAPODS: 1.12.1 diff --git a/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart b/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart index 789667dda47..3fd5876282f 100644 --- a/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart +++ b/packages/smooth_app/lib/generic_lib/dialogs/smooth_alert_dialog.dart @@ -594,6 +594,7 @@ class SmoothSimpleErrorAlertDialog extends StatelessWidget { body: content, positiveAction: positiveButton, negativeAction: negativeAction, + actionsAxis: actionsAxis, actionsOrder: actionsOrder, contentPadding: contentPadding, ); diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index b61ee80fbbb..13e8bd0ae83 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1855,6 +1855,18 @@ "@gallery_source_label": { "description": "Label for the gallery image source" }, + "gallery_source_access_denied_dialog_title": "Access denied", + "@gallery_source_access_denied_dialog_title": { + "description": "On iOS, the user has refused to give the permission (title of the dialog)" + }, + "gallery_source_access_denied_dialog_message_ios": "Unfortunately, the application can't access your gallery, as you have previously denied the permission.\n\nPlease go to the app settings in your phone Settings -> Photos", + "@gallery_source_access_denied_dialog_message_ios": { + "description": "On iOS, the user has refused to give the permission" + }, + "gallery_source_access_denied_dialog_button": "Open the Settings", + "@gallery_source_access_denied_dialog_button": { + "description": "Button to open the app settings" + }, "share": "Share", "@share": { "description": "Button label for sharing something on another app. For example sharing the link to a product via Email" diff --git a/packages/smooth_app/lib/pages/image_crop_page.dart b/packages/smooth_app/lib/pages/image_crop_page.dart index 38c002cdb3f..a74fecd0c18 100644 --- a/packages/smooth_app/lib/pages/image_crop_page.dart +++ b/packages/smooth_app/lib/pages/image_crop_page.dart @@ -1,7 +1,9 @@ import 'dart:io'; +import 'package:app_settings/app_settings.dart'; import 'package:flutter/material.dart'; import 'package:flutter/rendering.dart'; +import 'package:flutter/services.dart'; import 'package:flutter_gen/gen_l10n/app_localizations.dart'; import 'package:http/http.dart' as http; import 'package:image_picker/image_picker.dart'; @@ -12,20 +14,36 @@ import 'package:smooth_app/data_models/preferences/user_preferences.dart'; import 'package:smooth_app/database/dao_int.dart'; import 'package:smooth_app/generic_lib/bottom_sheets/smooth_bottom_sheet.dart'; import 'package:smooth_app/generic_lib/design_constants.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; import 'package:smooth_app/generic_lib/loading_dialog.dart'; import 'package:smooth_app/helpers/camera_helper.dart'; import 'package:smooth_app/helpers/database_helper.dart'; import 'package:smooth_app/pages/crop_page.dart'; /// Picks an image file from gallery or camera. -Future pickImageFile(final State widget) async { +Future pickImageFile( + final State widget, { + bool ignorePlatformException = false, +}) async { final UserPictureSource? source = await _getUserPictureSource(widget.context); if (source == null) { return null; } final ImagePicker picker = ImagePicker(); if (source == UserPictureSource.GALLERY) { - return picker.pickImage(source: ImageSource.gallery); + try { + return picker.pickImage(source: ImageSource.gallery); + } on PlatformException catch (e) { + // On debug builds this catch won't work. + // Please run on profile/release modes to test it + if (ignorePlatformException) { + return null; + } else if (e.code == 'photo_access_denied') { + throw PhotoAccessDenied(); + } else { + rethrow; + } + } } return picker.pickImage(source: ImageSource.camera); } @@ -215,7 +233,20 @@ Future confirmAndUploadNewPicture( required final String barcode, required final OpenFoodFactsLanguage language, }) async { - final XFile? croppedPhoto = await pickImageFile(widget); + XFile? croppedPhoto; + try { + croppedPhoto = await pickImageFile(widget); + } on PhotoAccessDenied catch (_) { + final bool? res = await _onGalleryAccessDenied(widget); + if (res == true) { + // Let's retry + croppedPhoto = await pickImageFile( + widget, + ignorePlatformException: true, + ); + } + } + if (croppedPhoto == null) { return null; } @@ -228,7 +259,7 @@ Future confirmAndUploadNewPicture( builder: (BuildContext context) => CropPage( barcode: barcode, imageField: imageField, - inputFile: File(croppedPhoto.path), + inputFile: File(croppedPhoto!.path), initiallyDifferent: true, language: language, ), @@ -237,6 +268,36 @@ Future confirmAndUploadNewPicture( ); } +Future _onGalleryAccessDenied(State widget) { + return showDialog( + context: widget.context, + builder: (BuildContext context) { + final AppLocalizations appLocalizations = AppLocalizations.of(context); + return SmoothSimpleErrorAlertDialog( + title: appLocalizations.gallery_source_access_denied_dialog_title, + message: + appLocalizations.gallery_source_access_denied_dialog_message_ios, + positiveAction: SmoothActionButton( + text: appLocalizations.gallery_source_access_denied_dialog_button, + onPressed: () { + AppSettings.openAppSettings(callback: () { + if (widget.mounted) { + Navigator.of(context).maybePop(true); + } + }); + }, + ), + negativeAction: SmoothActionButton( + text: appLocalizations.close, + onPressed: () { + Navigator.of(context).maybePop(false); + }, + ), + actionsAxis: Axis.vertical, + ); + }); +} + /// Downloads an image URL into a file, with a dialog. Future downloadImageUrl( final BuildContext context, @@ -287,3 +348,5 @@ Future _downloadImageFile(DaoInt daoInt, String url) async { return file.writeAsBytes(response.bodyBytes); } + +class PhotoAccessDenied implements Exception {}