import 'dart:convert'; import 'package:shared_preferences/shared_preferences.dart'; class SavedDevice { const SavedDevice({ required this.ip, required this.port, required this.name, required this.lastUsedAt, }); final String ip; final int port; final String name; final DateTime lastUsedAt; String get address => '$ip:$port'; Map toJson() { return { 'ip': ip, 'port': port, 'name': name, 'lastUsedAt': lastUsedAt.toIso8601String(), }; } factory SavedDevice.fromJson(Map json) { return SavedDevice( ip: _normalizeIp(json['ip']?.toString()), port: _normalizePort(json['port']), name: _normalizeName(json['name']?.toString()), lastUsedAt: DateTime.tryParse(json['lastUsedAt']?.toString() ?? '') ?? DateTime.fromMillisecondsSinceEpoch(0), ); } static String _normalizeIp(String? value) { final normalized = value?.trim() ?? ''; return normalized.isEmpty ? '127.0.0.1' : normalized; } static int _normalizePort(dynamic value) { final port = int.tryParse(value?.toString() ?? ''); if (port == null || port <= 0 || port > 65535) { return 5000; } return port; } static String _normalizeName(String? value) { final normalized = value?.trim() ?? ''; return normalized.isEmpty ? 'Showen' : normalized; } } class DeviceStorageService { static const String _devicesKey = 'showen_device_list'; static const int _maxDevices = 10; SharedPreferences? _preferences; Future saveDevice(String ip, int port, String? name) async { final prefs = await _getPreferences(); final devices = await getDevices(); final normalizedIp = _normalizeIp(ip); final normalizedPort = _normalizePort(port); final normalizedName = _normalizeName(name); final now = DateTime.now(); final nextDevices = [ SavedDevice( ip: normalizedIp, port: normalizedPort, name: normalizedName, lastUsedAt: now, ), ...devices.where( (device) => !(device.ip == normalizedIp && device.port == normalizedPort), ), ] ..sort((a, b) => b.lastUsedAt.compareTo(a.lastUsedAt)); await prefs.setString( _devicesKey, jsonEncode( nextDevices .take(_maxDevices) .map((device) => device.toJson()) .toList(growable: false), ), ); } Future> getDevices() async { final prefs = await _getPreferences(); final raw = prefs.getString(_devicesKey); if (raw == null || raw.isEmpty) { return const []; } try { final decoded = jsonDecode(raw); if (decoded is! List) { return const []; } final devices = decoded .whereType() .map((item) => SavedDevice.fromJson(Map.from(item))) .toList(growable: false) ..sort((a, b) => b.lastUsedAt.compareTo(a.lastUsedAt)); return devices.take(_maxDevices).toList(growable: false); } on FormatException { return const []; } } Future removeDevice(String ip, [int? port]) async { final prefs = await _getPreferences(); final normalizedIp = _normalizeIp(ip); final nextDevices = (await getDevices()) .where( (device) => device.ip != normalizedIp || (port != null && device.port != _normalizePort(port)), ) .map((device) => device.toJson()) .toList(growable: false); await prefs.setString(_devicesKey, jsonEncode(nextDevices)); } Future getLastDevice() async { final devices = await getDevices(); if (devices.isEmpty) { return null; } return devices.first; } Future _getPreferences() async { return _preferences ??= await SharedPreferences.getInstance(); } String _normalizeIp(String value) => SavedDevice._normalizeIp(value); int _normalizePort(dynamic value) => SavedDevice._normalizePort(value); String _normalizeName(String? value) => SavedDevice._normalizeName(value); }