This commit is contained in:
ytc1012
2025-11-26 16:32:47 +08:00
parent 96658339e1
commit 0195cdf54b
18 changed files with 1052 additions and 240 deletions

View File

@@ -1,3 +1,4 @@
import 'dart:async';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter/foundation.dart';
import 'package:permission_handler/permission_handler.dart';
@@ -9,10 +10,21 @@ class NotificationService {
factory NotificationService() => _instance;
NotificationService._internal();
final FlutterLocalNotificationsPlugin _notifications =
final FlutterLocalNotificationsPlugin _notifications =
FlutterLocalNotificationsPlugin();
bool _initialized = false;
/// Stream controller for permission status changes
final StreamController<bool> _permissionStatusController = StreamController<bool>.broadcast();
/// Get the permission status stream
Stream<bool> get permissionStatusStream => _permissionStatusController.stream;
/// Dispose the stream controller
void dispose() {
_permissionStatusController.close();
}
/// Initialize notification service
Future<void> initialize() async {
@@ -28,7 +40,9 @@ class NotificationService {
try {
// Android initialization settings
const androidSettings = AndroidInitializationSettings('@drawable/ic_notification');
const androidSettings = AndroidInitializationSettings(
'@drawable/ic_notification',
);
// iOS initialization settings
const iosSettings = DarwinInitializationSettings(
@@ -48,6 +62,13 @@ class NotificationService {
);
_initialized = true;
// Start listening for permission changes
await listenForPermissionChanges();
// Check initial permission status
await hasPermission();
if (kDebugMode) {
print('Notification service initialized successfully');
}
@@ -63,7 +84,6 @@ class NotificationService {
if (kDebugMode) {
print('Notification tapped: ${response.payload}');
}
// TODO: Navigate to appropriate screen if needed
}
/// Request notification permissions (iOS and Android 13+)
@@ -71,39 +91,43 @@ class NotificationService {
if (kIsWeb) return false;
try {
bool isGranted = false;
// Check if we're on Android or iOS
if (Platform.isAndroid) {
// Android 13+ requires runtime permission
final status = await Permission.notification.request();
isGranted = status.isGranted;
if (kDebugMode) {
print('Android notification permission status: $status');
}
return status.isGranted;
} else if (Platform.isIOS) {
// iOS permission request
final result = await _notifications
.resolvePlatformSpecificImplementation<
IOSFlutterLocalNotificationsPlugin>()
?.requestPermissions(
alert: true,
badge: true,
sound: true,
);
if (kDebugMode) {
print('iOS notification permission result: $result');
final iosImplementation = _notifications.resolvePlatformSpecificImplementation<IOSFlutterLocalNotificationsPlugin>();
if (iosImplementation != null) {
final result = await iosImplementation.requestPermissions(alert: true, badge: true, sound: true);
isGranted = result ?? false;
if (kDebugMode) {
print('iOS notification permission result: $result');
}
} else {
isGranted = true; // Assume granted if we can't request
}
return result ?? false;
} else {
isGranted = true; // Assume granted for other platforms
}
return true; // Other platforms
// Update the permission status stream
_permissionStatusController.add(isGranted);
return isGranted;
} catch (e) {
if (kDebugMode) {
print('Failed to request permissions: $e');
}
_permissionStatusController.add(false);
return false;
}
}
@@ -113,21 +137,39 @@ class NotificationService {
if (kIsWeb) return false;
try {
bool isGranted = false;
if (Platform.isAndroid) {
final status = await Permission.notification.status;
return status.isGranted;
isGranted = status.isGranted;
} else if (Platform.isIOS) {
// For iOS, we can't easily check without requesting, so we assume granted after request
return true;
// For iOS, we assume granted after initial request
isGranted = true;
} else {
isGranted = true; // Assume granted for other platforms
}
return true;
// Update the permission status stream
_permissionStatusController.add(isGranted);
return isGranted;
} catch (e) {
if (kDebugMode) {
print('Failed to check permission status: $e');
}
_permissionStatusController.add(false);
return false;
}
}
/// Listen for permission status changes
Future<void> listenForPermissionChanges() async {
// Permission status changes listening is not supported in current permission_handler version
// This method is kept for future implementation
if (kDebugMode) {
print('Permission status changes listening is not supported');
}
}
/// Show focus session completed notification
Future<void> showFocusCompletedNotification({
@@ -163,7 +205,8 @@ class NotificationService {
// Use provided title/body or fall back to English
final notificationTitle = title ?? '🎉 Focus session complete!';
final notificationBody = body ??
final notificationBody =
body ??
(distractionCount == 0
? 'You focused for $minutes ${minutes == 1 ? 'minute' : 'minutes'} without distractions!'
: 'You focused for $minutes ${minutes == 1 ? 'minute' : 'minutes'}. Great effort!');
@@ -187,9 +230,7 @@ class NotificationService {
}
/// Show reminder notification (optional feature for future)
Future<void> showReminderNotification({
required String message,
}) async {
Future<void> showReminderNotification({required String message}) async {
if (kIsWeb || !_initialized) return;
try {
@@ -261,7 +302,8 @@ class NotificationService {
try {
// Format time display for fallback
final timeStr = '${remainingMinutes.toString().padLeft(2, '0')}:${(remainingSeconds % 60).toString().padLeft(2, '0')}';
final timeStr =
'${remainingMinutes.toString().padLeft(2, '0')}:${(remainingSeconds % 60).toString().padLeft(2, '0')}';
const androidDetails = AndroidNotificationDetails(
'focus_timer',