app mobile allarmi prima
This commit is contained in:
166
lib/services/api_service.dart
Normal file
166
lib/services/api_service.dart
Normal 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);
|
||||
}
|
||||
}
|
||||
76
lib/services/auth_service.dart
Normal file
76
lib/services/auth_service.dart
Normal 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');
|
||||
}
|
||||
}
|
||||
}
|
||||
186
lib/services/notification_service.dart
Normal file
186
lib/services/notification_service.dart
Normal 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();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user