diff --git a/android/app/build.gradle.kts b/android/app/build.gradle.kts index 018bbcb..5eff307 100644 --- a/android/app/build.gradle.kts +++ b/android/app/build.gradle.kts @@ -6,7 +6,7 @@ plugins { android { namespace = "com.slrpg.app" - compileSdk = 36 + compileSdk = 35 compileOptions { sourceCompatibility = JavaVersion.VERSION_17 @@ -21,7 +21,7 @@ android { defaultConfig { applicationId = "com.slrpg.app" minSdk = flutter.minSdkVersion - targetSdk = 36 + targetSdk = 35 versionCode = 1 versionName = "1.0.0" } diff --git a/lib/main.dart b/lib/main.dart index 883e0c0..b707544 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -23,23 +23,30 @@ void main() async { DeviceOrientation.portraitDown, ]); - final notificationService = NotificationService(); - await notificationService.init(); - final database = AppDatabase(); - final authStore = PbAuthStore(); await authStore.loadFromStorage(); + final container = ProviderContainer( + overrides: [ + appDatabaseProvider.overrideWithValue(database), + apiClientProvider + .overrideWith((ref) => ApiClient(authStore: authStore)), + ], + ); + + try { + log('Initializing NotificationService...'); + container.read(notificationServiceProvider).init(); + } catch (e) { + log('Error triggering NotificationService: $e'); + } + log("Auth loaded. Valid? ${authStore.isValid}"); runApp( - ProviderScope( - overrides: [ - appDatabaseProvider.overrideWithValue(database), - apiClientProvider - .overrideWith((ref) => ApiClient(authStore: authStore)), - ], + UncontrolledProviderScope( + container: container, child: const SLRPGApp(), ), ); diff --git a/lib/src/core/utils/error_handler.dart b/lib/src/core/utils/error_handler.dart index 715ee74..edd6e59 100644 --- a/lib/src/core/utils/error_handler.dart +++ b/lib/src/core/utils/error_handler.dart @@ -31,7 +31,8 @@ class ErrorHandler { return l10n.errorEntryNotUnique; } // PocketBase specific error for failed login - if (e.contains('Failed to authenticate') || e.contains('identity or password')) { + if (e.contains('Failed to authenticate') || + e.contains('identity or password')) { return l10n.errorAuthenticationFailed; } return l10n.errorIllegalRequest; @@ -43,15 +44,15 @@ class ErrorHandler { static void showErrorSnackBar(BuildContext context, Object error) { final scaffoldMessenger = ScaffoldMessenger.of(context); scaffoldMessenger.hideCurrentSnackBar(); - + scaffoldMessenger.showSnackBar( SnackBar( content: Text(getReadableError(context, error)), backgroundColor: AppTheme.errorColor, behavior: SnackBarBehavior.floating, duration: const Duration(seconds: 4), - dismissDirection: DismissDirection.horizontal, // Easier to swipe away - margin: const EdgeInsets.fromLTRB(16, 16, 16, 60), // Move it up to not block navigation + dismissDirection: DismissDirection.horizontal, + margin: const EdgeInsets.fromLTRB(16, 16, 16, 60), action: SnackBarAction( label: 'OK', textColor: Colors.white, diff --git a/lib/src/core/utils/notification_service.dart b/lib/src/core/utils/notification_service.dart index 776dde2..0587dfe 100644 --- a/lib/src/core/utils/notification_service.dart +++ b/lib/src/core/utils/notification_service.dart @@ -19,7 +19,7 @@ class NotificationService { tz.initializeTimeZones(); const AndroidInitializationSettings initializationSettingsAndroid = - AndroidInitializationSettings('@mipmap/ic_launcher'); + AndroidInitializationSettings('ic_launcher'); const DarwinInitializationSettings initializationSettingsDarwin = DarwinInitializationSettings( @@ -47,8 +47,17 @@ class NotificationService { final androidImplementation = _notifications.resolvePlatformSpecificImplementation< AndroidFlutterLocalNotificationsPlugin>(); - await androidImplementation?.requestNotificationsPermission(); - await androidImplementation?.requestExactAlarmsPermission(); + + if (androidImplementation != null) { + // Request notification permission (Android 13+) + await androidImplementation.requestNotificationsPermission(); + + // requestExactAlarmsPermission is typically for SCHEDULE_EXACT_ALARM. + // Since we use USE_EXACT_ALARM in the manifest for newer versions, + // we only request this if absolutely necessary or on older versions if supported. + // On Android 13+, USE_EXACT_ALARM is granted at install time. + await androidImplementation.requestExactAlarmsPermission(); + } } } diff --git a/lib/src/features/workout_runner/application/rest_timer_service.dart b/lib/src/features/workout_runner/application/rest_timer_service.dart index e6903a8..897d6fc 100644 --- a/lib/src/features/workout_runner/application/rest_timer_service.dart +++ b/lib/src/features/workout_runner/application/rest_timer_service.dart @@ -1,5 +1,7 @@ import 'dart:async'; import 'package:riverpod_annotation/riverpod_annotation.dart'; +import '../../../shared/data/repositories/user_repository.dart'; +import '../../../core/utils/notification_service.dart'; part 'rest_timer_service.g.dart'; @@ -75,12 +77,21 @@ class RestTimer extends _$RestTimer { ); } - void complete() { + void complete() async { cancel(); state = state.copyWith( isActive: false, remainingSeconds: 0, ); + + final userRepo = ref.read(userRepositoryProvider); + final user = await userRepo.getLocalUser(); + final settings = user?.notificationSettings ?? {}; + final restEnabled = settings['rest_finished_enabled'] ?? true; + + if (restEnabled) { + ref.read(notificationServiceProvider).showRestFinishedNotification(); + } } void cancel() {