// 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: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 onReady() async { super.onReady(); await _load(); // Háttérben regisztrálás — nem blokkolja az UI-t unawaited(_registerDevice()); _isReady = true; FirebaseLogger.setDeviceIdentifier(info.deviceId, deviceLabel.value); FirebaseLogger.setKey('model', model); FirebaseLogger.setKey('os_version', osInfo); FirebaseLogger.setKey('app_version', appInfo); } // ── Betöltés ────────────────────────────────────────────────────── Future _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; // 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, 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}'); } // ── SecureStorage műveletek ─────────────────────────────────────── Future _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 _getStoredLabel() => _storage.read(key: _keyLabel); // ── MethodChannel hívások ───────────────────────────────────────── /// Settings.Secure.ANDROID_ID — egyedi, app-specifikus (Android 8+) Future _getAndroidId() async { try { return await _channel.invokeMethod('getAndroidId'); } catch (_) { return null; } } /// Settings.Global.DEVICE_NAME — felhasználó által adott eszköznév Future _getSystemDeviceName() async { try { final name = await _channel.invokeMethod('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 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 _registerDevice() async { // await Supabase.instance.client // .from('devices') // .upsert( // info.toRegistrationMap(label: deviceLabel.value), // onConflict: 'device_id', // ); } // ── 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'); }