app mobile allarmi prima

This commit is contained in:
2025-10-20 19:17:45 +02:00
commit 300912ee02
159 changed files with 11755 additions and 0 deletions

View File

@@ -0,0 +1,166 @@
import 'dart:convert';
import 'package:http/http.dart' as http;
import '../utils/constants.dart';
import '../models/user.dart';
import '../models/allarme.dart';
import '../models/sito.dart';
import '../models/statistiche.dart';
class ApiService {
static final ApiService _instance = ApiService._internal();
factory ApiService() => _instance;
ApiService._internal();
String? _authToken;
void setAuthToken(String token) => _authToken = token;
void clearAuthToken() => _authToken = null;
Map<String, String> _getHeaders({bool includeAuth = true}) {
final headers = {
'Content-Type': 'application/json',
'Accept': 'application/json',
};
if (includeAuth && _authToken != null) {
headers['Authorization'] = 'Bearer $_authToken';
}
return headers;
}
Future<Map<String, dynamic>> _handleResponse(http.Response response) async {
if (response.statusCode >= 200 && response.statusCode < 300) {
return json.decode(utf8.decode(response.bodyBytes));
} else {
final error = json.decode(utf8.decode(response.bodyBytes));
throw Exception(error['detail'] ?? 'Errore sconosciuto');
}
}
Future<Map<String, dynamic>> login(String email, String password) async {
final response = await http.post(
Uri.parse('${AppConstants.apiBaseUrl}/auth/login'),
headers: _getHeaders(includeAuth: false),
body: json.encode({'email': email, 'password': password}),
);
return _handleResponse(response);
}
Future<User> getCurrentUser() async {
final response = await http.get(
Uri.parse('${AppConstants.apiBaseUrl}/auth/me'),
headers: _getHeaders(),
);
final data = await _handleResponse(response);
return User.fromJson(data);
}
Future<void> registerFcmToken(String fcmToken) async {
final response = await http.post(
Uri.parse('${AppConstants.apiBaseUrl}/auth/register-fcm-token'),
headers: _getHeaders(),
body: json.encode({'fcm_token': fcmToken}),
);
await _handleResponse(response);
}
Future<AllarmeListResponse> getAllarmi({
int page = 1,
int pageSize = 20,
String? severita,
String? stato,
}) async {
final queryParams = {
'page': page.toString(),
'page_size': pageSize.toString(),
if (severita != null) 'severita': severita,
if (stato != null) 'stato': stato,
};
final uri = Uri.parse('${AppConstants.apiBaseUrl}/allarmi')
.replace(queryParameters: queryParams);
final response = await http.get(uri, headers: _getHeaders());
final data = await _handleResponse(response);
return AllarmeListResponse.fromJson(data);
}
Future<Allarme> getAllarme(int id) async {
final response = await http.get(
Uri.parse('${AppConstants.apiBaseUrl}/allarmi/$id'),
headers: _getHeaders(),
);
final data = await _handleResponse(response);
return Allarme.fromJson(data);
}
Future<Allarme> updateAllarme(int id, {String? stato}) async {
final body = <String, dynamic>{};
if (stato != null) body['stato'] = stato;
final response = await http.patch(
Uri.parse('${AppConstants.apiBaseUrl}/allarmi/$id'),
headers: _getHeaders(),
body: json.encode(body),
);
final data = await _handleResponse(response);
return Allarme.fromJson(data);
}
Future<Sito> getSito(int sitoId) async {
final response = await http.get(
Uri.parse('${AppConstants.apiBaseUrl}/siti/$sitoId'),
headers: _getHeaders(),
);
final data = await _handleResponse(response);
return Sito.fromJson(data);
}
Future<List<Sito>> getSiti({String? tipo}) async {
final queryParams = <String, String>{};
if (tipo != null) queryParams['tipo'] = tipo;
final uri = Uri.parse('${AppConstants.apiBaseUrl}/siti')
.replace(queryParameters: queryParams.isNotEmpty ? queryParams : null);
final response = await http.get(uri, headers: _getHeaders());
final data = await _handleResponse(response);
final items = (data['items'] as List)
.map((item) => Sito.fromJson(item as Map<String, dynamic>))
.toList();
return items;
}
Future<AllarmeListResponse> getAllarmiBySito(int sitoId, {int page = 1, int pageSize = 20}) async {
final queryParams = {
'page': page.toString(),
'page_size': pageSize.toString(),
};
final uri = Uri.parse('${AppConstants.apiBaseUrl}/allarmi/sito/$sitoId')
.replace(queryParameters: queryParams);
final response = await http.get(uri, headers: _getHeaders());
final data = await _handleResponse(response);
return AllarmeListResponse.fromJson(data);
}
Future<Statistiche> getStatistiche() async {
final response = await http.get(
Uri.parse('${AppConstants.apiBaseUrl}/statistiche'),
headers: _getHeaders(),
);
final data = await _handleResponse(response);
return Statistiche.fromJson(data);
}
Future<AllarmiPerGiornoResponse> getAllarmiPerGiorno({int giorni = 30}) async {
final uri = Uri.parse('${AppConstants.apiBaseUrl}/statistiche/allarmi-per-giorno')
.replace(queryParameters: {'giorni': giorni.toString()});
final response = await http.get(uri, headers: _getHeaders());
final data = await _handleResponse(response);
return AllarmiPerGiornoResponse.fromJson(data);
}
}

View File

@@ -0,0 +1,76 @@
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'dart:convert';
import '../models/user.dart';
import '../utils/constants.dart';
import 'api_service.dart';
class AuthService {
static final AuthService _instance = AuthService._internal();
factory AuthService() => _instance;
AuthService._internal();
final _storage = const FlutterSecureStorage();
final _apiService = ApiService();
User? _currentUser;
User? get currentUser => _currentUser;
bool get isAuthenticated => _currentUser != null;
Future<bool> login(String email, String password) async {
try {
final response = await _apiService.login(email, password);
final token = response['access_token'] as String;
await _storage.write(key: AppConstants.storageKeyToken, value: token);
_apiService.setAuthToken(token);
_currentUser = await _apiService.getCurrentUser();
final prefs = await SharedPreferences.getInstance();
await prefs.setString(
AppConstants.storageKeyUser,
json.encode(_currentUser!.toJson()),
);
return true;
} catch (e) {
print('Login error: $e');
return false;
}
}
Future<bool> autoLogin() async {
try {
final token = await _storage.read(key: AppConstants.storageKeyToken);
if (token == null) return false;
_apiService.setAuthToken(token);
_currentUser = await _apiService.getCurrentUser();
return true;
} catch (e) {
print('Auto login error: $e');
await logout();
return false;
}
}
Future<void> logout() async {
_currentUser = null;
_apiService.clearAuthToken();
await _storage.delete(key: AppConstants.storageKeyToken);
final prefs = await SharedPreferences.getInstance();
await prefs.remove(AppConstants.storageKeyUser);
await prefs.remove(AppConstants.storageKeyFcmToken);
}
Future<void> saveFcmToken(String fcmToken) async {
try {
await _apiService.registerFcmToken(fcmToken);
final prefs = await SharedPreferences.getInstance();
await prefs.setString(AppConstants.storageKeyFcmToken, fcmToken);
} catch (e) {
print('Error saving FCM token: $e');
}
}
}

View File

@@ -0,0 +1,186 @@
import 'dart:async';
import 'dart:convert';
import 'package:firebase_core/firebase_core.dart';
import 'package:firebase_messaging/firebase_messaging.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'auth_service.dart';
@pragma('vm:entry-point')
Future<void> _firebaseMessagingBackgroundHandler(RemoteMessage message) async {
await Firebase.initializeApp();
print('Background message: ${message.messageId}');
}
class NotificationService {
static final NotificationService _instance = NotificationService._internal();
factory NotificationService() => _instance;
NotificationService._internal();
final FirebaseMessaging _firebaseMessaging = FirebaseMessaging.instance;
final FlutterLocalNotificationsPlugin _localNotifications =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
// Stream per gestire i tap sulle notifiche
final StreamController<RemoteMessage> _messageStreamController =
StreamController<RemoteMessage>.broadcast();
Stream<RemoteMessage> get onMessageTap => _messageStreamController.stream;
Future<void> initialize() async {
if (_initialized) return;
try {
await _requestPermissions();
await _initializeLocalNotifications();
_configureHandlers();
await _getFcmToken();
_initialized = true;
print('NotificationService initialized');
} catch (e) {
print('Error initializing notifications: $e');
}
}
Future<void> _requestPermissions() async {
final settings = await _firebaseMessaging.requestPermission(
alert: true,
badge: true,
sound: true,
);
print('Notification permission: ${settings.authorizationStatus}');
}
Future<void> _initializeLocalNotifications() async {
const androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher');
const iosSettings = DarwinInitializationSettings();
await _localNotifications.initialize(
const InitializationSettings(android: androidSettings, iOS: iosSettings),
onDidReceiveNotificationResponse: _onNotificationTapped,
);
const channel = AndroidNotificationChannel(
'allarmi_channel',
'Allarmi',
description: 'Notifiche per allarmi del sistema di monitoraggio',
importance: Importance.high,
playSound: true,
enableVibration: true,
);
await _localNotifications
.resolvePlatformSpecificImplementation<
AndroidFlutterLocalNotificationsPlugin>()
?.createNotificationChannel(channel);
}
void _onNotificationTapped(NotificationResponse response) {
print('Notifica tappata: ${response.payload}');
if (response.payload != null && response.payload!.isNotEmpty) {
try {
final data = jsonDecode(response.payload!);
// Crea un RemoteMessage mock per compatibilità
final message = RemoteMessage(
data: data,
messageId: DateTime.now().toString(),
);
_messageStreamController.add(message);
} catch (e) {
print('Errore parsing payload: $e');
}
}
}
void _configureHandlers() {
FirebaseMessaging.onBackgroundMessage(_firebaseMessagingBackgroundHandler);
FirebaseMessaging.onMessage.listen(_handleForegroundMessage);
// Handler quando app aperta da notifica (background/terminated)
FirebaseMessaging.onMessageOpenedApp.listen((message) {
print('Notifica aperta (background): ${message.messageId}');
_messageStreamController.add(message);
});
// Controlla se app avviata da notifica
_firebaseMessaging.getInitialMessage().then((message) {
if (message != null) {
print('App avviata da notifica: ${message.messageId}');
_messageStreamController.add(message);
}
});
}
Future<void> _getFcmToken() async {
try {
final token = await _firebaseMessaging.getToken();
if (token != null) {
print('FCM Token: $token');
await AuthService().saveFcmToken(token);
}
_firebaseMessaging.onTokenRefresh.listen((newToken) {
AuthService().saveFcmToken(newToken);
});
} catch (e) {
print('Error getting FCM token: $e');
}
}
Future<void> _handleForegroundMessage(RemoteMessage message) async {
print('Foreground message: ${message.messageId}');
final notification = message.notification;
if (notification != null) {
await _showLocalNotification(
title: notification.title ?? 'ASE Monitor',
body: notification.body ?? 'Nuovo allarme',
payload: jsonEncode(message.data),
);
}
}
Future<void> _showLocalNotification({
required String title,
required String body,
String? payload,
}) async {
const androidDetails = AndroidNotificationDetails(
'allarmi_channel',
'Allarmi',
channelDescription: 'Notifiche per allarmi del sistema di monitoraggio',
importance: Importance.high,
priority: Priority.high,
playSound: true,
enableVibration: true,
icon: '@mipmap/ic_launcher',
);
const iosDetails = DarwinNotificationDetails(
presentAlert: true,
presentBadge: true,
presentSound: true,
);
await _localNotifications.show(
DateTime.now().millisecondsSinceEpoch ~/ 1000,
title,
body,
const NotificationDetails(android: androidDetails, iOS: iosDetails),
payload: payload,
);
}
Future<String?> getFcmToken() async {
try {
return await _firebaseMessaging.getToken();
} catch (e) {
print('Errore ottenimento FCM token: $e');
return null;
}
}
void dispose() {
_messageStreamController.close();
}
}