app_state.dart 5.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175
  1. import 'package:pb_authenticator_state/state.dart';
  2. import 'package:pb_authenticator_totp/totp.dart';
  3. import 'package:flutter/widgets.dart';
  4. import 'package:collection/collection.dart' show ListEquality;
  5. import 'package:shared_preferences/shared_preferences.dart';
  6. import 'package:flutter/material.dart';
  7. import 'package:flutter_secure_storage/flutter_secure_storage.dart';
  8. import 'package:local_auth/local_auth.dart';
  9. // TODO: Transition to new type
  10. typedef BaseItemType = LegacyAuthenticatorItem;
  11. /// Represents app state.
  12. class AppState extends ChangeNotifier {
  13. final RepositoryBase<BaseItemType> _repository;
  14. // Theme and screen capture settings
  15. ThemeMode _themeMode = ThemeMode.system;
  16. bool _screenCapturePrevented = false;
  17. // PIN and biometric settings
  18. bool _pinEnabled = false;
  19. bool _biometricEnabled = false;
  20. DateTime? _lastUnlockTime;
  21. static const _pinTimeout = Duration(minutes: 5);
  22. static const _pinKey = 'app_pin';
  23. static const _pinEnabledKey = 'pinEnabled';
  24. static const _biometricEnabledKey = 'biometricEnabled';
  25. static final _secureStorage = FlutterSecureStorage();
  26. // Show/hide icons setting
  27. bool _showIcons = true;
  28. static const _showIconsKey = 'showIcons';
  29. bool get showIcons => _showIcons;
  30. // Force monochrome icons setting
  31. bool _forceMonochromeIcons = false;
  32. static const _forceMonochromeIconsKey = 'forceMonochromeIcons';
  33. bool get forceMonochromeIcons => _forceMonochromeIcons;
  34. // Locale setting
  35. Locale _locale = const Locale('en');
  36. static const _localeKey = 'locale';
  37. Locale get locale => _locale;
  38. bool get pinEnabled => _pinEnabled;
  39. bool get biometricEnabled => _biometricEnabled;
  40. DateTime? get lastUnlockTime => _lastUnlockTime;
  41. Duration get pinTimeout => _pinTimeout;
  42. ThemeMode get themeMode => _themeMode;
  43. bool get screenCapturePrevented => _screenCapturePrevented;
  44. AppState(this._repository) {
  45. _loadSettings();
  46. }
  47. /// List of TOTP items (internal implementation).
  48. List<BaseItemType>? _items;
  49. /// TOTP items as list.
  50. List<BaseItemType>? get items {
  51. if (_items == null) {
  52. loadItems();
  53. }
  54. return _items;
  55. }
  56. Future loadItems() async {
  57. _items = await _repository.loadItems();
  58. notifyListeners();
  59. }
  60. /// Adds a TOTP item to the list.
  61. Future addItem(TotpItem item) async {
  62. await _repository.addItem(item);
  63. await loadItems();
  64. }
  65. /// Replace list of TOTP items.
  66. Future replaceItems(List<BaseItemType> items) async {
  67. await _repository.replaceItems(items);
  68. await loadItems();
  69. }
  70. bool itemsChanged(List<BaseItemType>? newItems) {
  71. return !const ListEquality().equals(items, newItems);
  72. }
  73. Future<void> _loadSettings() async {
  74. final prefs = await SharedPreferences.getInstance();
  75. final themeIndex = prefs.getInt('themeMode') ?? 0;
  76. _themeMode = ThemeMode.values[themeIndex];
  77. _screenCapturePrevented = prefs.getBool('screenCapturePrevented') ?? false;
  78. _pinEnabled = prefs.getBool(_pinEnabledKey) ?? false;
  79. _biometricEnabled = prefs.getBool(_biometricEnabledKey) ?? false;
  80. _showIcons = prefs.getBool(_showIconsKey) ?? true;
  81. _forceMonochromeIcons = prefs.getBool(_forceMonochromeIconsKey) ?? false;
  82. // Load locale
  83. final localeCode = prefs.getString(_localeKey);
  84. if (localeCode != null) {
  85. _locale = Locale(localeCode);
  86. }
  87. notifyListeners();
  88. }
  89. Future<void> setThemeMode(ThemeMode mode) async {
  90. _themeMode = mode;
  91. final prefs = await SharedPreferences.getInstance();
  92. await prefs.setInt('themeMode', mode.index);
  93. notifyListeners();
  94. }
  95. Future<void> setScreenCapturePrevented(bool value) async {
  96. _screenCapturePrevented = value;
  97. final prefs = await SharedPreferences.getInstance();
  98. await prefs.setBool('screenCapturePrevented', value);
  99. // Platform channel will handle screen capture prevention
  100. notifyListeners();
  101. }
  102. Future<void> setPinEnabled(bool value) async {
  103. _pinEnabled = value;
  104. final prefs = await SharedPreferences.getInstance();
  105. await prefs.setBool(_pinEnabledKey, value);
  106. notifyListeners();
  107. }
  108. Future<void> setBiometricEnabled(bool value) async {
  109. _biometricEnabled = value;
  110. final prefs = await SharedPreferences.getInstance();
  111. await prefs.setBool(_biometricEnabledKey, value);
  112. notifyListeners();
  113. }
  114. Future<void> setShowIcons(bool value) async {
  115. _showIcons = value;
  116. final prefs = await SharedPreferences.getInstance();
  117. await prefs.setBool(_showIconsKey, value);
  118. notifyListeners();
  119. }
  120. Future<void> setForceMonochromeIcons(bool value) async {
  121. _forceMonochromeIcons = value;
  122. final prefs = await SharedPreferences.getInstance();
  123. await prefs.setBool(_forceMonochromeIconsKey, value);
  124. notifyListeners();
  125. }
  126. Future<void> setLocale(Locale locale) async {
  127. _locale = locale;
  128. final prefs = await SharedPreferences.getInstance();
  129. await prefs.setString(_localeKey, locale.languageCode);
  130. notifyListeners();
  131. }
  132. Future<void> setPin(String pin) async {
  133. await _secureStorage.write(key: _pinKey, value: pin);
  134. }
  135. Future<String?> getPin() async {
  136. return await _secureStorage.read(key: _pinKey);
  137. }
  138. void updateLastUnlockTime() {
  139. _lastUnlockTime = DateTime.now();
  140. notifyListeners();
  141. }
  142. bool shouldRequireAuth() {
  143. if (!_pinEnabled && !_biometricEnabled) return false;
  144. if (_lastUnlockTime == null) return true;
  145. return DateTime.now().difference(_lastUnlockTime!) > _pinTimeout;
  146. }
  147. }