MobilApp/lib/services/device_identity_service.dart

203 lines
7.6 KiB
Dart
Raw Permalink Normal View History

2026-06-23 15:21:20 +02:00
// 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';
2026-06-23 15:21:20 +02:00
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
unawaited(_registerDevice());
_isReady = true;
FirebaseLogger.setDeviceIdentifier(info.deviceId, deviceLabel.value);
FirebaseLogger.setKey('model', model);
FirebaseLogger.setKey('os_version', osInfo);
FirebaseLogger.setKey('app_version', appInfo);
2026-06-23 15:21:20 +02:00
}
// ── 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;
// 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<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 {
// 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');
}