Files
MobilApp/lib/services/nav_autostart_service.dart
T

102 lines
3.7 KiB
Dart
Raw Normal View History

import 'dart:developer';
import 'package:flutter/services.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:supabase_flutter/supabase_flutter.dart';
import 'package:terepi_seged/models/project.dart';
import 'package:terepi_seged/services/app_database.dart';
import 'package:terepi_seged/services/app_logger.dart';
/// Induláskor eldönti, hogy a navigációs képernyőt is meg kell-e nyitni.
///
/// Mind a négy feltételnek teljesülnie kell:
/// 1. A felhasználó be van jelentkezve (a munkamenet a készüléken tárolt,
/// ez offline is eldönthető).
/// 2. Az utoljára aktív projekt FELHŐS (nem csak helyi), aktív, és már
/// szinkronizált (sync_status == 'synced').
/// 3. A szerver elérhető — ezt maga a lekérdezés bizonyítja (offline hibára
/// fut).
/// 4. A `terepi_seged_app_config` táblában megtalálható a
/// `nav_autostart:<ANDROID_ID>` kulcs. Az érték tetszőleges (pl. 'igen'),
/// csak a kulcs megléte számít. A járművet (V1, V2…) NEM állítja be, az
/// marad a helyi érték.
///
/// OFFLINE-FIRST: bármilyen hiba, időtúllépés vagy hiányzó adat esetén a
/// válasz `false`, az app a megszokott módon indul. Sosem dob kivételt.
class NavAutoStartService {
NavAutoStartService._();
static const keyPrefix = 'nav_autostart:';
static const _channel = MethodChannel('hu.app_dev.terepi_seged/deviceInfo');
static bool _no(String reason) {
// ignore: avoid_print
print('🚀 NavAutoStart: NEM — $reason');
return false;
}
static Future<bool> shouldStartNavigation() async {
try {
final androidId = await _androidId();
if (androidId == null || androidId.isEmpty) {
return _no('nincs ANDROID_ID');
}
// ignore: avoid_print
print('🚀 NavAutoStart: androidId=$androidId');
if (Supabase.instance.client.auth.currentUser == null) {
return _no('nincs bejelentkezett felhasználó');
}
if (!await _activeProjectIsOnline()) return false;
final row = await Supabase.instance.client
.from('terepi_seged_app_config')
.select('key')
.eq('key', '$keyPrefix$androidId')
.maybeSingle()
.timeout(const Duration(seconds: 3));
if (row == null) {
return _no('nincs sor a kulccsal: $keyPrefix$androidId');
}
AppLogger.event('nav_autostart');
// ignore: avoid_print
print('🚀 NavAutoStart: IGEN');
return true;
} catch (e) {
return _no('hiba / időtúllépés: $e');
}
}
/// Az utoljára aktív projektet a ProjectService is a SharedPreferences-ből
/// állítja vissza, ezért itt közvetlenül azt vizsgáljuk — így nem függ a
/// ProjectService aszinkron indulásának időzítésétől.
static Future<bool> _activeProjectIsOnline() async {
final prefs = await SharedPreferences.getInstance();
final id = prefs.getInt('active_project_id');
if (id == null) return _no('nincs mentett aktív projekt');
final project = await AppDatabase.instance.getProject(id);
if (project == null) return _no('a projekt nincs a helyi adatbázisban');
if (project.isLocalOnly) return _no('az aktív projekt csak helyi');
if (project.status != ProjectStatus.active) {
return _no('a projekt nem aktív (${project.status})');
}
final sync = await AppDatabase.instance.getProjectSyncStatus(id);
if (sync != 'synced') return _no('sync_status=$sync (nem synced)');
return true;
}
/// Settings.Secure.ANDROID_ID — ugyanaz a csatorna, mint amit a
/// DeviceIdentityService használ.
static Future<String?> _androidId() async {
try {
return await _channel.invokeMethod<String>('getAndroidId');
} catch (_) {
return null;
}
}
}