CH-J SM Mobileδιαχείριση διακομιστή πάνω από SSH
Μενού
Δημοσίευση πηγής

CH-J SM Mobile

Περιηγηθείτε σε καταλόγους και αρχεία για μια συγκεκριμένη έκδοση εφαρμογών.

Κατεβάστε την πηγή ZIP
CH-J / SM Mobile 134 αρχεία
sm-mobile-20261016T050549Z-3678f2a05fa9 Προστέθηκε: · Χτισμένο:
Αρχεία
app_controller.dart Προστέθηκε: Ημερομηνία που δεν έχει καταγραφεί 5,2 KB · 188 γραμμές
Λήψη αρχείου
1import 'dart:async';
2import 'dart:convert';
3import 'package:flutter/material.dart';
4import 'package:local_auth/local_auth.dart';
5import '../services/legal.dart';
6import '../services/secure_store.dart';
7import '../services/ssh_service.dart';
8import '../services/vault.dart';
9import 'failure.dart';
11class AppController extends ChangeNotifier with WidgetsBindingObserver {
12 final SecureStore store;
13 late final Vault vault = Vault(store);
14 late final Legal legal = Legal(store);
15 late final SshService ssh = SshService(vault);
16 bool ready = false, accepted = false, initialized = false, foreground = true;
17 bool biometric = false, lockBackground = true, authenticating = false;
18 bool systemDialog = false;
19 String? startupError;
20 Locale? locale;
21 ThemeMode theme = ThemeMode.system;
22 int idleMinutes = 5, monitorSeconds = 10;
23 DateTime _lastActivity = DateTime.now();
24 Timer? _timer;
25 void Function()? onLocked;
26 AppController(this.store);
27 Future<void> initialize() async {
28 WidgetsBinding.instance.addObserver(this);
29 try {
30 final prefs = jsonDecode(await store.read('preferences') ?? '{}');
31 if (['cs', 'de', 'en'].contains(prefs['language'])) {
32 locale = Locale(prefs['language']);
33 }
34 theme = ThemeMode.values.firstWhere(
35 (t) => t.name == prefs['theme'],
36 orElse: () => ThemeMode.system,
37 );
38 biometric = prefs['biometric'] == true;
39 lockBackground = prefs['lockBackground'] != false;
40 idleMinutes = [1, 5, 15].contains(prefs['idleMinutes'])
41 ? prefs['idleMinutes']
42 : 5;
43 monitorSeconds = [5, 10, 30, 60].contains(prefs['monitorSeconds'])
44 ? prefs['monitorSeconds']
45 : 10;
46 accepted = await legal.load();
47 initialized = await vault.exists();
48 } catch (e) {
49 startupError = e is Failure ? e.code : 'storageError';
50 }
51 ready = true;
52 _timer = Timer.periodic(const Duration(seconds: 1), (_) {
53 if (vault.unlocked &&
54 DateTime.now().difference(_lastActivity) >=
55 Duration(minutes: idleMinutes)) {
56 lock();
57 }
58 });
59 notifyListeners();
60 }
62 void activity() {
63 _lastActivity = DateTime.now();
64 }
66 void changed() {
67 notifyListeners();
68 }
70 Future<T> withSystemDialog<T>(Future<T> Function() action) async {
71 systemDialog = true;
72 try {
73 return await action();
74 } finally {
75 systemDialog = false;
76 activity();
77 }
78 }
80 Future<void> saveSettings() async {
81 await store.write(
82 'preferences',
83 jsonEncode({
84 'language': locale?.languageCode,
85 'theme': theme.name,
86 'biometric': biometric,
87 'lockBackground': lockBackground,
88 'idleMinutes': idleMinutes,
89 'monitorSeconds': monitorSeconds,
90 }),
91 );
92 notifyListeners();
93 }
95 Future<void> acceptLicense() async {
96 await legal.accept(guidanceDisplayed: true);
97 accepted = true;
98 notifyListeners();
99 }
101 Future<void> openVault(String password, {bool create = false}) async {
102 if (!accepted) throw const Failure('licenseError');
103 if (create) {
104 try {
105 await vault.create(password);
106 } finally {
107 initialized = await vault.exists();
108 notifyListeners();
109 }
110 } else {
111 await vault.unlock(password);
112 }
113 activity();
114 notifyListeners();
115 }
117 Future<void> biometricUnlock(String reason) async {
118 if (!accepted) throw const Failure('licenseError');
119 authenticating = true;
120 try {
121 await vault.unlockBiometric(DeviceStore.biometric(reason));
122 activity();
123 } finally {
124 authenticating = false;
125 notifyListeners();
126 }
127 }
129 Future<void> setBiometric(bool enable, String reason) async {
130 authenticating = true;
131 try {
132 if (enable) {
133 if (!await LocalAuthentication().isDeviceSupported()) {
134 throw const Failure('biometricUnavailable');
135 }
136 // This probe is only availability/UI. The key itself is gated by the OS.
137 if (!await LocalAuthentication().authenticate(
138 localizedReason: reason,
139 )) {
140 throw const Failure('unlockFailed');
141 }
142 await vault.enableBiometric(DeviceStore.biometric(reason));
143 } else {
144 await DeviceStore.biometric(reason).delete('key');
145 }
146 biometric = enable;
147 await saveSettings();
148 } finally {
149 authenticating = false;
150 notifyListeners();
151 }
152 }
154 void lock() {
155 ssh.disconnect(clear: true);
156 vault.lock();
157 onLocked?.call();
158 notifyListeners();
159 }
161 @override
162 void didChangeAppLifecycleState(AppLifecycleState state) {
163 foreground = state == AppLifecycleState.resumed;
164 if (state == AppLifecycleState.resumed) {
165 if (vault.unlocked &&
166 DateTime.now().difference(_lastActivity) >=
167 Duration(minutes: idleMinutes)) {
168 lock();
169 }
170 } else if ((state == AppLifecycleState.hidden ||
171 state == AppLifecycleState.paused ||
172 state == AppLifecycleState.detached) &&
173 !systemDialog &&
174 lockBackground) {
175 lock();
176 }
177 notifyListeners();
178 }
180 @override
181 void dispose() {
182 _timer?.cancel();
183 WidgetsBinding.instance.removeObserver(this);
184 ssh.dispose();
185 vault.lock();
186 super.dispose();
187 }
SHA-256c6ab83939bf0be12eb1f819a4932fb0dca5cb419ceda11c3e9d26c8010fe9f9a

CH-J Proprietary Software License 1.14 · Η πηγή παρέχεται στο CH-J Proprietary Software License 1.14. Η διαθεσιμότητά του δεν αλλάζει τους όρους της άδειας χρήσης ούτε χορηγεί πρόσθετα δικαιώματα.

Archive SHA-256c486a807e4828998b9c57ab0f2e760f7980b31965fe118e89d3e1a5a2b65be65