Files
MobilApp/lib/services/device_identity_service.dart
T
2026-08-31 20:38:18 +02:00

235 lines
8.8 KiB
Dart

// Eszközazonosítás és eszközinformációk.
//
// Tárolás:
// FlutterSecureStorage → appInstanceId (UUID), deviceLabel
// MethodChannel → ANDROID_ID, rendszer eszköznév
// device_info_plus → gyártó, modell, OS verzió
// package_info_plus → app verzió
import 'dart:async';
import 'dart:io';
import 'package:device_info_plus/device_info_plus.dart';
import 'package:flutter/services.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:get/get.dart';
import 'package:package_info_plus/package_info_plus.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/services/app_database.dart';
import 'package:terepi_seged/services/app_logger.dart';
import 'package:terepi_seged/services/firebase_logger.dart';
import 'package:uuid/uuid.dart';
import '../models/device_info_model.dart';
class DeviceIdentityService extends GetxService {
static DeviceIdentityService get to => Get.find();
// ── Konstansok ────────────────────────────────────────────────────
static const _channel = MethodChannel('hu.app_dev.terepi_seged/deviceInfo');
static const _storage = FlutterSecureStorage(
aOptions: AndroidOptions(
encryptedSharedPreferences: true, // Android Keystore alapú titkosítás
),
iOptions: IOSOptions(
accessibility: KeychainAccessibility.first_unlock_this_device,
),
);
static const _keyInstanceId = 'device_app_instance_id';
static const _keyLabel = 'device_label';
// ── Publikus mezők ────────────────────────────────────────────────
/// Statikus eszközinformációk — egyszer töltődik be, nem változik.
late final DeviceInfoModel info;
/// Felhasználó által megadott eszköznév — reaktív, szerkeszthető.
/// Alapértelmezett: rendszer eszköznév (pl. "Pista telefonja").
final deviceLabel = ''.obs;
bool _isReady = false;
bool get isReady => _isReady;
// ── Lifecycle ─────────────────────────────────────────────────────
@override
Future<void> onReady() async {
super.onReady();
await _load();
// Háttérben regisztrálás — nem blokkolja az UI-t
_isReady = true;
unawaited(registerDevice());
unawaited(AppDatabase.instance.repairDeviceIds(info.appInstanceId));
FirebaseLogger.setDeviceIdentifier(info.deviceId, deviceLabel.value);
FirebaseLogger.setKey('model', model);
FirebaseLogger.setKey('os_version', osInfo);
FirebaseLogger.setKey('app_version', appInfo);
}
// ── Betöltés ──────────────────────────────────────────────────────
Future<void> _load() async {
// Párhuzamos lekérdezések az indulás gyorsításához
final results = await Future.wait([
_getOrCreateInstanceId(),
_getSystemDeviceName(),
_getStoredLabel(),
DeviceInfoPlugin().androidInfo,
PackageInfo.fromPlatform(),
]);
final instanceId = results[0] as String;
final systemName = results[1] as String;
final storedLabel = results[2] as String?;
final android = results[3] as AndroidDeviceInfo;
final pkg = results[4] as PackageInfo;
String platform = '';
if (Platform.isAndroid) {
platform = 'Android';
} else if (Platform.isIOS) {
platform = 'iOS';
} else if (Platform.isMacOS) {
platform = 'macOS';
}
// ANDROID_ID — MethodChannel-en keresztül (Settings.Secure.ANDROID_ID)
final androidId = await _getAndroidId() ?? android.fingerprint;
print('AndroidId: $androidId');
info = DeviceInfoModel(
deviceId: androidId,
appInstanceId: instanceId,
platform: platform,
manufacturer: android.manufacturer,
model: android.model,
brand: android.brand,
systemDeviceName: systemName,
osVersion: android.version.release,
sdkInt: android.version.sdkInt,
securityPatch: android.version.securityPatch ?? '',
appVersion: pkg.version,
buildNumber: pkg.buildNumber,
);
// Label: tárolt érték > rendszer neve
deviceLabel.value = storedLabel ?? systemName;
print('Device label: ${deviceLabel.value}');
print('DeviceId: ${info.deviceId}');
print('AppInstanceId: ${info.appInstanceId}');
print('SystemDeviceName: ${info.systemDeviceName}');
print('AppVersion: ${info.appVersion}');
print('buildNumber: ${info.buildNumber}');
print('model: ${info.model}');
}
// ── SecureStorage műveletek ───────────────────────────────────────
Future<String> _getOrCreateInstanceId() async {
final existing = await _storage.read(key: _keyInstanceId);
if (existing != null) return existing;
final newId = const Uuid().v4();
await _storage.write(key: _keyInstanceId, value: newId);
return newId;
}
Future<String?> _getStoredLabel() => _storage.read(key: _keyLabel);
// ── MethodChannel hívások ─────────────────────────────────────────
/// Settings.Secure.ANDROID_ID — egyedi, app-specifikus (Android 8+)
Future<String?> _getAndroidId() async {
try {
return await _channel.invokeMethod<String>('getAndroidId');
} catch (_) {
return null;
}
}
/// Settings.Global.DEVICE_NAME — felhasználó által adott eszköznév
Future<String> _getSystemDeviceName() async {
try {
final name = await _channel.invokeMethod<String>('getAndroidDeviceName');
if (name != null && name.isNotEmpty) return name;
} catch (_) {}
// Fallback: gyártó + modell
if (Platform.isAndroid) {
final a = await DeviceInfoPlugin().androidInfo;
return '${a.manufacturer} ${a.model}';
}
return 'Eszköz';
}
// ── Eszköznév beállítása ──────────────────────────────────────────
/// Felhasználó által megadott eszköznév mentése.
/// Üres string esetén visszaáll a rendszer névhez.
Future<void> setLabel(String label) async {
final trimmed = label.trim();
if (trimmed.isEmpty) {
// Visszaállás rendszer névre
await _storage.delete(key: _keyLabel);
deviceLabel.value = info.systemDeviceName;
} else {
await _storage.write(key: _keyLabel, value: trimmed);
deviceLabel.value = trimmed;
}
unawaited(registerDevice());
}
// ── Supabase regisztráció ─────────────────────────────────────────
Future<void> registerDevice() async {
final user = Supabase.instance.client.auth.currentUser;
if (user == null || deviceId.isEmpty) return;
try {
await Supabase.instance.client.from('terepi_seged_devices').upsert({
'id': appInstanceId,
'user_id': user.id,
'name': info.systemDeviceName,
'device_id': deviceId,
'platform': info.platform,
'model': model,
'os_version': info.osVersion,
'app_version': appInfo,
'last_seen_at': DateTime.now().toUtc().toIso8601String()
});
} catch (e) {
AppLogger.e('DeviceIdentityService.registerDevice',
'Eszköz-regisztráció hiba (user=${user.id} - device=$deviceId): $e');
}
}
// ── Gyors elérők (kényelemért) ────────────────────────────────────
String get deviceId => _isReady ? info.deviceId : '';
String get appInstanceId =>
_isReady ? info.appInstanceId : ''; // ← _isReady guard hiányzott
String get deviceLabelSync => deviceLabel.value; // ← ÚJ
String get model => _isReady ? '${info.manufacturer} ${info.model}' : '';
String get osInfo =>
_isReady ? 'Android ${info.osVersion} (SDK ${info.sdkInt})' : '';
String get appInfo =>
_isReady ? '${info.appVersion}+${info.buildNumber}' : '';
// ── Debug ─────────────────────────────────────────────────────────
@override
String toString() => [
'DeviceIdentityService',
' deviceId: ${info.deviceId}',
' instanceId: ${info.appInstanceId}',
' label: ${deviceLabel.value}',
' model: $model',
' os: $osInfo',
' app: $appInfo',
].join('\n');
}