fix(notification): harden local alerts
This commit is contained in:
@@ -117,11 +117,15 @@ class NotificationService {
|
||||
enableVibration: true,
|
||||
);
|
||||
|
||||
await _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.createNotificationChannel(androidChannel);
|
||||
try {
|
||||
await _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>()
|
||||
?.createNotificationChannel(androidChannel);
|
||||
} catch (e) {
|
||||
AppLogger.debug('안드로이드 채널 생성 실패: $e');
|
||||
}
|
||||
}
|
||||
|
||||
/// 알림 권한 요청
|
||||
@@ -214,6 +218,46 @@ class NotificationService {
|
||||
return true;
|
||||
}
|
||||
|
||||
/// 정확 알람 권한 가능 여부 확인 (Android 12+)
|
||||
Future<bool> canScheduleExactAlarms() async {
|
||||
if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
final androidImplementation = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
final canExact = await androidImplementation
|
||||
?.canScheduleExactNotifications();
|
||||
return canExact ?? true;
|
||||
} catch (e) {
|
||||
AppLogger.debug('정확 알람 권한 확인 실패: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// 정확 알람 권한 요청 (Android 12+ 설정 화면 이동)
|
||||
Future<bool> requestExactAlarmsPermission() async {
|
||||
if (kIsWeb || defaultTargetPlatform != TargetPlatform.android) {
|
||||
return true;
|
||||
}
|
||||
|
||||
try {
|
||||
final androidImplementation = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
final granted = await androidImplementation
|
||||
?.requestExactAlarmsPermission();
|
||||
return granted ?? false;
|
||||
} catch (e) {
|
||||
AppLogger.debug('정확 알람 권한 요청 실패: $e');
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 알림 탭 콜백
|
||||
static void Function(NotificationResponse)? onNotificationTap;
|
||||
|
||||
@@ -238,7 +282,7 @@ class NotificationService {
|
||||
}
|
||||
|
||||
final location = await _ensureLocalTimezone();
|
||||
final minutesToWait = delayMinutes ?? 90 + Random().nextInt(31);
|
||||
final minutesToWait = max(delayMinutes ?? 90 + Random().nextInt(31), 1);
|
||||
final scheduledTime = tz.TZDateTime.now(
|
||||
location,
|
||||
).add(Duration(minutes: minutesToWait));
|
||||
@@ -343,17 +387,9 @@ class NotificationService {
|
||||
return AndroidScheduleMode.exactAllowWhileIdle;
|
||||
}
|
||||
|
||||
try {
|
||||
final androidImplementation = _notifications
|
||||
.resolvePlatformSpecificImplementation<
|
||||
AndroidFlutterLocalNotificationsPlugin
|
||||
>();
|
||||
final canExact = await androidImplementation
|
||||
?.canScheduleExactNotifications();
|
||||
if (canExact == false) {
|
||||
return AndroidScheduleMode.inexactAllowWhileIdle;
|
||||
}
|
||||
} catch (_) {
|
||||
final canExact = await canScheduleExactAlarms();
|
||||
if (!canExact) {
|
||||
AppLogger.debug('정확 알람 권한 없음 → 근사 모드로 예약');
|
||||
return AndroidScheduleMode.inexactAllowWhileIdle;
|
||||
}
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ import 'package:permission_handler/permission_handler.dart';
|
||||
import '../../../core/constants/app_colors.dart';
|
||||
import '../../../core/constants/app_typography.dart';
|
||||
import '../../providers/settings_provider.dart';
|
||||
import '../../providers/notification_provider.dart';
|
||||
|
||||
class SettingsScreen extends ConsumerStatefulWidget {
|
||||
const SettingsScreen({super.key});
|
||||
@@ -174,6 +175,29 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
);
|
||||
},
|
||||
),
|
||||
if (!kIsWeb && defaultTargetPlatform == TargetPlatform.android)
|
||||
FutureBuilder<bool>(
|
||||
future: ref
|
||||
.read(notificationServiceProvider)
|
||||
.canScheduleExactAlarms(),
|
||||
builder: (context, snapshot) {
|
||||
final canExact = snapshot.data;
|
||||
|
||||
// 권한이 이미 허용된 경우 UI 생략
|
||||
if (canExact == true) {
|
||||
return const SizedBox.shrink();
|
||||
}
|
||||
|
||||
return _buildPermissionTile(
|
||||
icon: Icons.alarm,
|
||||
title: '정확 알람 권한',
|
||||
subtitle: '정확한 예약 알림을 위해 필요합니다',
|
||||
isGranted: canExact ?? false,
|
||||
onRequest: _requestExactAlarmPermission,
|
||||
isDark: isDark,
|
||||
);
|
||||
},
|
||||
),
|
||||
], isDark),
|
||||
|
||||
// 알림 설정
|
||||
@@ -407,6 +431,19 @@ class _SettingsScreenState extends ConsumerState<SettingsScreen> {
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> _requestExactAlarmPermission() async {
|
||||
final notificationService = ref.read(notificationServiceProvider);
|
||||
final granted = await notificationService.requestExactAlarmsPermission();
|
||||
|
||||
if (!mounted) return;
|
||||
|
||||
setState(() {});
|
||||
|
||||
if (!granted) {
|
||||
_showPermissionDialog('정확 알람');
|
||||
}
|
||||
}
|
||||
|
||||
void _showPermissionDialog(String permissionName) {
|
||||
final isDark = Theme.of(context).brightness == Brightness.dark;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user