diff --git a/lib/main.dart b/lib/main.dart index d2c30c8b..4fb4f85d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -75,7 +75,11 @@ void _initLog() { } Future _initPref() async { - await Pref().init(); + final provider = PrefSharedPreferencesProvider(); + await provider.init(); + final pref = Pref.scoped(provider); + Pref.setGlobalInstance(pref); + if (Pref().getLastVersion() == null) { if (Pref().getSetupProgress() == null) { // new install diff --git a/lib/pref.dart b/lib/pref.dart index 1c710382..2a2b8780 100644 --- a/lib/pref.dart +++ b/lib/pref.dart @@ -9,21 +9,21 @@ import 'package:nc_photos/use_case/compat/v32.dart'; import 'package:shared_preferences/shared_preferences.dart'; class Pref { - factory Pref() => _inst; + factory Pref() { + _inst ??= Pref.scoped(PrefMemoryProvider()); + return _inst!; + } - Pref.scoped(); + Pref.scoped(this.provider); - Future init() async { - if (await CompatV32.isPrefNeedMigration()) { - await CompatV32.migratePref(); - } - return SharedPreferences.getInstance().then((pref) { - _pref = pref; - }); + /// Set the global [Pref] instance returned by the default constructor + static void setGlobalInstance(Pref pref) { + assert(_inst == null); + _inst = pref; } List? getAccounts2() { - final jsonObjs = _pref.getStringList(_toKey(PrefKey.accounts2)); + final jsonObjs = provider.getStringList(PrefKey.accounts2); return jsonObjs?.map((e) => PrefAccount.fromJson(jsonDecode(e))).toList(); } @@ -31,127 +31,129 @@ class Pref { getAccounts2() ?? def; Future setAccounts2(List value) { final jsons = value.map((e) => jsonEncode(e.toJson())).toList(); - return _setStringList(PrefKey.accounts2, jsons); + return provider.setStringList(PrefKey.accounts2, jsons); } - int? getCurrentAccountIndex() => - _pref.getInt(_toKey(PrefKey.currentAccountIndex)); + int? getCurrentAccountIndex() => provider.getInt(PrefKey.currentAccountIndex); int getCurrentAccountIndexOr(int def) => getCurrentAccountIndex() ?? def; Future setCurrentAccountIndex(int value) => - _setInt(PrefKey.currentAccountIndex, value); + provider.setInt(PrefKey.currentAccountIndex, value); - int? getHomePhotosZoomLevel() => - _pref.getInt(_toKey(PrefKey.homePhotosZoomLevel)); + int? getHomePhotosZoomLevel() => provider.getInt(PrefKey.homePhotosZoomLevel); int getHomePhotosZoomLevelOr(int def) => getHomePhotosZoomLevel() ?? def; Future setHomePhotosZoomLevel(int value) => - _setInt(PrefKey.homePhotosZoomLevel, value); + provider.setInt(PrefKey.homePhotosZoomLevel, value); int? getAlbumBrowserZoomLevel() => - _pref.getInt(_toKey(PrefKey.albumBrowserZoomLevel)); + provider.getInt(PrefKey.albumBrowserZoomLevel); int getAlbumBrowserZoomLevelOr(int def) => getAlbumBrowserZoomLevel() ?? def; Future setAlbumBrowserZoomLevel(int value) => - _setInt(PrefKey.albumBrowserZoomLevel, value); + provider.setInt(PrefKey.albumBrowserZoomLevel, value); - int? getHomeAlbumsSort() => _pref.getInt(_toKey(PrefKey.homeAlbumsSort)); + int? getHomeAlbumsSort() => provider.getInt(PrefKey.homeAlbumsSort); int getHomeAlbumsSortOr(int def) => getHomeAlbumsSort() ?? def; Future setHomeAlbumsSort(int value) => - _setInt(PrefKey.homeAlbumsSort, value); + provider.setInt(PrefKey.homeAlbumsSort, value); - bool? isEnableExif() => _pref.getBool(_toKey(PrefKey.enableExif)); + bool? isEnableExif() => provider.getBool(PrefKey.enableExif); bool isEnableExifOr([bool def = true]) => isEnableExif() ?? def; - Future setEnableExif(bool value) => _setBool(PrefKey.enableExif, value); + Future setEnableExif(bool value) => + provider.setBool(PrefKey.enableExif, value); int? getViewerScreenBrightness() => - _pref.getInt(_toKey(PrefKey.viewerScreenBrightness)); + provider.getInt(PrefKey.viewerScreenBrightness); int getViewerScreenBrightnessOr([int def = -1]) => getViewerScreenBrightness() ?? def; Future setViewerScreenBrightness(int value) => - _setInt(PrefKey.viewerScreenBrightness, value); + provider.setInt(PrefKey.viewerScreenBrightness, value); bool? isViewerForceRotation() => - _pref.getBool(_toKey(PrefKey.viewerForceRotation)); + provider.getBool(PrefKey.viewerForceRotation); bool isViewerForceRotationOr([bool def = false]) => isViewerForceRotation() ?? def; Future setViewerForceRotation(bool value) => - _setBool(PrefKey.viewerForceRotation, value); + provider.setBool(PrefKey.viewerForceRotation, value); - int? getSetupProgress() => _pref.getInt(_toKey(PrefKey.setupProgress)); + int? getSetupProgress() => provider.getInt(PrefKey.setupProgress); int getSetupProgressOr([int def = 0]) => getSetupProgress() ?? def; Future setSetupProgress(int value) => - _setInt(PrefKey.setupProgress, value); + provider.setInt(PrefKey.setupProgress, value); /// Return the version number when the app last ran - int? getLastVersion() => _pref.getInt(_toKey(PrefKey.lastVersion)); + int? getLastVersion() => provider.getInt(PrefKey.lastVersion); int getLastVersionOr(int def) => getLastVersion() ?? def; - Future setLastVersion(int value) => _setInt(PrefKey.lastVersion, value); + Future setLastVersion(int value) => + provider.setInt(PrefKey.lastVersion, value); - bool? isDarkTheme() => _pref.getBool(_toKey(PrefKey.darkTheme)); + bool? isDarkTheme() => provider.getBool(PrefKey.darkTheme); bool isDarkThemeOr(bool def) => isDarkTheme() ?? def; - Future setDarkTheme(bool value) => _setBool(PrefKey.darkTheme, value); + Future setDarkTheme(bool value) => + provider.setBool(PrefKey.darkTheme, value); - bool? isFollowSystemTheme() => - _pref.getBool(_toKey(PrefKey.followSystemTheme)); + bool? isFollowSystemTheme() => provider.getBool(PrefKey.followSystemTheme); bool isFollowSystemThemeOr(bool def) => isFollowSystemTheme() ?? def; Future setFollowSystemTheme(bool value) => - _setBool(PrefKey.followSystemTheme, value); + provider.setBool(PrefKey.followSystemTheme, value); bool? isUseBlackInDarkTheme() => - _pref.getBool(_toKey(PrefKey.useBlackInDarkTheme)); + provider.getBool(PrefKey.useBlackInDarkTheme); bool isUseBlackInDarkThemeOr(bool def) => isUseBlackInDarkTheme() ?? def; Future setUseBlackInDarkTheme(bool value) => - _setBool(PrefKey.useBlackInDarkTheme, value); + provider.setBool(PrefKey.useBlackInDarkTheme, value); - int? getLanguage() => _pref.getInt(_toKey(PrefKey.language)); + int? getLanguage() => provider.getInt(PrefKey.language); int getLanguageOr(int def) => getLanguage() ?? def; - Future setLanguage(int value) => _setInt(PrefKey.language, value); + Future setLanguage(int value) => + provider.setInt(PrefKey.language, value); - int? getSlideshowDuration() => - _pref.getInt(_toKey(PrefKey.slideshowDuration)); + int? getSlideshowDuration() => provider.getInt(PrefKey.slideshowDuration); int getSlideshowDurationOr(int def) => getSlideshowDuration() ?? def; Future setSlideshowDuration(int value) => - _setInt(PrefKey.slideshowDuration, value); + provider.setInt(PrefKey.slideshowDuration, value); - bool? isSlideshowShuffle() => - _pref.getBool(_toKey(PrefKey.isSlideshowShuffle)); + bool? isSlideshowShuffle() => provider.getBool(PrefKey.isSlideshowShuffle); bool isSlideshowShuffleOr(bool def) => isSlideshowShuffle() ?? def; Future setSlideshowShuffle(bool value) => - _setBool(PrefKey.isSlideshowShuffle, value); + provider.setBool(PrefKey.isSlideshowShuffle, value); - bool? isSlideshowRepeat() => _pref.getBool(_toKey(PrefKey.isSlideshowRepeat)); + bool? isSlideshowRepeat() => provider.getBool(PrefKey.isSlideshowRepeat); bool isSlideshowRepeatOr(bool def) => isSlideshowRepeat() ?? def; Future setSlideshowRepeat(bool value) => - _setBool(PrefKey.isSlideshowRepeat, value); + provider.setBool(PrefKey.isSlideshowRepeat, value); bool? isAlbumBrowserShowDate() => - _pref.getBool(_toKey(PrefKey.isAlbumBrowserShowDate)); + provider.getBool(PrefKey.isAlbumBrowserShowDate); bool isAlbumBrowserShowDateOr([bool def = false]) => isAlbumBrowserShowDate() ?? def; Future setAlbumBrowserShowDate(bool value) => - _setBool(PrefKey.isAlbumBrowserShowDate, value); + provider.setBool(PrefKey.isAlbumBrowserShowDate, value); - bool? hasNewSharedAlbum() => _pref.getBool(_toKey(PrefKey.newSharedAlbum)); + bool? hasNewSharedAlbum() => provider.getBool(PrefKey.newSharedAlbum); bool hasNewSharedAlbumOr(bool def) => hasNewSharedAlbum() ?? def; Future setNewSharedAlbum(bool value) => - _setBool(PrefKey.newSharedAlbum, value); + provider.setBool(PrefKey.newSharedAlbum, value); bool? isLabEnableSharedAlbum() => - _pref.getBool(_toKey(PrefKey.labEnableSharedAlbum)); + provider.getBool(PrefKey.labEnableSharedAlbum); bool isLabEnableSharedAlbumOr(bool def) => isLabEnableSharedAlbum() ?? def; Future setLabEnableSharedAlbum(bool value) => - _setBool(PrefKey.labEnableSharedAlbum, value); + provider.setBool(PrefKey.labEnableSharedAlbum, value); - Future _setBool(PrefKey key, bool value) async { - return _onPostSet(await _pref.setBool(_toKey(key), value), key, value); - } + final PrefProvider provider; - Future _setInt(PrefKey key, int value) async { - return _onPostSet(await _pref.setInt(_toKey(key), value), key, value); - } + static Pref? _inst; +} - Future _setStringList(PrefKey key, List value) async { - return _onPostSet( - await _pref.setStringList(_toKey(key), value), key, value); - } +/// Provide the data for [Pref] +abstract class PrefProvider { + bool? getBool(PrefKey key); + Future setBool(PrefKey key, bool value); + + int? getInt(PrefKey key); + Future setInt(PrefKey key, int value); + + List? getStringList(PrefKey key); + Future setStringList(PrefKey key, List value); bool _onPostSet(bool result, PrefKey key, dynamic value) { if (result) { @@ -161,56 +163,90 @@ class Pref { return false; } } +} - String _toKey(PrefKey key) { - switch (key) { - case PrefKey.accounts2: - return "accounts2"; - case PrefKey.currentAccountIndex: - return "currentAccountIndex"; - case PrefKey.homePhotosZoomLevel: - return "homePhotosZoomLevel"; - case PrefKey.albumBrowserZoomLevel: - return "albumViewerZoomLevel"; - case PrefKey.homeAlbumsSort: - return "homeAlbumsSort"; - case PrefKey.enableExif: - return "isEnableExif"; - case PrefKey.viewerScreenBrightness: - return "viewerScreenBrightness"; - case PrefKey.viewerForceRotation: - return "viewerForceRotation"; - case PrefKey.setupProgress: - return "setupProgress"; - case PrefKey.lastVersion: - return "lastVersion"; - case PrefKey.darkTheme: - return "isDarkTheme"; - case PrefKey.followSystemTheme: - return "isFollowSystemTheme"; - case PrefKey.useBlackInDarkTheme: - return "isUseBlackInDarkTheme"; - case PrefKey.language: - return "language"; - case PrefKey.newSharedAlbum: - return "hasNewSharedAlbum"; - case PrefKey.labEnableSharedAlbum: - return "isLabEnableSharedAlbum"; - case PrefKey.slideshowDuration: - return "slideshowDuration"; - case PrefKey.isSlideshowShuffle: - return "isSlideshowShuffle"; - case PrefKey.isSlideshowRepeat: - return "isSlideshowRepeat"; - case PrefKey.isAlbumBrowserShowDate: - return "isAlbumBrowserShowDate"; +/// [Pref] stored with [SharedPreferences] lib +class PrefSharedPreferencesProvider extends PrefProvider { + Future init() async { + if (await CompatV32.isPrefNeedMigration()) { + await CompatV32.migratePref(); } + return SharedPreferences.getInstance().then((pref) { + _pref = pref; + }); + } + + @override + getBool(PrefKey key) => _pref.getBool(key.toStringKey()); + + @override + setBool(PrefKey key, bool value) async { + return _onPostSet( + await _pref.setBool(key.toStringKey(), value), key, value); + } + + @override + getInt(PrefKey key) => _pref.getInt(key.toStringKey()); + + @override + setInt(PrefKey key, int value) async { + return _onPostSet(await _pref.setInt(key.toStringKey(), value), key, value); + } + + @override + getStringList(PrefKey key) => _pref.getStringList(key.toStringKey()); + + @override + setStringList(PrefKey key, List value) async { + return _onPostSet( + await _pref.setStringList(key.toStringKey(), value), key, value); } - static late final _inst = Pref.scoped(); late SharedPreferences _pref; } +/// [Pref] stored in memory +class PrefMemoryProvider extends PrefProvider { + PrefMemoryProvider([ + Map initialData = const {}, + ]) : _data = Map.of(initialData); + + @override + getBool(PrefKey key) => _data[key.toStringKey()]; + + @override + setBool(PrefKey key, bool value) async { + return _onPostSet(() { + _data[key.toStringKey()] = value; + return true; + }(), key, value); + } + + @override + getInt(PrefKey key) => _data[key.toStringKey()]; + + @override + setInt(PrefKey key, int value) async { + return _onPostSet(() { + _data[key.toStringKey()] = value; + return true; + }(), key, value); + } + + @override + getStringList(PrefKey key) => _data[key.toStringKey()]; + + @override + setStringList(PrefKey key, List value) async { + return _onPostSet(() { + _data[key.toStringKey()] = value; + return true; + }(), key, value); + } + + final Map _data; +} + class PrefAccount { const PrefAccount( this.account, [ @@ -274,6 +310,53 @@ enum PrefKey { isAlbumBrowserShowDate, } +extension on PrefKey { + String toStringKey() { + switch (this) { + case PrefKey.accounts2: + return "accounts2"; + case PrefKey.currentAccountIndex: + return "currentAccountIndex"; + case PrefKey.homePhotosZoomLevel: + return "homePhotosZoomLevel"; + case PrefKey.albumBrowserZoomLevel: + return "albumViewerZoomLevel"; + case PrefKey.homeAlbumsSort: + return "homeAlbumsSort"; + case PrefKey.enableExif: + return "isEnableExif"; + case PrefKey.viewerScreenBrightness: + return "viewerScreenBrightness"; + case PrefKey.viewerForceRotation: + return "viewerForceRotation"; + case PrefKey.setupProgress: + return "setupProgress"; + case PrefKey.lastVersion: + return "lastVersion"; + case PrefKey.darkTheme: + return "isDarkTheme"; + case PrefKey.followSystemTheme: + return "isFollowSystemTheme"; + case PrefKey.useBlackInDarkTheme: + return "isUseBlackInDarkTheme"; + case PrefKey.language: + return "language"; + case PrefKey.newSharedAlbum: + return "hasNewSharedAlbum"; + case PrefKey.labEnableSharedAlbum: + return "isLabEnableSharedAlbum"; + case PrefKey.slideshowDuration: + return "slideshowDuration"; + case PrefKey.isSlideshowShuffle: + return "isSlideshowShuffle"; + case PrefKey.isSlideshowRepeat: + return "isSlideshowRepeat"; + case PrefKey.isAlbumBrowserShowDate: + return "isAlbumBrowserShowDate"; + } + } +} + extension PrefExtension on Pref { Account? getCurrentAccount() { try {