From 99f85fb359e117a3f971e0fb190a09643cef181b Mon Sep 17 00:00:00 2001 From: Fynn Petersen-Frey <10599762+fyfrey@users.noreply.github.com> Date: Mon, 26 Jun 2023 17:27:32 +0200 Subject: [PATCH] feat(Android): guard against missing EXIF info (#2965) --- .../backup/services/backup.service.dart | 11 +++++ .../gallery_permission.provider.dart | 42 ++++++++++++------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/mobile/lib/modules/backup/services/backup.service.dart b/mobile/lib/modules/backup/services/backup.service.dart index 8984a18282..cedc9e4886 100644 --- a/mobile/lib/modules/backup/services/backup.service.dart +++ b/mobile/lib/modules/backup/services/backup.service.dart @@ -16,7 +16,9 @@ import 'package:immich_mobile/shared/providers/db.provider.dart'; import 'package:immich_mobile/shared/services/api.service.dart'; import 'package:immich_mobile/utils/files_helper.dart'; import 'package:isar/isar.dart'; +import 'package:logging/logging.dart'; import 'package:openapi/api.dart'; +import 'package:permission_handler/permission_handler.dart'; import 'package:photo_manager/photo_manager.dart'; import 'package:http_parser/http_parser.dart'; import 'package:path/path.dart' as p; @@ -33,6 +35,7 @@ class BackupService { final httpClient = http.Client(); final ApiService _apiService; final Isar _db; + final Logger _log = Logger("BackupService"); BackupService(this._apiService, this._db); @@ -203,6 +206,14 @@ class BackupService { Function(CurrentUploadAsset) setCurrentUploadAssetCb, Function(ErrorUploadAsset) errorCb, ) async { + if (Platform.isAndroid && + !(await Permission.accessMediaLocation.status).isGranted) { + // double check that permission is granted here, to guard against + // uploading corrupt assets without EXIF information + _log.warning("Media location permission is not granted. " + "Cannot access original assets for backup."); + return false; + } final String deviceId = Store.get(StoreKey.deviceId); final String savedEndpoint = Store.get(StoreKey.serverEndpoint); File? file; diff --git a/mobile/lib/modules/onboarding/providers/gallery_permission.provider.dart b/mobile/lib/modules/onboarding/providers/gallery_permission.provider.dart index 61ea75e5bf..7554a6a6bf 100644 --- a/mobile/lib/modules/onboarding/providers/gallery_permission.provider.dart +++ b/mobile/lib/modules/onboarding/providers/gallery_permission.provider.dart @@ -6,7 +6,7 @@ import 'package:permission_handler/permission_handler.dart'; class GalleryPermissionNotifier extends StateNotifier { GalleryPermissionNotifier() - : super(PermissionStatus.denied) // Denied is the intitial state + : super(PermissionStatus.denied) // Denied is the intitial state { // Sets the initial state getGalleryPermissionStatus(); @@ -16,19 +16,20 @@ class GalleryPermissionNotifier extends StateNotifier { /// Requests the gallery permission Future requestGalleryPermission() async { + PermissionStatus result; // Android 32 and below uses Permission.storage if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt <= 32) { // Android 32 and below need storage final permission = await Permission.storage.request(); - state = permission; - return permission; + result = permission; } else { // Android 33 need photo & video final photos = await Permission.photos.request(); if (!photos.isGranted) { // Don't ask twice for the same permission + state = photos; return photos; } final videos = await Permission.videos.request(); @@ -45,28 +46,32 @@ class GalleryPermissionNotifier extends StateNotifier { status = PermissionStatus.denied; } - state = status; - return status; + result = status; + } + if (result == PermissionStatus.granted && + androidInfo.version.sdkInt >= 29) { + result = await Permission.accessMediaLocation.request(); } } else { // iOS can use photos final photos = await Permission.photos.request(); - state = photos; - return photos; + result = photos; } + state = result; + return result; } /// Checks the current state of the gallery permissions without /// requesting them again Future getGalleryPermissionStatus() async { + PermissionStatus result; // Android 32 and below uses Permission.storage if (Platform.isAndroid) { final androidInfo = await DeviceInfoPlugin().androidInfo; if (androidInfo.version.sdkInt <= 32) { // Android 32 and below need storage final permission = await Permission.storage.status; - state = permission; - return permission; + result = permission; } else { // Android 33 needs photo & video final photos = await Permission.photos.status; @@ -84,18 +89,23 @@ class GalleryPermissionNotifier extends StateNotifier { status = PermissionStatus.denied; } - state = status; - return status; + result = status; + } + if (state == PermissionStatus.granted && + androidInfo.version.sdkInt >= 29) { + result = await Permission.accessMediaLocation.status; } } else { // iOS can use photos final photos = await Permission.photos.status; - state = photos; - return photos; + result = photos; } + state = result; + return result; } } -final galleryPermissionNotifier - = StateNotifierProvider - ((ref) => GalleryPermissionNotifier()); +final galleryPermissionNotifier = + StateNotifierProvider( + (ref) => GalleryPermissionNotifier(), +);