feat: Save profile defaults per device

This commit is contained in:
Janez T
2026-03-18 13:34:50 +01:00
parent 87cb890877
commit 29b75a3155
7 changed files with 257 additions and 26 deletions

View File

@@ -0,0 +1,29 @@
import '../models/device_info.dart';
class ProfileDeviceKeyResolver {
static String? resolve({
required DeviceInfo deviceInfo,
required ConnectionMode connectionMode,
}) {
final publicKey = deviceInfo.publicKey;
if (publicKey != null && publicKey.isNotEmpty) {
final hex = publicKey
.map((byte) => byte.toRadixString(16).padLeft(2, '0'))
.join();
if (hex.isNotEmpty) {
return 'pk:$hex';
}
}
final deviceId = deviceInfo.deviceId?.trim();
if (deviceId == null || deviceId.isEmpty) {
return null;
}
if (connectionMode == ConnectionMode.usb && deviceId == 'usb') {
return null;
}
return 'id:$deviceId';
}
}

View File

@@ -9,11 +9,14 @@ import 'profiles_feature_service.dart';
class ProfileManager with ChangeNotifier {
static const String _profilesKey = 'profiles_library';
static const String activeProfileIdKey = 'profiles_active_profile_id';
static const String _deviceProfileDefaultsKey =
'profiles_device_active_profile_ids';
static const String _transferHistoryKey = 'profiles_transfer_history';
final List<ConfigProfile> _customProfiles = <ConfigProfile>[];
final List<ProfileTransferRecord> _transferHistory =
<ProfileTransferRecord>[];
final Map<String, String> _deviceProfileDefaults = <String, String>{};
bool _isInitialized = false;
bool _profilesEnabled = false;
String _activeProfileId = ConfigProfile.defaultProfileId;
@@ -37,6 +40,19 @@ class ProfileManager with ChangeNotifier {
_activeProfileId =
prefs.getString(activeProfileIdKey) ?? ConfigProfile.defaultProfileId;
final deviceDefaultsJson = prefs.getString(_deviceProfileDefaultsKey);
if (deviceDefaultsJson != null && deviceDefaultsJson.isNotEmpty) {
final decoded = jsonDecode(deviceDefaultsJson);
if (decoded is Map<String, dynamic>) {
_deviceProfileDefaults
..clear()
..addAll(
decoded.map((key, value) => MapEntry(key, value?.toString() ?? ''))
..removeWhere((key, value) => value.isEmpty),
);
}
}
final profilesJson = prefs.getString(_profilesKey);
if (profilesJson != null && profilesJson.isNotEmpty) {
final decoded = jsonDecode(profilesJson) as List<dynamic>;
@@ -90,9 +106,30 @@ class ProfileManager with ChangeNotifier {
}
Future<void> setActiveProfileId(String id) async {
await setActiveProfileIdForDevice(id);
}
String profileIdForDevice(String? deviceKey) {
if (deviceKey == null || deviceKey.isEmpty) {
return _activeProfileId;
}
return _deviceProfileDefaults[deviceKey] ?? ConfigProfile.defaultProfileId;
}
Future<void> setActiveProfileIdForDevice(
String id, {
String? deviceKey,
}) async {
_activeProfileId = id;
final prefs = await SharedPreferences.getInstance();
await prefs.setString(activeProfileIdKey, id);
if (deviceKey != null && deviceKey.isNotEmpty) {
_deviceProfileDefaults[deviceKey] = id;
await prefs.setString(
_deviceProfileDefaultsKey,
jsonEncode(_deviceProfileDefaults),
);
}
ProfileStorageScope.setScope(
profilesEnabled: _profilesEnabled,
activeProfileId: _activeProfileId,

View File

@@ -19,6 +19,7 @@ import 'contact_storage_service.dart';
import 'device_config_applicator.dart';
import 'message_storage_service.dart';
import 'profile_manager.dart';
import 'profile_device_key_resolver.dart';
import 'profiles_feature_service.dart';
import 'map_workspace_snapshot_service.dart';
@@ -63,6 +64,7 @@ class ProfileWorkspaceCoordinator {
final DeviceConfigApplicator _deviceConfigApplicator;
final MessageStorageService _messageStorageService;
final ContactStorageService _contactStorageService;
bool _isSyncingDeviceProfile = false;
Future<void> setProfilesEnabled(bool enabled) async {
final wasEnabled = profileManager.profilesEnabled;
@@ -77,6 +79,14 @@ class ProfileWorkspaceCoordinator {
activeProfileId: enabled ? profileManager.activeProfileId : 'default',
);
if (enabled) {
final deviceKey = _currentDeviceProfileKey;
final targetProfileId = profileManager.profileIdForDevice(deviceKey);
if (targetProfileId != profileManager.activeProfileId) {
await profileManager.setActiveProfileIdForDevice(
targetProfileId,
deviceKey: deviceKey,
);
}
if (wasEnabled) {
await openProfile(profileManager.activeProfileId);
} else {
@@ -181,9 +191,13 @@ class ProfileWorkspaceCoordinator {
}
Future<void> openProfile(String profileId) async {
final deviceKey = _currentDeviceProfileKey;
await _saveActiveCustomProfileSnapshot();
await connectionProvider.disconnect();
await profileManager.setActiveProfileId(profileId);
await profileManager.setActiveProfileIdForDevice(
profileId,
deviceKey: deviceKey,
);
await _switchRuntimeScope(profileId);
final profile = await resolveProfile(profileId);
@@ -271,6 +285,49 @@ class ProfileWorkspaceCoordinator {
return imported;
}
Future<void> syncActiveProfileForCurrentDevice() async {
if (!profileManager.profilesEnabled || _isSyncingDeviceProfile) {
return;
}
final deviceKey = _currentDeviceProfileKey;
if (deviceKey == null) {
return;
}
final targetProfileId = profileManager.profileIdForDevice(deviceKey);
if (targetProfileId == profileManager.activeProfileId) {
return;
}
_isSyncingDeviceProfile = true;
try {
await _persistCurrentState();
await profileManager.setActiveProfileIdForDevice(
targetProfileId,
deviceKey: deviceKey,
);
await _switchRuntimeScope(targetProfileId);
final profile = await resolveProfile(targetProfileId);
await _appConfigSnapshotService.apply(
profile.sections.appSettings,
appProvider,
);
await _mapWorkspaceSnapshotService.apply(
profile.sections.mapWorkspace,
mapProvider: mapProvider,
drawingProvider: drawingProvider,
);
} finally {
_isSyncingDeviceProfile = false;
}
}
String? get _currentDeviceProfileKey => ProfileDeviceKeyResolver.resolve(
deviceInfo: connectionProvider.deviceInfo,
connectionMode: connectionProvider.connectionMode,
);
Future<void> _switchRuntimeScope(String profileId) async {
final runtimeProfilesEnabled = profileManager.profilesEnabled;
ProfileStorageScope.setScope(