Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Error message when the access to the user gallery is denied (iOS only) #4464

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
85 changes: 53 additions & 32 deletions packages/smooth_app/ios/Podfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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):
Expand All @@ -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
Expand All @@ -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`)
Expand All @@ -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`)
Expand Down Expand Up @@ -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:
Expand All @@ -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:
Expand All @@ -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
Expand All @@ -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
Expand All @@ -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
Original file line number Diff line number Diff line change
Expand Up @@ -594,6 +594,7 @@ class SmoothSimpleErrorAlertDialog extends StatelessWidget {
body: content,
positiveAction: positiveButton,
negativeAction: negativeAction,
actionsAxis: actionsAxis,
actionsOrder: actionsOrder,
contentPadding: contentPadding,
);
Expand Down
12 changes: 12 additions & 0 deletions packages/smooth_app/lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
71 changes: 67 additions & 4 deletions packages/smooth_app/lib/pages/image_crop_page.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand All @@ -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<XFile?> pickImageFile(final State<StatefulWidget> widget) async {
Future<XFile?> pickImageFile(
final State<StatefulWidget> 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);
}
Expand Down Expand Up @@ -215,7 +233,20 @@ Future<File?> 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;
}
Expand All @@ -228,7 +259,7 @@ Future<File?> confirmAndUploadNewPicture(
builder: (BuildContext context) => CropPage(
barcode: barcode,
imageField: imageField,
inputFile: File(croppedPhoto.path),
inputFile: File(croppedPhoto!.path),
initiallyDifferent: true,
language: language,
),
Expand All @@ -237,6 +268,36 @@ Future<File?> confirmAndUploadNewPicture(
);
}

Future<bool?> _onGalleryAccessDenied(State<StatefulWidget> widget) {
return showDialog<bool>(
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<File?> downloadImageUrl(
final BuildContext context,
Expand Down Expand Up @@ -287,3 +348,5 @@ Future<File?> _downloadImageFile(DaoInt daoInt, String url) async {

return file.writeAsBytes(response.bodyBytes);
}

class PhotoAccessDenied implements Exception {}