From a3c92baca50dfc234d3540fd9198941fcbe6a2a3 Mon Sep 17 00:00:00 2001 From: Ming Ming Date: Wed, 8 Jun 2022 02:51:37 +0800 Subject: [PATCH] Allow processing exif over data network --- app/lib/future_extension.dart | 3 ++ app/lib/l10n/app_en.arb | 8 +++++ app/lib/l10n/untranslated-messages.txt | 22 +++++++++++++ app/lib/metadata_task_manager.dart | 14 ++++++-- app/lib/pref.dart | 12 +++++++ app/lib/service.dart | 29 +++++++++++++++-- app/lib/use_case/update_missing_metadata.dart | 10 ++++-- app/lib/widget/settings.dart | 32 +++++++++++++++++++ 8 files changed, 124 insertions(+), 6 deletions(-) create mode 100644 app/lib/future_extension.dart diff --git a/app/lib/future_extension.dart b/app/lib/future_extension.dart new file mode 100644 index 00000000..89dd5677 --- /dev/null +++ b/app/lib/future_extension.dart @@ -0,0 +1,3 @@ +extension FutureNotNullExtension on Future { + Future notNull() async => (await this)!; +} diff --git a/app/lib/l10n/app_en.arb b/app/lib/l10n/app_en.arb index 0e3d2cc4..514d324e 100644 --- a/app/lib/l10n/app_en.arb +++ b/app/lib/l10n/app_en.arb @@ -309,6 +309,14 @@ "@settingsExifSupportTrueSubtitle": { "description": "Subtitle of the EXIF support setting when the value is true. The goal is to warn user about the possible side effects of enabling this setting" }, + "settingsExifWifiOnlyTitle": "Process EXIF over Wi-Fi only", + "@settingsExifWifiOnlyTitle": { + "description": "Whether to only process EXIF data when connected to a Wi-Fi network" + }, + "settingsExifWifiOnlyFalseSubtitle": "Data charges may apply", + "@settingsExifWifiOnlyFalseSubtitle": { + "description": "Shown when users allow processing exif data over any network" + }, "settingsMemoriesTitle": "Memories", "@settingsMemoriesTitle": { "description": "Memory albums contain photos taken in a specific time range in the past" diff --git a/app/lib/l10n/untranslated-messages.txt b/app/lib/l10n/untranslated-messages.txt index 44d39ed5..b688500e 100644 --- a/app/lib/l10n/untranslated-messages.txt +++ b/app/lib/l10n/untranslated-messages.txt @@ -1,5 +1,7 @@ { "cs": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsMemoriesTitle", "settingsMemoriesSubtitle", "settingsAccountTitle", @@ -110,6 +112,8 @@ ], "de": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsMemoriesTitle", "settingsMemoriesSubtitle", "settingsAccountTitle", @@ -235,6 +239,8 @@ "el": [ "collectionsTooltip", + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsMemoriesTitle", "settingsMemoriesSubtitle", "settingsAccountTitle", @@ -413,6 +419,8 @@ ], "es": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -442,6 +450,8 @@ ], "fi": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsMiscellaneousTitle", "settingsMiscellaneousPageTitle", "settingsPhotosTabSortByNameTitle", @@ -454,6 +464,8 @@ "fr": [ "collectionsTooltip", + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -482,6 +494,8 @@ ], "pl": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -528,6 +542,8 @@ ], "pt": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -553,6 +569,8 @@ ], "ru": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -578,6 +596,8 @@ ], "zh": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", @@ -603,6 +623,8 @@ ], "zh_Hant": [ + "settingsExifWifiOnlyTitle", + "settingsExifWifiOnlyFalseSubtitle", "settingsPhotoEnhancementTitle", "settingsPhotoEnhancementPageTitle", "settingsEnhanceMaxResolutionTitle", diff --git a/app/lib/metadata_task_manager.dart b/app/lib/metadata_task_manager.dart index 4ee9893c..cb7608c3 100644 --- a/app/lib/metadata_task_manager.dart +++ b/app/lib/metadata_task_manager.dart @@ -32,7 +32,8 @@ class MetadataTask { for (final r in account.roots) { final dir = File(path: file_util.unstripPath(account, r)); hasScanShareFolder |= file_util.isOrUnderDir(shareFolder, dir); - final op = UpdateMissingMetadata(fileRepo); + final op = UpdateMissingMetadata( + fileRepo, const _UpdateMissingMetadataConfigProvider()); await for (final _ in op(account, dir)) { if (!Pref().isEnableExifOr()) { _log.info("[call] EXIF disabled, task ending immaturely"); @@ -42,7 +43,8 @@ class MetadataTask { } } if (!hasScanShareFolder) { - final op = UpdateMissingMetadata(fileRepo); + final op = UpdateMissingMetadata( + fileRepo, const _UpdateMissingMetadataConfigProvider()); await for (final _ in op( account, shareFolder, @@ -117,3 +119,11 @@ class MetadataTaskManager { static MetadataTaskManager? _inst; } + +class _UpdateMissingMetadataConfigProvider + implements UpdateMissingMetadataConfigProvider { + const _UpdateMissingMetadataConfigProvider(); + + @override + isWifiOnly() async => Pref().shouldProcessExifWifiOnlyOr(); +} diff --git a/app/lib/pref.dart b/app/lib/pref.dart index 3aefc5b4..2cf54cee 100644 --- a/app/lib/pref.dart +++ b/app/lib/pref.dart @@ -208,6 +208,15 @@ class Pref { value, (key, value) => provider.setBool(key, value)); + bool? shouldProcessExifWifiOnly() => + provider.getBool(PrefKey.shouldProcessExifWifiOnly); + bool shouldProcessExifWifiOnlyOr([bool def = true]) => + shouldProcessExifWifiOnly() ?? def; + Future setProcessExifWifiOnly(bool value) => _set( + PrefKey.shouldProcessExifWifiOnly, + value, + (key, value) => provider.setBool(key, value)); + Future _set(PrefKey key, T value, Future Function(PrefKey key, T value) setFn) async { if (await setFn(key, value)) { @@ -502,6 +511,7 @@ enum PrefKey { hasShownEnhanceInfo, firstRunTime, isPhotosTabSortByName, + shouldProcessExifWifiOnly, // account pref isEnableFaceRecognitionApp, @@ -566,6 +576,8 @@ extension on PrefKey { return "firstRunTime"; case PrefKey.isPhotosTabSortByName: return "isPhotosTabSortByName"; + case PrefKey.shouldProcessExifWifiOnly: + return "shouldProcessExifWifiOnly"; // account pref case PrefKey.isEnableFaceRecognitionApp: diff --git a/app/lib/service.dart b/app/lib/service.dart index 76032cfc..de454242 100644 --- a/app/lib/service.dart +++ b/app/lib/service.dart @@ -13,6 +13,7 @@ import 'package:nc_photos/entity/file/data_source.dart'; import 'package:nc_photos/entity/file_util.dart' as file_util; import 'package:nc_photos/event/event.dart'; import 'package:nc_photos/event/native_event.dart'; +import 'package:nc_photos/future_extension.dart'; import 'package:nc_photos/language_util.dart' as language_util; import 'package:nc_photos/pref.dart'; import 'package:nc_photos/use_case/update_missing_metadata.dart'; @@ -35,6 +36,9 @@ Future startService() async { onBackground: () => throw UnimplementedError(), ), ); + // sync settings + await ServiceConfig.setProcessExifWifiOnly( + Pref().shouldProcessExifWifiOnlyOr()); await service.start(); } @@ -54,6 +58,12 @@ void serviceMain() async { await _Service()(); } +class ServiceConfig { + static Future setProcessExifWifiOnly(bool flag) async { + await Preference.setBool(_servicePref, _servicePrefProcessWifiOnly, flag); + } +} + class _Service { Future call() async { final service = FlutterBackgroundService(); @@ -229,7 +239,8 @@ class _MetadataTask { for (final r in account.roots) { final dir = File(path: file_util.unstripPath(account, r)); hasScanShareFolder |= file_util.isOrUnderDir(shareFolder, dir); - final updater = UpdateMissingMetadata(fileRepo); + final updater = UpdateMissingMetadata( + fileRepo, const _UpdateMissingMetadataConfigProvider()); void onServiceStop() { _log.info("[_updateMetadata] Stopping task: user canceled"); updater.stop(); @@ -247,7 +258,8 @@ class _MetadataTask { } } if (!hasScanShareFolder) { - final shareUpdater = UpdateMissingMetadata(fileRepo); + final shareUpdater = UpdateMissingMetadata( + fileRepo, const _UpdateMissingMetadataConfigProvider()); void onServiceStop() { _log.info("[_updateMetadata] Stopping task: user canceled"); shareUpdater.stop(); @@ -295,7 +307,20 @@ class _MetadataTask { static final _log = Logger("service._MetadataTask"); } +class _UpdateMissingMetadataConfigProvider + implements UpdateMissingMetadataConfigProvider { + const _UpdateMissingMetadataConfigProvider(); + + @override + isWifiOnly() => + Preference.getBool(_servicePref, _servicePrefProcessWifiOnly, true) + .notNull(); +} + const _dataKeyEvent = "event"; const _eventStop = "stop"; +const _servicePref = "service"; +const _servicePrefProcessWifiOnly = "shouldProcessWifiOnly"; + final _log = Logger("service"); diff --git a/app/lib/use_case/update_missing_metadata.dart b/app/lib/use_case/update_missing_metadata.dart index 3e2957bc..4e87dfd7 100644 --- a/app/lib/use_case/update_missing_metadata.dart +++ b/app/lib/use_case/update_missing_metadata.dart @@ -14,8 +14,12 @@ import 'package:nc_photos/use_case/load_metadata.dart'; import 'package:nc_photos/use_case/scan_missing_metadata.dart'; import 'package:nc_photos/use_case/update_property.dart'; +abstract class UpdateMissingMetadataConfigProvider { + Future isWifiOnly(); +} + class UpdateMissingMetadata { - UpdateMissingMetadata(this.fileRepo); + UpdateMissingMetadata(this.fileRepo, this.configProvider); /// Update metadata for all files that support one under a dir /// @@ -91,7 +95,8 @@ class UpdateMissingMetadata { Future _ensureWifi() async { var count = 0; - while (!await connectivity_util.isWifi()) { + while (await configProvider.isWifiOnly() && + !await connectivity_util.isWifi()) { if (!_shouldRun) { throw const InterruptedException(); } @@ -117,6 +122,7 @@ class UpdateMissingMetadata { } final FileRepo fileRepo; + final UpdateMissingMetadataConfigProvider configProvider; bool _shouldRun = true; diff --git a/app/lib/widget/settings.dart b/app/lib/widget/settings.dart index c8289b16..09220091 100644 --- a/app/lib/widget/settings.dart +++ b/app/lib/widget/settings.dart @@ -15,6 +15,7 @@ import 'package:nc_photos/mobile/platform.dart' import 'package:nc_photos/platform/k.dart' as platform_k; import 'package:nc_photos/platform/notification.dart'; import 'package:nc_photos/pref.dart'; +import 'package:nc_photos/service.dart'; import 'package:nc_photos/snack_bar_manager.dart'; import 'package:nc_photos/theme.dart'; import 'package:nc_photos/widget/fancy_option_picker.dart'; @@ -63,6 +64,7 @@ class _SettingsState extends State { initState() { super.initState(); _isEnableExif = Pref().isEnableExifOr(); + _shouldProcessExifWifiOnly = Pref().shouldProcessExifWifiOnlyOr(); final settings = AccountPref.of(widget.account); _isEnableMemoryAlbum = settings.isEnableMemoryAlbumOr(true); @@ -111,6 +113,15 @@ class _SettingsState extends State { value: _isEnableExif, onChanged: (value) => _onExifSupportChanged(context, value), ), + if (platform_k.isMobile) + SwitchListTile( + title: Text(L10n.global().settingsExifWifiOnlyTitle), + subtitle: _shouldProcessExifWifiOnly + ? null + : Text(L10n.global().settingsExifWifiOnlyFalseSubtitle), + value: _shouldProcessExifWifiOnly, + onChanged: _isEnableExif ? _onExifWifiOnlyChanged : null, + ), SwitchListTile( title: Text(L10n.global().settingsMemoriesTitle), subtitle: Text(L10n.global().settingsMemoriesSubtitle), @@ -322,6 +333,26 @@ class _SettingsState extends State { } } + Future _onExifWifiOnlyChanged(bool value) async { + _log.info("[_onExifWifiOnlyChanged] New value: $value"); + final oldValue = _shouldProcessExifWifiOnly; + setState(() { + _shouldProcessExifWifiOnly = value; + }); + if (!await Pref().setProcessExifWifiOnly(value)) { + _log.severe("[_onExifWifiOnlyChanged] Failed writing pref"); + SnackBarManager().showSnackBar(SnackBar( + content: Text(L10n.global().writePreferenceFailureNotification), + duration: k.snackBarDurationNormal, + )); + setState(() { + _shouldProcessExifWifiOnly = oldValue; + }); + } else { + ServiceConfig.setProcessExifWifiOnly(value); + } + } + Future _onEnableMemoryAlbumChanged(bool value) async { _log.info("[_onEnableMemoryAlbumChanged] New value: $value"); final oldValue = _isEnableMemoryAlbum; @@ -408,6 +439,7 @@ class _SettingsState extends State { } late bool _isEnableExif; + late bool _shouldProcessExifWifiOnly; late bool _isEnableMemoryAlbum; late final _prefUpdatedListener =