From 9a70b9f002b20a7eb2a8421bfebd507bab9b843d Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Fri, 7 Jun 2024 13:40:09 +0300 Subject: [PATCH 01/16] wip background service --- android/app/src/main/AndroidManifest.xml | 3 +++ ios/Runner/Info.plist | 4 ++++ lib/domain/background_service_controller.dart | 14 +++++++++++ .../background_service_repository.dart | 23 +++++++++++++++++++ lib/main.dart | 2 ++ pubspec.yaml | 1 + 6 files changed, 47 insertions(+) create mode 100644 lib/domain/background_service_controller.dart create mode 100644 lib/infrastructure/background_service_repository.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 1ac7cf4..6f70915 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,9 @@ + + + UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight + BGTaskSchedulerPermittedIdentifiers + + dev.flutter.background.refresh + diff --git a/lib/domain/background_service_controller.dart b/lib/domain/background_service_controller.dart new file mode 100644 index 0000000..31203ad --- /dev/null +++ b/lib/domain/background_service_controller.dart @@ -0,0 +1,14 @@ +import 'dart:io'; + +import 'package:flutter_background_service/flutter_background_service.dart'; + +part 'package:infinite_horizons/infrastructure/background_service_repository.dart'; + +abstract class BackgroundServiceController { + static BackgroundServiceController? _instance; + + static BackgroundServiceController get instance => + _instance ??= _BackgroundServiceRepository(); + + Future init(); +} diff --git a/lib/infrastructure/background_service_repository.dart b/lib/infrastructure/background_service_repository.dart new file mode 100644 index 0000000..083fbb7 --- /dev/null +++ b/lib/infrastructure/background_service_repository.dart @@ -0,0 +1,23 @@ +part of 'package:infinite_horizons/domain/background_service_controller.dart'; + +class _BackgroundServiceRepository extends BackgroundServiceController { + late FlutterBackgroundService backgroundService; + bool supportedPlatform = true; + + @override + Future init() async { + if (!Platform.isAndroid && !Platform.isIOS) { + supportedPlatform = false; + return; + } + + backgroundService = FlutterBackgroundService(); + backgroundService.configure( + iosConfiguration: IosConfiguration(), + androidConfiguration: AndroidConfiguration( + onStart: (serviceInstance) {}, + isForegroundMode: true, + ), + ); + } +} diff --git a/lib/main.dart b/lib/main.dart index f52d363..ff0072a 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/background_service_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; @@ -12,6 +13,7 @@ void main() async { PlayerController.instance.initialize(); await VibrationController.instance.init(); await EasyLocalization.ensureInitialized(); + BackgroundServiceController.instance.init(); runApp( EasyLocalization( diff --git a/pubspec.yaml b/pubspec.yaml index abac17a..894f716 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -20,6 +20,7 @@ dependencies: easy_localization: ^3.0.6 flutter: sdk: flutter + flutter_background_service: ^5.0.5 flutter_cached_pdfview: ^0.4.2 flutter_dnd: ^0.1.4+1 flutter_inappwebview: ^6.0.0 From 3f5c8d68ef140d266534e74dfa2454411eaa0584 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Fri, 7 Jun 2024 18:36:41 +0300 Subject: [PATCH 02/16] Added ios required permissions for background service --- ios/Runner/Info.plist | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index b11e31e..9976b0f 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -2,6 +2,10 @@ + BGTaskSchedulerPermittedIdentifiers + + dev.flutter.background.refresh + CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion @@ -30,6 +34,11 @@ UIApplicationSupportsIndirectInputEvents + UIBackgroundModes + + audio + fetch + UILaunchStoryboardName LaunchScreen UIMainStoryboardFile @@ -47,9 +56,5 @@ UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight - BGTaskSchedulerPermittedIdentifiers - - dev.flutter.background.refresh - From f1b6dac714feb1c5676bb51ad40a3aa28b84fead Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Sun, 9 Jun 2024 14:44:38 +0300 Subject: [PATCH 03/16] wip background service is playing sound --- android/app/build.gradle | 6 +- android/app/src/main/AndroidManifest.xml | 2 + lib/domain/background_service_controller.dart | 8 ++ lib/domain/player_controller.dart | 4 +- lib/domain/timer_states.dart | 2 +- .../background_service_repository.dart | 51 ++++++- lib/infrastructure/player_repository.dart | 2 +- lib/main.dart | 2 +- .../progress_indicator_molecule.dart | 3 +- .../molecules/timer_molecule.dart | 1 + .../organisms/timer_organism.dart | 127 +++++++++++++----- pubspec.yaml | 3 + 12 files changed, 161 insertions(+), 50 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index ad9aaa5..4ab9607 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,6 +34,7 @@ android { ndkVersion flutter.ndkVersion compileOptions { + coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } @@ -54,6 +55,7 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName + multiDexEnabled true } signingConfigs { release { @@ -84,4 +86,6 @@ flutter { source '../..' } -dependencies {} +dependencies { + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' +} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 6f70915..67268fd 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,6 +4,8 @@ +` + _instance ??= _PlayerRepository(); + void init(); + void setIsSound(bool value); bool isSound(); - void initialize(); - Future play(String fileName); } diff --git a/lib/domain/timer_states.dart b/lib/domain/timer_states.dart index d5aa65f..6f618a8 100644 --- a/lib/domain/timer_states.dart +++ b/lib/domain/timer_states.dart @@ -65,7 +65,7 @@ enum EnergyType { 'undefined', Duration.zero, ), - veryLow('very_low', Duration(minutes: 5)), + veryLow('very_low', Duration(seconds: 5)), low('low', Duration(minutes: 10)), pomodoro('Pomodoro', Duration(minutes: 25)), high('high', Duration(minutes: 45), tipsId: ['45m/5m']), diff --git a/lib/infrastructure/background_service_repository.dart b/lib/infrastructure/background_service_repository.dart index 083fbb7..fcdca12 100644 --- a/lib/infrastructure/background_service_repository.dart +++ b/lib/infrastructure/background_service_repository.dart @@ -1,7 +1,7 @@ part of 'package:infinite_horizons/domain/background_service_controller.dart'; class _BackgroundServiceRepository extends BackgroundServiceController { - late FlutterBackgroundService backgroundService; + late FlutterBackgroundService service; bool supportedPlatform = true; @override @@ -10,14 +10,53 @@ class _BackgroundServiceRepository extends BackgroundServiceController { supportedPlatform = false; return; } - - backgroundService = FlutterBackgroundService(); - backgroundService.configure( - iosConfiguration: IosConfiguration(), + service = FlutterBackgroundService(); + service.configure( + iosConfiguration: IosConfiguration( + onForeground: onStart, + ), androidConfiguration: AndroidConfiguration( - onStart: (serviceInstance) {}, + onStart: onStart, isForegroundMode: true, ), ); } + + @override + void startIterateTimerStates() => service.invoke('startIterateTimerStates'); + + @override + void stopIterateTimerStates() { + // TODO: implement stopIterateTimerStates + } +} + +@pragma('vm:entry-point') +Future onIosBackground(ServiceInstance service) async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + + final SharedPreferences preferences = await SharedPreferences.getInstance(); + await preferences.reload(); + // final log = preferences.getStringList('log') ?? []; + // log.add(DateTime.now().toIso8601String()); + // await preferences.setStringList('log', log); + + return true; +} + +@pragma('vm:entry-point') +void onStart(ServiceInstance service) async { + DartPluginRegistrant.ensureInitialized(); + PlayerController.instance.init(); + + service.on('stopService').listen((event) { + service.stopSelf(); + }); + + service.on('startIterateTimerStates').listen((event) { + Timer(const Duration(seconds: 3), () async { + PlayerController.instance.play('start_session.wav'); + }); + }); } diff --git a/lib/infrastructure/player_repository.dart b/lib/infrastructure/player_repository.dart index f9b7f56..a1df847 100644 --- a/lib/infrastructure/player_repository.dart +++ b/lib/infrastructure/player_repository.dart @@ -6,7 +6,7 @@ class _PlayerRepository extends PlayerController { bool _isSound = true; @override - void initialize() { + void init() { player = AudioPlayer(); } diff --git a/lib/main.dart b/lib/main.dart index ff0072a..670b935 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -10,7 +10,7 @@ import 'package:infinite_horizons/presentation/pages/pages.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); await PreferencesController.instance.init(); - PlayerController.instance.initialize(); + PlayerController.instance.init(); await VibrationController.instance.init(); await EasyLocalization.ensureInitialized(); BackgroundServiceController.instance.init(); diff --git a/lib/presentation/molecules/progress_indicator_molecule.dart b/lib/presentation/molecules/progress_indicator_molecule.dart index e3049f2..9f584ac 100644 --- a/lib/presentation/molecules/progress_indicator_molecule.dart +++ b/lib/presentation/molecules/progress_indicator_molecule.dart @@ -3,6 +3,7 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/tip.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; +import 'package:infinite_horizons/presentation/organisms/organisms.dart'; class ProgressIndicatorMolecule extends StatelessWidget { const ProgressIndicatorMolecule({ @@ -13,7 +14,7 @@ class ProgressIndicatorMolecule extends StatelessWidget { @override Widget build(BuildContext context) { - const Duration getReadyDuration = Duration(seconds: 10); + final Duration getReadyDuration = TimerStateManager.getReadyDuration; final List tips = tipsList .where( diff --git a/lib/presentation/molecules/timer_molecule.dart b/lib/presentation/molecules/timer_molecule.dart index cf8fcc6..8f6b477 100644 --- a/lib/presentation/molecules/timer_molecule.dart +++ b/lib/presentation/molecules/timer_molecule.dart @@ -26,6 +26,7 @@ class _TimerMoleculeState extends State end: Duration.zero, ); controller.start(); + // TODO: Send selected time and timer start to background process controller.addListener(() { if (controller.state.value == CustomTimerState.finished) { widget.onComplete(); diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index 5ad950d..53a129e 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -1,4 +1,7 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/background_service_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; @@ -9,89 +12,139 @@ import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; import 'package:infinite_horizons/presentation/pages/pages.dart'; +class TimerStateManager { + static HomeState state = HomeState.study; + static TimerStates timerStates = StudyTypeAbstract.instance!.getTimerStates(); + static Duration getReadyDuration = const Duration(seconds: 10); + static Timer? _timer; + static VoidCallback? callback; + + static void incrementState() { + switch (state) { + case HomeState.study: + state = HomeState.getReadyForBreak; + PlayerController.instance.play('session_completed.wav'); + VibrationController.instance.vibrate(VibrationType.medium); + case HomeState.getReadyForBreak: + state = HomeState.breakTime; + case HomeState.breakTime: + state = HomeState.readyToStart; + PlayerController.instance.play('break_ended.wav'); + case HomeState.readyToStart: + timerStates.promoteSession(); + PlayerController.instance.play('start_session.wav'); + VibrationController.instance.vibrate(VibrationType.heavy); + state = HomeState.study; + } + } + + static Duration getTimerDuration() { + switch (state) { + case HomeState.study: + return timerStates.getCurrentSession().study; + case HomeState.getReadyForBreak: + return getReadyDuration; + case HomeState.breakTime: + return timerStates.getCurrentSession().breakDuration; + case HomeState.readyToStart: + return Duration.zero; + } + } + + int? getTime() => _timer?.tick; + + static Future iterateOverTimerStates() async { + final Duration stateDuration = getTimerDuration(); + if (stateDuration == Duration.zero) { + return; + } + + _timer = Timer( + stateDuration, + () { + incrementState(); + callback?.call(); + TimerStateManager.iterateOverTimerStates(); + }, + ); + } +} + class TimerOrganism extends StatefulWidget { @override State createState() => _TimerOrganismState(); } class _TimerOrganismState extends State - with AutomaticKeepAliveClientMixin { - HomeState state = HomeState.study; - late TimerStates timerStates; - - @override - bool get wantKeepAlive => true; + with WidgetsBindingObserver { + HomeState state = TimerStateManager.state; @override void initState() { super.initState(); + WidgetsBinding.instance.addObserver(this); final PreferencesController prefs = PreferencesController.instance; final bool lockScreen = prefs.getBool("isLockScreen") ?? true; WakeLockController.instance.setWakeLock(lockScreen); PlayerController.instance.setIsSound(prefs.getBool("isSound") ?? true); - timerStates = StudyTypeAbstract.instance!.getTimerStates(); if (lockScreen) { WakeLockController.instance.setWakeLock(true); } PlayerController.instance.play('start_session.wav'); VibrationController.instance.vibrate(VibrationType.heavy); + TimerStateManager.callback = setCurrentState; + TimerStateManager.iterateOverTimerStates(); } @override void dispose() { WakeLockController.instance.setWakeLock(false); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } - void setNextState() { - HomeState nextState; + @override + void didChangeAppLifecycleState(AppLifecycleState state) { switch (state) { - case HomeState.study: - nextState = HomeState.getReadyForBreak; - PlayerController.instance.play('session_completed.wav'); - VibrationController.instance.vibrate(VibrationType.medium); - case HomeState.getReadyForBreak: - nextState = HomeState.breakTime; - case HomeState.breakTime: - nextState = HomeState.readyToStart; - PlayerController.instance.play('break_ended.wav'); - case HomeState.readyToStart: - timerStates.promoteSession(); - PlayerController.instance.play('start_session.wav'); - VibrationController.instance.vibrate(VibrationType.heavy); - nextState = HomeState.study; + case AppLifecycleState.detached: + case AppLifecycleState.resumed: + case AppLifecycleState.inactive: + case AppLifecycleState.hidden: + return; + case AppLifecycleState.paused: + BackgroundServiceController.instance.startIterateTimerStates(); + return; } + } + + void setCurrentState() { setState(() { - state = nextState; + state = TimerStateManager.state; }); } Widget stateWidget() { switch (state) { case HomeState.study: - return TimerMolecule( - setNextState, - timerStates.getCurrentSession().study, - ); - case HomeState.getReadyForBreak: - return ProgressIndicatorMolecule(onComplete: setNextState); case HomeState.breakTime: return TimerMolecule( - setNextState, - timerStates.getCurrentSession().breakDuration, + () {}, + TimerStateManager.getTimerDuration(), ); + case HomeState.getReadyForBreak: + return ProgressIndicatorMolecule(onComplete: () {}); case HomeState.readyToStart: - return ReadyForSessionOrganism(setNextState); + return ReadyForSessionOrganism(() { + TimerStateManager.incrementState(); + TimerStateManager.iterateOverTimerStates(); + setCurrentState(); + }); } } - void settingsOnComplete() {} - @override Widget build(BuildContext context) { - super.build(context); - String title; switch (state) { case HomeState.study: diff --git a/pubspec.yaml b/pubspec.yaml index 894f716..b5a5989 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -17,13 +17,16 @@ dependencies: # Make an animated circular countdown custom_timer: ^0.2.3 # Internationalization (Easy translations) + device_info_plus: ^10.1.0 easy_localization: ^3.0.6 flutter: sdk: flutter flutter_background_service: ^5.0.5 + flutter_background_service_android: ^6.2.2 flutter_cached_pdfview: ^0.4.2 flutter_dnd: ^0.1.4+1 flutter_inappwebview: ^6.0.0 + flutter_local_notifications: ^17.1.2 flutter_vibrate: ^1.3.0 # Font Awesome Icon pack available as Flutter Icons. font_awesome_flutter: ^10.7.0 From a033b53360d0e4c08209c626e7d48f37947ce5e8 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Mon, 10 Jun 2024 13:33:01 +0300 Subject: [PATCH 04/16] wip background service transferring timer state --- lib/domain/background_service_controller.dart | 20 ++- .../{timer_states.dart => energy_level.dart} | 17 ++- lib/domain/preferences_controller.dart | 4 + lib/domain/study_type_abstract.dart | 14 +- .../background_service_repository.dart | 122 +++++++++++++---- .../preferences_repository.dart | 13 ++ .../molecules/energy_selection_molecule.dart | 12 +- .../progress_indicator_molecule.dart | 2 +- .../study_type_selection_molecule.dart | 14 +- .../molecules/timer_molecule.dart | 5 +- .../organisms/intro/tips_organism.dart | 2 +- .../organisms/ready_for_session_organism.dart | 2 +- .../organisms/timer_organism.dart | 123 ++++++++++++------ lib/presentation/pages/energy_tips_page.dart | 2 +- lib/presentation/pages/intro_page.dart | 9 +- .../pages/ready_for_session_page.dart | 2 +- pubspec.yaml | 1 - 17 files changed, 267 insertions(+), 97 deletions(-) rename lib/domain/{timer_states.dart => energy_level.dart} (81%) diff --git a/lib/domain/background_service_controller.dart b/lib/domain/background_service_controller.dart index c06b097..3276090 100644 --- a/lib/domain/background_service_controller.dart +++ b/lib/domain/background_service_controller.dart @@ -4,7 +4,16 @@ import 'dart:ui'; import 'package:flutter/material.dart'; import 'package:flutter_background_service/flutter_background_service.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; +import 'package:infinite_horizons/domain/preferences_controller.dart'; +import 'package:infinite_horizons/domain/study_type_abstract.dart'; +import 'package:infinite_horizons/domain/study_type_analytical.dart'; +import 'package:infinite_horizons/domain/study_type_creatively.dart'; +import 'package:infinite_horizons/domain/tip.dart'; +import 'package:infinite_horizons/domain/vibration_controller.dart'; +import 'package:infinite_horizons/infrastructure/core/logger.dart'; +import 'package:infinite_horizons/presentation/organisms/timer_organism.dart'; import 'package:shared_preferences/shared_preferences.dart'; part 'package:infinite_horizons/infrastructure/background_service_repository.dart'; @@ -17,6 +26,15 @@ abstract class BackgroundServiceController { Future init(); - void startIterateTimerStates(); + Future startService(); + void stopService(); + + void startIterateTimerStates( + TipType tipType, + EnergyType energyType, + TimerState timerState, + Duration remainingTime, + ); + void stopIterateTimerStates(); } diff --git a/lib/domain/timer_states.dart b/lib/domain/energy_level.dart similarity index 81% rename from lib/domain/timer_states.dart rename to lib/domain/energy_level.dart index 6f618a8..30d0d34 100644 --- a/lib/domain/timer_states.dart +++ b/lib/domain/energy_level.dart @@ -1,7 +1,7 @@ -class TimerStates { - TimerStates(this.type, this.sessions); +class EnergyLevel { + EnergyLevel(this.type, this.sessions); - factory TimerStates.fromEnergyType(EnergyType type) { + factory EnergyLevel.fromEnergyType(EnergyType type) { List states; switch (type) { @@ -34,7 +34,7 @@ class TimerStates { ]; } - return TimerStates(type, states); + return EnergyLevel(type, states); } EnergyType type; @@ -87,3 +87,12 @@ enum EnergyType { final String previewName; final List? tipsId; } + +extension EnergyTypeExtension on EnergyType { + static EnergyType fromString(String typeAsString) { + return EnergyType.values.firstWhere( + (element) => element.toString().split('.').last == typeAsString, + orElse: () => EnergyType.undefined, + ); + } +} diff --git a/lib/domain/preferences_controller.dart b/lib/domain/preferences_controller.dart index 74a1e1f..40764e0 100644 --- a/lib/domain/preferences_controller.dart +++ b/lib/domain/preferences_controller.dart @@ -9,15 +9,19 @@ abstract class PreferencesController { _instance ??= _PreferencesRepository(); Future init(); + Future reload(); String? getString(String key); bool? getBool(String key); + Duration? getDuration(String key); + void remove(String key); void setString(String key, String value); void setBool(String key, bool value); + void setDuration(String key, Duration value); } diff --git a/lib/domain/study_type_abstract.dart b/lib/domain/study_type_abstract.dart index 059f3e5..a43f874 100644 --- a/lib/domain/study_type_abstract.dart +++ b/lib/domain/study_type_abstract.dart @@ -1,22 +1,22 @@ import 'package:collection/collection.dart'; import 'package:flutter/cupertino.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/tip.dart'; abstract class StudyTypeAbstract { - StudyTypeAbstract(this.studyType); + StudyTypeAbstract(this.tipType); static StudyTypeAbstract? instance; - TipType studyType; + TipType tipType; - TimerStates? _timerStates; + EnergyLevel? _timerStates; void setTimerStates(EnergyType energy) => - _timerStates = TimerStates.fromEnergyType(energy); + _timerStates = EnergyLevel.fromEnergyType(energy); - TimerStates getTimerStates() => - _timerStates ?? TimerStates.fromEnergyType(EnergyType.undefined); + EnergyLevel getTimerStates() => + _timerStates ?? EnergyLevel.fromEnergyType(EnergyType.undefined); @protected List tips = []; diff --git a/lib/infrastructure/background_service_repository.dart b/lib/infrastructure/background_service_repository.dart index fcdca12..db07541 100644 --- a/lib/infrastructure/background_service_repository.dart +++ b/lib/infrastructure/background_service_repository.dart @@ -11,52 +11,126 @@ class _BackgroundServiceRepository extends BackgroundServiceController { return; } service = FlutterBackgroundService(); + service.configure( iosConfiguration: IosConfiguration( onForeground: onStart, + autoStart: false, ), androidConfiguration: AndroidConfiguration( onStart: onStart, isForegroundMode: true, + autoStart: false, ), ); } @override - void startIterateTimerStates() => service.invoke('startIterateTimerStates'); + Future startService() => service.startService(); + + @override + void stopService() => service.invoke('stopService'); + + @override + void startIterateTimerStates( + TipType tipType, + EnergyType energyType, + TimerState timerState, + Duration remainingTime, + ) => + service.invoke( + 'startIterateTimerStates', + // { + // 'tipType': tipType.name, + // 'energyType': energyType.name, + // 'timerState': timerState.name, + // 'remainingTime': 'remainingTime', + // } + ); @override void stopIterateTimerStates() { // TODO: implement stopIterateTimerStates } -} -@pragma('vm:entry-point') -Future onIosBackground(ServiceInstance service) async { - WidgetsFlutterBinding.ensureInitialized(); - DartPluginRegistrant.ensureInitialized(); + @pragma('vm:entry-point') + static Future onIosBackground(ServiceInstance service) async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); - final SharedPreferences preferences = await SharedPreferences.getInstance(); - await preferences.reload(); - // final log = preferences.getStringList('log') ?? []; - // log.add(DateTime.now().toIso8601String()); - // await preferences.setStringList('log', log); + final SharedPreferences preferences = await SharedPreferences.getInstance(); + await preferences.reload(); - return true; -} + return true; + } + + @pragma('vm:entry-point') + static Future onStart(ServiceInstance service) async { + WidgetsFlutterBinding.ensureInitialized(); + DartPluginRegistrant.ensureInitialized(); + PlayerController.instance.init(); -@pragma('vm:entry-point') -void onStart(ServiceInstance service) async { - DartPluginRegistrant.ensureInitialized(); - PlayerController.instance.init(); + service.on('stopService').listen((event) { + PreferencesController.instance + .setString('timerState', TimerStateManager.state.name); + PreferencesController.instance.setDuration( + 'remainingTimerTime', + TimerStateManager.getRemainingTime(), + ); + service.stopSelf(); + }); + + service.on('startIterateTimerStates').listen((event) async { + await PreferencesController.instance.init(); + await PreferencesController.instance.reload(); + await VibrationController.instance.init(); + PlayerController.instance.init(); + + // final TipType tipType= event!['tipType'] as TipType; + // final EnergyType energyType = event['energyType'] as EnergyType; + // final TimerState timerState= event['timerState'] as TimerState; + // final Duration remainingTime= event['remainingTime'] as Duration; + + final TimerState state = TimerStateExtension.fromString( + PreferencesController.instance.getString('timerState') ?? '', + ); + TimerStateManager.state = state; + + final Duration remainingTime = + PreferencesController.instance.getDuration('remainingTimerTime') ?? + Duration.zero; + final TipType selectedType = TipTypeExtension.fromString( + PreferencesController.instance.getString('tipType') ?? '', + ); + StudyTypeAbstract.instance = selectedType == TipType.analytical + ? StudyTypeAnalytical() + : StudyTypeCreatively(); - service.on('stopService').listen((event) { - service.stopSelf(); - }); + final EnergyType energy = EnergyTypeExtension.fromString( + PreferencesController.instance.getString('energyType') ?? '', + ); - service.on('startIterateTimerStates').listen((event) { - Timer(const Duration(seconds: 3), () async { - PlayerController.instance.play('start_session.wav'); + StudyTypeAbstract.instance!.setTimerStates(energy); + + TimerStateManager.callback = () async { + final TimerState currentState = TimerStateManager.state; + PreferencesController.instance + .setString('timerState', currentState.name); + logger.i('Service saved timerState $currentState'); + + if (currentState != TimerState.readyToStart) { + return; + } + PreferencesController.instance.setDuration( + 'remainingTimerTime', + TimerStateManager.getRemainingTime(), + ); + await Future.delayed(const Duration(seconds: 5)); + logger.i('Kill process'); + service.stopSelf(); + }; + + TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); }); - }); + } } diff --git a/lib/infrastructure/preferences_repository.dart b/lib/infrastructure/preferences_repository.dart index a1c7530..0485b3b 100644 --- a/lib/infrastructure/preferences_repository.dart +++ b/lib/infrastructure/preferences_repository.dart @@ -6,12 +6,21 @@ class _PreferencesRepository extends PreferencesController { @override Future init() async => preferences = await SharedPreferences.getInstance(); + @override + Future reload() => preferences.reload(); + @override String? getString(String key) => preferences.getString(key); @override bool? getBool(String key) => preferences.getBool(key); + @override + Duration? getDuration(String key) { + final int? milliseconds = preferences.getInt(key); + return milliseconds != null ? Duration(milliseconds: milliseconds) : null; + } + @override void remove(String key) => preferences.remove(key); @@ -20,4 +29,8 @@ class _PreferencesRepository extends PreferencesController { @override void setBool(String key, bool value) => preferences.setBool(key, value); + + @override + void setDuration(String key, Duration value) => + preferences.setInt(key, value.inMilliseconds); } diff --git a/lib/presentation/molecules/energy_selection_molecule.dart b/lib/presentation/molecules/energy_selection_molecule.dart index db55b21..0e23ba9 100644 --- a/lib/presentation/molecules/energy_selection_molecule.dart +++ b/lib/presentation/molecules/energy_selection_molecule.dart @@ -1,8 +1,9 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; +import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; @@ -19,7 +20,7 @@ class EnergySelectionMolecule extends StatefulWidget { } class _EnergySelectionMoleculeState extends State { - late TimerStates timerStates; + late EnergyLevel timerStates; @override void initState() { @@ -28,7 +29,10 @@ class _EnergySelectionMoleculeState extends State { } void onChanged(EnergyType? type) { - StudyTypeAbstract.instance!.setTimerStates(type ?? EnergyType.undefined); + final EnergyType energy = type ?? EnergyType.undefined; + StudyTypeAbstract.instance!.setTimerStates(energy); + PreferencesController.instance.setString('energyType', energy.name); + setState(() { timerStates = StudyTypeAbstract.instance!.getTimerStates(); }); @@ -36,7 +40,7 @@ class _EnergySelectionMoleculeState extends State { } Widget energyWidget(EnergyType type) { - final TimerStates timerStatesTemp = TimerStates.fromEnergyType(type); + final EnergyLevel timerStatesTemp = EnergyLevel.fromEnergyType(type); String subtitle = ''; final TimerSession firstTimerState = timerStatesTemp.sessions.first; diff --git a/lib/presentation/molecules/progress_indicator_molecule.dart b/lib/presentation/molecules/progress_indicator_molecule.dart index 9f584ac..e414f42 100644 --- a/lib/presentation/molecules/progress_indicator_molecule.dart +++ b/lib/presentation/molecules/progress_indicator_molecule.dart @@ -20,7 +20,7 @@ class ProgressIndicatorMolecule extends StatelessWidget { .where( (element) => element.timing == TipTiming.inBreak && - (element.type == StudyTypeAbstract.instance!.studyType || + (element.type == StudyTypeAbstract.instance!.tipType || element.type == TipType.general), ) .toList(); diff --git a/lib/presentation/molecules/study_type_selection_molecule.dart b/lib/presentation/molecules/study_type_selection_molecule.dart index 09dfa85..0b21161 100644 --- a/lib/presentation/molecules/study_type_selection_molecule.dart +++ b/lib/presentation/molecules/study_type_selection_molecule.dart @@ -1,5 +1,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/study_type_analytical.dart'; import 'package:infinite_horizons/domain/study_type_creatively.dart'; @@ -25,18 +26,19 @@ class _StudyTypeSelectionMoleculeState @override void initState() { super.initState(); - selectedType = StudyTypeAbstract.instance?.studyType ?? TipType.undefined; + selectedType = StudyTypeAbstract.instance?.tipType ?? TipType.undefined; } void onChanged(TipType? type) { setState(() { selectedType = type ?? TipType.undefined; }); - if (selectedType == TipType.analytical) { - StudyTypeAbstract.instance = StudyTypeAnalytical(); - } else { - StudyTypeAbstract.instance = StudyTypeCreatively(); - } + StudyTypeAbstract.instance = selectedType == TipType.analytical + ? StudyTypeAnalytical() + : StudyTypeCreatively(); + + PreferencesController.instance.setString('tipType', selectedType.name); + widget.onSelected(); } diff --git a/lib/presentation/molecules/timer_molecule.dart b/lib/presentation/molecules/timer_molecule.dart index 8f6b477..6f51298 100644 --- a/lib/presentation/molecules/timer_molecule.dart +++ b/lib/presentation/molecules/timer_molecule.dart @@ -4,10 +4,11 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; class TimerMolecule extends StatefulWidget { - const TimerMolecule(this.onComplete, this.duration); + const TimerMolecule(this.onComplete, this.duration, {this.initialValue}); final Duration duration; final VoidCallback onComplete; + final Duration? initialValue; @override State createState() => _TimerMoleculeState(); @@ -22,7 +23,7 @@ class _TimerMoleculeState extends State super.initState(); controller = CustomTimerController( vsync: this, - begin: widget.duration, + begin: widget.initialValue ?? widget.duration, end: Duration.zero, ); controller.start(); diff --git a/lib/presentation/organisms/intro/tips_organism.dart b/lib/presentation/organisms/intro/tips_organism.dart index 5c0ce8b..d5b1b8a 100644 --- a/lib/presentation/organisms/intro/tips_organism.dart +++ b/lib/presentation/organisms/intro/tips_organism.dart @@ -57,7 +57,7 @@ class _TipsOrganismState extends State { (element) => (element.timing == TipTiming.before && (element.type == TipType.general || - element.type == StudyTypeAbstract.instance!.studyType)) && + element.type == StudyTypeAbstract.instance!.tipType)) && // We can toggle dnd only on android !(element.id == 'dnd' && Platform.isAndroid), ) diff --git a/lib/presentation/organisms/ready_for_session_organism.dart b/lib/presentation/organisms/ready_for_session_organism.dart index 9aff741..ecb8650 100644 --- a/lib/presentation/organisms/ready_for_session_organism.dart +++ b/lib/presentation/organisms/ready_for_session_organism.dart @@ -44,7 +44,7 @@ class _ReadyForSessionOrganismState extends State { .where( (element) => element.timing == TipTiming.inSession && - (element.type == StudyTypeAbstract.instance!.studyType || + (element.type == StudyTypeAbstract.instance!.tipType || element.type == TipType.general), ) .toList(); diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index 53a129e..c51aa91 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -2,69 +2,78 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/background_service_controller.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; import 'package:infinite_horizons/domain/wake_lock_controller.dart'; +import 'package:infinite_horizons/infrastructure/core/logger.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; import 'package:infinite_horizons/presentation/pages/pages.dart'; class TimerStateManager { - static HomeState state = HomeState.study; - static TimerStates timerStates = StudyTypeAbstract.instance!.getTimerStates(); + static TimerState state = TimerState.study; + static EnergyLevel timerStates = StudyTypeAbstract.instance!.getTimerStates(); static Duration getReadyDuration = const Duration(seconds: 10); static Timer? _timer; static VoidCallback? callback; + static Duration _remainingTime = Duration.zero; static void incrementState() { switch (state) { - case HomeState.study: - state = HomeState.getReadyForBreak; + case TimerState.study: + state = TimerState.getReadyForBreak; PlayerController.instance.play('session_completed.wav'); VibrationController.instance.vibrate(VibrationType.medium); - case HomeState.getReadyForBreak: - state = HomeState.breakTime; - case HomeState.breakTime: - state = HomeState.readyToStart; + case TimerState.getReadyForBreak: + state = TimerState.breakTime; + case TimerState.breakTime: + state = TimerState.readyToStart; PlayerController.instance.play('break_ended.wav'); - case HomeState.readyToStart: + case TimerState.readyToStart: timerStates.promoteSession(); PlayerController.instance.play('start_session.wav'); VibrationController.instance.vibrate(VibrationType.heavy); - state = HomeState.study; + state = TimerState.study; } } static Duration getTimerDuration() { switch (state) { - case HomeState.study: + case TimerState.study: return timerStates.getCurrentSession().study; - case HomeState.getReadyForBreak: + case TimerState.getReadyForBreak: return getReadyDuration; - case HomeState.breakTime: + case TimerState.breakTime: return timerStates.getCurrentSession().breakDuration; - case HomeState.readyToStart: + case TimerState.readyToStart: return Duration.zero; } } - int? getTime() => _timer?.tick; + static Duration getRemainingTime() => _remainingTime; + static void pauseTimer() => _timer?.cancel(); - static Future iterateOverTimerStates() async { - final Duration stateDuration = getTimerDuration(); + static Future iterateOverTimerStates({Duration? remainingTime}) async { + final Duration stateDuration = remainingTime ?? getTimerDuration(); if (stateDuration == Duration.zero) { return; } - _timer = Timer( - stateDuration, - () { - incrementState(); - callback?.call(); - TimerStateManager.iterateOverTimerStates(); + _remainingTime = stateDuration; + _timer = Timer.periodic( + const Duration(seconds: 1), + (Timer timer) { + if (_remainingTime <= const Duration(seconds: 1)) { + _timer?.cancel(); + incrementState(); + callback?.call(); + iterateOverTimerStates(); + return; + } + _remainingTime = _remainingTime - const Duration(seconds: 1); }, ); } @@ -77,7 +86,7 @@ class TimerOrganism extends StatefulWidget { class _TimerOrganismState extends State with WidgetsBindingObserver { - HomeState state = TimerStateManager.state; + TimerState state = TimerStateManager.state; @override void initState() { @@ -105,15 +114,44 @@ class _TimerOrganismState extends State } @override - void didChangeAppLifecycleState(AppLifecycleState state) { - switch (state) { + Future didChangeAppLifecycleState(AppLifecycleState appState) async { + switch (appState) { case AppLifecycleState.detached: - case AppLifecycleState.resumed: case AppLifecycleState.inactive: case AppLifecycleState.hidden: return; + case AppLifecycleState.resumed: + BackgroundServiceController.instance.stopService(); + await PreferencesController.instance.reload(); + final TimerState state = TimerStateExtension.fromString( + PreferencesController.instance.getString('timerState') ?? '', + ); + logger.i('App resumed with timerState $state'); + TimerStateManager.state = state; + + final Duration remainingTime = + PreferencesController.instance.getDuration('remainingTimerTime') ?? + Duration.zero; + TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); + setCurrentState(); + return; case AppLifecycleState.paused: - BackgroundServiceController.instance.startIterateTimerStates(); + TimerStateManager.pauseTimer(); + await BackgroundServiceController.instance.startService(); + PreferencesController.instance.setDuration( + 'remainingTimerTime', + TimerStateManager.getRemainingTime(), + ); + + PreferencesController.instance + .setString('tipType', StudyTypeAbstract.instance!.tipType.name); + + BackgroundServiceController.instance.startIterateTimerStates( + StudyTypeAbstract.instance!.tipType, + StudyTypeAbstract.instance!.getTimerStates().type, + state, + TimerStateManager.getRemainingTime(), + ); return; } } @@ -126,15 +164,15 @@ class _TimerOrganismState extends State Widget stateWidget() { switch (state) { - case HomeState.study: - case HomeState.breakTime: + case TimerState.study: + case TimerState.breakTime: return TimerMolecule( () {}, TimerStateManager.getTimerDuration(), ); - case HomeState.getReadyForBreak: + case TimerState.getReadyForBreak: return ProgressIndicatorMolecule(onComplete: () {}); - case HomeState.readyToStart: + case TimerState.readyToStart: return ReadyForSessionOrganism(() { TimerStateManager.incrementState(); TimerStateManager.iterateOverTimerStates(); @@ -147,13 +185,13 @@ class _TimerOrganismState extends State Widget build(BuildContext context) { String title; switch (state) { - case HomeState.study: + case TimerState.study: title = 'study_timer'; - case HomeState.getReadyForBreak: + case TimerState.getReadyForBreak: title = 'ready_for_break'; - case HomeState.breakTime: + case TimerState.breakTime: title = 'take_break'; - case HomeState.readyToStart: + case TimerState.readyToStart: title = 'ready_for_session'; } @@ -167,10 +205,19 @@ class _TimerOrganismState extends State } } -enum HomeState { +enum TimerState { study, getReadyForBreak, breakTime, readyToStart, ; } + +extension TimerStateExtension on TimerState { + static TimerState fromString(String typeAsString) { + return TimerState.values.firstWhere( + (element) => element.toString().split('.').last == typeAsString, + orElse: () => TimerState.values.first, + ); + } +} diff --git a/lib/presentation/pages/energy_tips_page.dart b/lib/presentation/pages/energy_tips_page.dart index f4d12ca..c0291e2 100644 --- a/lib/presentation/pages/energy_tips_page.dart +++ b/lib/presentation/pages/energy_tips_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; import 'package:font_awesome_flutter/font_awesome_flutter.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/tip.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; diff --git a/lib/presentation/pages/intro_page.dart b/lib/presentation/pages/intro_page.dart index 442488c..0dbb509 100644 --- a/lib/presentation/pages/intro_page.dart +++ b/lib/presentation/pages/intro_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; import 'package:infinite_horizons/domain/tip.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; @@ -66,7 +66,7 @@ class _IntroPageState extends State { decoration: emptyPageDecoration(), bodyWidget: StudyTypeSelectionMolecule(() async { setState(() { - studyType = StudyTypeAbstract.instance!.studyType.name; + studyType = StudyTypeAbstract.instance!.tipType.name; }); await Future.delayed(selectionTransitionDelay); nextPage(); @@ -103,9 +103,8 @@ class _IntroPageState extends State { bool showNextButtonTemp = true; if (state == IntroState.studyType && - (StudyTypeAbstract.instance?.studyType == null || - StudyTypeAbstract.instance!.studyType == - TipType.undefined)) { + (StudyTypeAbstract.instance?.tipType == null || + StudyTypeAbstract.instance!.tipType == TipType.undefined)) { showNextButtonTemp = false; } else if (state == IntroState.energy && StudyTypeAbstract.instance!.getTimerStates().type == diff --git a/lib/presentation/pages/ready_for_session_page.dart b/lib/presentation/pages/ready_for_session_page.dart index 13d0754..b6501ff 100644 --- a/lib/presentation/pages/ready_for_session_page.dart +++ b/lib/presentation/pages/ready_for_session_page.dart @@ -1,6 +1,6 @@ import 'package:flutter/cupertino.dart'; +import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; -import 'package:infinite_horizons/domain/timer_states.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; diff --git a/pubspec.yaml b/pubspec.yaml index b5a5989..7d1ca1d 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -22,7 +22,6 @@ dependencies: flutter: sdk: flutter flutter_background_service: ^5.0.5 - flutter_background_service_android: ^6.2.2 flutter_cached_pdfview: ^0.4.2 flutter_dnd: ^0.1.4+1 flutter_inappwebview: ^6.0.0 From 993c6fec26d759ee6bbf2a4c64f86386e038b5bd Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Tue, 11 Jun 2024 10:16:29 +0300 Subject: [PATCH 05/16] Works on Android api 29 --- lib/domain/background_service_controller.dart | 7 +---- .../background_service_repository.dart | 28 +++++++------------ .../organisms/timer_organism.dart | 26 +++++++++-------- 3 files changed, 26 insertions(+), 35 deletions(-) diff --git a/lib/domain/background_service_controller.dart b/lib/domain/background_service_controller.dart index 3276090..f667c35 100644 --- a/lib/domain/background_service_controller.dart +++ b/lib/domain/background_service_controller.dart @@ -29,12 +29,7 @@ abstract class BackgroundServiceController { Future startService(); void stopService(); - void startIterateTimerStates( - TipType tipType, - EnergyType energyType, - TimerState timerState, - Duration remainingTime, - ); + void startIterateTimerStates(); void stopIterateTimerStates(); } diff --git a/lib/infrastructure/background_service_repository.dart b/lib/infrastructure/background_service_repository.dart index db07541..ba65038 100644 --- a/lib/infrastructure/background_service_repository.dart +++ b/lib/infrastructure/background_service_repository.dart @@ -32,13 +32,7 @@ class _BackgroundServiceRepository extends BackgroundServiceController { void stopService() => service.invoke('stopService'); @override - void startIterateTimerStates( - TipType tipType, - EnergyType energyType, - TimerState timerState, - Duration remainingTime, - ) => - service.invoke( + void startIterateTimerStates() => service.invoke( 'startIterateTimerStates', // { // 'tipType': tipType.name, @@ -69,13 +63,17 @@ class _BackgroundServiceRepository extends BackgroundServiceController { WidgetsFlutterBinding.ensureInitialized(); DartPluginRegistrant.ensureInitialized(); PlayerController.instance.init(); - service.on('stopService').listen((event) { - PreferencesController.instance - .setString('timerState', TimerStateManager.state.name); + // PreferencesController.instance + // .setString('timerState', TimerStateManager.state.name); + // PreferencesController.instance.setDuration( + // 'remainingTimerTime', + // TimerStateManager.getRemainingTime(), + // ); + PreferencesController.instance.setDuration( 'remainingTimerTime', - TimerStateManager.getRemainingTime(), + TimerStateManager.getRemainingTime() ?? Duration.zero, ); service.stopSelf(); }); @@ -86,11 +84,6 @@ class _BackgroundServiceRepository extends BackgroundServiceController { await VibrationController.instance.init(); PlayerController.instance.init(); - // final TipType tipType= event!['tipType'] as TipType; - // final EnergyType energyType = event['energyType'] as EnergyType; - // final TimerState timerState= event['timerState'] as TimerState; - // final Duration remainingTime= event['remainingTime'] as Duration; - final TimerState state = TimerStateExtension.fromString( PreferencesController.instance.getString('timerState') ?? '', ); @@ -116,14 +109,13 @@ class _BackgroundServiceRepository extends BackgroundServiceController { final TimerState currentState = TimerStateManager.state; PreferencesController.instance .setString('timerState', currentState.name); - logger.i('Service saved timerState $currentState'); if (currentState != TimerState.readyToStart) { return; } PreferencesController.instance.setDuration( 'remainingTimerTime', - TimerStateManager.getRemainingTime(), + TimerStateManager.getRemainingTime() ?? Duration.zero, ); await Future.delayed(const Duration(seconds: 5)); logger.i('Kill process'); diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index c51aa91..d4d8975 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -53,7 +53,9 @@ class TimerStateManager { } } - static Duration getRemainingTime() => _remainingTime; + static Duration? getRemainingTime() => + _remainingTime == Duration.zero ? null : _remainingTime; + static void pauseTimer() => _timer?.cancel(); static Future iterateOverTimerStates({Duration? remainingTime}) async { @@ -67,6 +69,7 @@ class TimerStateManager { const Duration(seconds: 1), (Timer timer) { if (_remainingTime <= const Duration(seconds: 1)) { + _remainingTime = Duration.zero; _timer?.cancel(); incrementState(); callback?.call(); @@ -108,6 +111,7 @@ class _TimerOrganismState extends State @override void dispose() { + TimerStateManager.pauseTimer(); WakeLockController.instance.setWakeLock(false); WidgetsBinding.instance.removeObserver(this); super.dispose(); @@ -122,36 +126,35 @@ class _TimerOrganismState extends State return; case AppLifecycleState.resumed: BackgroundServiceController.instance.stopService(); + await Future.delayed(const Duration(milliseconds: 200)); await PreferencesController.instance.reload(); final TimerState state = TimerStateExtension.fromString( PreferencesController.instance.getString('timerState') ?? '', ); - logger.i('App resumed with timerState $state'); TimerStateManager.state = state; final Duration remainingTime = PreferencesController.instance.getDuration('remainingTimerTime') ?? Duration.zero; + logger.i('remainingTimerTime from background $remainingTime'); TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); setCurrentState(); return; case AppLifecycleState.paused: + if (TimerStateManager.state == TimerState.readyToStart) { + return; + } + TimerStateManager.pauseTimer(); await BackgroundServiceController.instance.startService(); PreferencesController.instance.setDuration( 'remainingTimerTime', - TimerStateManager.getRemainingTime(), + TimerStateManager.getRemainingTime() ?? Duration.zero, ); PreferencesController.instance - .setString('tipType', StudyTypeAbstract.instance!.tipType.name); - - BackgroundServiceController.instance.startIterateTimerStates( - StudyTypeAbstract.instance!.tipType, - StudyTypeAbstract.instance!.getTimerStates().type, - state, - TimerStateManager.getRemainingTime(), - ); + .setString('timerState', TimerStateManager.state.name); + BackgroundServiceController.instance.startIterateTimerStates(); return; } } @@ -169,6 +172,7 @@ class _TimerOrganismState extends State return TimerMolecule( () {}, TimerStateManager.getTimerDuration(), + initialValue: TimerStateManager.getRemainingTime(), ); case TimerState.getReadyForBreak: return ProgressIndicatorMolecule(onComplete: () {}); From 56998164dd117dcdc6e12e9af0bcc924eb196ecd Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Tue, 11 Jun 2024 11:32:51 +0300 Subject: [PATCH 06/16] Fixed indicator state --- .../atoms/progress_indicator_atom.dart | 12 +++- .../progress_indicator_molecule.dart | 10 +-- .../organisms/timer_organism.dart | 66 ++++------------- lib/presentation/pages/home_page.dart | 71 +++++++++++++++++-- 4 files changed, 93 insertions(+), 66 deletions(-) diff --git a/lib/presentation/atoms/progress_indicator_atom.dart b/lib/presentation/atoms/progress_indicator_atom.dart index 2678cd7..bd7b68b 100644 --- a/lib/presentation/atoms/progress_indicator_atom.dart +++ b/lib/presentation/atoms/progress_indicator_atom.dart @@ -9,12 +9,15 @@ class ProgressIndicatorAtom extends StatefulWidget { this.inputController, this.isPdfLoader = false, this.centerWidget, + this.initialValue, }); + final Duration totalDuration; final VoidCallback callback; final AnimationController? inputController; final bool isPdfLoader; final Widget? centerWidget; + final Duration? initialValue; @override State createState() => _ProgressIndicatorAtomState(); @@ -25,15 +28,20 @@ class _ProgressIndicatorAtomState extends State late AnimationController controller; @override void initState() { - updateProgress(); + initiateController(); super.initState(); } - void updateProgress() { + void initiateController() { + final double initialValueRatio = + (widget.initialValue?.inMilliseconds ?? 0) / + widget.totalDuration.inMilliseconds; + controller = widget.inputController ?? AnimationController( vsync: this, duration: widget.totalDuration, + value: initialValueRatio, ); controller.addListener(() { setState(() {}); diff --git a/lib/presentation/molecules/progress_indicator_molecule.dart b/lib/presentation/molecules/progress_indicator_molecule.dart index e414f42..1864456 100644 --- a/lib/presentation/molecules/progress_indicator_molecule.dart +++ b/lib/presentation/molecules/progress_indicator_molecule.dart @@ -3,19 +3,20 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/tip.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; -import 'package:infinite_horizons/presentation/organisms/organisms.dart'; class ProgressIndicatorMolecule extends StatelessWidget { const ProgressIndicatorMolecule({ + required this.duration, required this.onComplete, + this.initialValue, }); + final Duration duration; final VoidCallback onComplete; + final Duration? initialValue; @override Widget build(BuildContext context) { - final Duration getReadyDuration = TimerStateManager.getReadyDuration; - final List tips = tipsList .where( (element) => @@ -50,8 +51,9 @@ class ProgressIndicatorMolecule extends StatelessWidget { ], ), ProgressIndicatorAtom( - totalDuration: getReadyDuration, + totalDuration: duration, callback: onComplete, + initialValue: initialValue, ), ], ); diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index d4d8975..2b33432 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -1,14 +1,12 @@ import 'dart:async'; import 'package:flutter/material.dart'; -import 'package:infinite_horizons/domain/background_service_controller.dart'; import 'package:infinite_horizons/domain/energy_level.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; import 'package:infinite_horizons/domain/wake_lock_controller.dart'; -import 'package:infinite_horizons/infrastructure/core/logger.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; import 'package:infinite_horizons/presentation/pages/pages.dart'; @@ -83,18 +81,18 @@ class TimerStateManager { } class TimerOrganism extends StatefulWidget { + const TimerOrganism({super.key}); + @override - State createState() => _TimerOrganismState(); + State createState() => TimerOrganismState(); } -class _TimerOrganismState extends State - with WidgetsBindingObserver { +class TimerOrganismState extends State { TimerState state = TimerStateManager.state; @override void initState() { super.initState(); - WidgetsBinding.instance.addObserver(this); final PreferencesController prefs = PreferencesController.instance; final bool lockScreen = prefs.getBool("isLockScreen") ?? true; WakeLockController.instance.setWakeLock(lockScreen); @@ -103,62 +101,15 @@ class _TimerOrganismState extends State if (lockScreen) { WakeLockController.instance.setWakeLock(true); } - PlayerController.instance.play('start_session.wav'); - VibrationController.instance.vibrate(VibrationType.heavy); TimerStateManager.callback = setCurrentState; - TimerStateManager.iterateOverTimerStates(); } @override void dispose() { - TimerStateManager.pauseTimer(); WakeLockController.instance.setWakeLock(false); - WidgetsBinding.instance.removeObserver(this); super.dispose(); } - @override - Future didChangeAppLifecycleState(AppLifecycleState appState) async { - switch (appState) { - case AppLifecycleState.detached: - case AppLifecycleState.inactive: - case AppLifecycleState.hidden: - return; - case AppLifecycleState.resumed: - BackgroundServiceController.instance.stopService(); - await Future.delayed(const Duration(milliseconds: 200)); - await PreferencesController.instance.reload(); - final TimerState state = TimerStateExtension.fromString( - PreferencesController.instance.getString('timerState') ?? '', - ); - TimerStateManager.state = state; - - final Duration remainingTime = - PreferencesController.instance.getDuration('remainingTimerTime') ?? - Duration.zero; - logger.i('remainingTimerTime from background $remainingTime'); - TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); - setCurrentState(); - return; - case AppLifecycleState.paused: - if (TimerStateManager.state == TimerState.readyToStart) { - return; - } - - TimerStateManager.pauseTimer(); - await BackgroundServiceController.instance.startService(); - PreferencesController.instance.setDuration( - 'remainingTimerTime', - TimerStateManager.getRemainingTime() ?? Duration.zero, - ); - - PreferencesController.instance - .setString('timerState', TimerStateManager.state.name); - BackgroundServiceController.instance.startIterateTimerStates(); - return; - } - } - void setCurrentState() { setState(() { state = TimerStateManager.state; @@ -175,7 +126,14 @@ class _TimerOrganismState extends State initialValue: TimerStateManager.getRemainingTime(), ); case TimerState.getReadyForBreak: - return ProgressIndicatorMolecule(onComplete: () {}); + final Duration totalTime = TimerStateManager.getReadyDuration; + final Duration timePassed = + totalTime - (TimerStateManager.getRemainingTime() ?? totalTime); + return ProgressIndicatorMolecule( + duration: totalTime, + onComplete: () {}, + initialValue: timePassed, + ); case TimerState.readyToStart: return ReadyForSessionOrganism(() { TimerStateManager.incrementState(); diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 5e1b17c..3f71e0e 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -1,4 +1,8 @@ import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/background_service_controller.dart'; +import 'package:infinite_horizons/domain/player_controller.dart'; +import 'package:infinite_horizons/domain/preferences_controller.dart'; +import 'package:infinite_horizons/domain/vibration_controller.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; @@ -7,19 +11,74 @@ class HomePage extends StatefulWidget { State createState() => _HomePageState(); } -class _HomePageState extends State { +class _HomePageState extends State with WidgetsBindingObserver { int _currentTabNum = 0; - final _tabs = [ - TimerOrganism(), - TextAreaOrganism(), - ]; + final GlobalKey timerKey = + GlobalKey(); + + List _tabs() => [ + TimerOrganism(key: timerKey), + TextAreaOrganism(), + ]; final _pageController = PageController(); + @override + void initState() { + super.initState(); + WidgetsBinding.instance.addObserver(this); + PlayerController.instance.play('start_session.wav'); + VibrationController.instance.vibrate(VibrationType.heavy); + TimerStateManager.iterateOverTimerStates(); + } + + @override + Future didChangeAppLifecycleState(AppLifecycleState appState) async { + switch (appState) { + case AppLifecycleState.detached: + case AppLifecycleState.inactive: + case AppLifecycleState.hidden: + return; + case AppLifecycleState.resumed: + BackgroundServiceController.instance.stopService(); + await Future.delayed(const Duration(milliseconds: 200)); + await PreferencesController.instance.reload(); + final TimerState state = TimerStateExtension.fromString( + PreferencesController.instance.getString('timerState') ?? '', + ); + TimerStateManager.state = state; + + final Duration remainingTime = + PreferencesController.instance.getDuration('remainingTimerTime') ?? + Duration.zero; + TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); + timerKey.currentState?.setCurrentState(); + return; + case AppLifecycleState.paused: + if (TimerStateManager.state == TimerState.readyToStart) { + return; + } + + TimerStateManager.pauseTimer(); + await BackgroundServiceController.instance.startService(); + PreferencesController.instance.setDuration( + 'remainingTimerTime', + TimerStateManager.getRemainingTime() ?? Duration.zero, + ); + + PreferencesController.instance + .setString('timerState', TimerStateManager.state.name); + BackgroundServiceController.instance.startIterateTimerStates(); + return; + } + } + @override void dispose() { + TimerStateManager.pauseTimer(); _pageController.dispose(); + WidgetsBinding.instance.removeObserver(this); super.dispose(); } @@ -44,7 +103,7 @@ class _HomePageState extends State { }); }, controller: _pageController, - children: _tabs, + children: _tabs(), ), bottomNavigationBar: BottomNavigationBarHomePage(callback, _currentTabNum), From 6e241819660103f1ea809fdd8d32570a65b3b574 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Tue, 11 Jun 2024 13:27:06 +0300 Subject: [PATCH 07/16] Fix background process not working on Android 14 --- android/app/build.gradle | 2 ++ android/app/src/main/AndroidManifest.xml | 8 +++++++- lib/presentation/pages/home_page.dart | 15 ++++++++++++--- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/android/app/build.gradle b/android/app/build.gradle index 4ab9607..534483c 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -87,5 +87,7 @@ flutter { } dependencies { + implementation "com.google.android.material:material:1.9.0" + implementation "androidx.window:window:1.1.0" coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' } diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 67268fd..74b9830 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -4,7 +4,7 @@ -` +` @@ -12,6 +12,12 @@ android:label="Infinite Horizons" android:name="${applicationName}" android:icon="@mipmap/ic_launcher"> + + + with WidgetsBindingObserver { case AppLifecycleState.hidden: return; case AppLifecycleState.resumed: - BackgroundServiceController.instance.stopService(); - await Future.delayed(const Duration(milliseconds: 200)); + // TODO: Check for ios + if (Platform.isAndroid) { + BackgroundServiceController.instance.stopService(); + await Future.delayed(const Duration(milliseconds: 200)); + } await PreferencesController.instance.reload(); final TimerState state = TimerStateExtension.fromString( PreferencesController.instance.getString('timerState') ?? '', @@ -61,7 +66,6 @@ class _HomePageState extends State with WidgetsBindingObserver { } TimerStateManager.pauseTimer(); - await BackgroundServiceController.instance.startService(); PreferencesController.instance.setDuration( 'remainingTimerTime', TimerStateManager.getRemainingTime() ?? Duration.zero, @@ -69,6 +73,11 @@ class _HomePageState extends State with WidgetsBindingObserver { PreferencesController.instance .setString('timerState', TimerStateManager.state.name); + // TODO: Check how it react + if (!Platform.isAndroid) { + return; + } + await BackgroundServiceController.instance.startService(); BackgroundServiceController.instance.startIterateTimerStates(); return; } From 49f9b6cf9b29a73d32769447589d46c9354327fa Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Tue, 11 Jun 2024 15:06:00 +0300 Subject: [PATCH 08/16] Removed background process parts --- android/app/build.gradle | 8 +- android/app/src/main/AndroidManifest.xml | 9 -- ios/Runner/Info.plist | 4 - lib/domain/background_service_controller.dart | 35 ----- .../background_service_repository.dart | 128 ------------------ lib/infrastructure/player_repository.dart | 4 +- lib/main.dart | 2 - .../atoms/progress_indicator_atom.dart | 7 - lib/presentation/atoms/timer_atom.dart | 3 +- .../molecules/pdf_viewer_molecule.dart | 1 - .../progress_indicator_molecule.dart | 3 - .../molecules/timer_molecule.dart | 10 +- .../organisms/timer_organism.dart | 7 +- lib/presentation/pages/home_page.dart | 9 +- 14 files changed, 11 insertions(+), 219 deletions(-) delete mode 100644 lib/domain/background_service_controller.dart delete mode 100644 lib/infrastructure/background_service_repository.dart diff --git a/android/app/build.gradle b/android/app/build.gradle index 534483c..ad9aaa5 100644 --- a/android/app/build.gradle +++ b/android/app/build.gradle @@ -34,7 +34,6 @@ android { ndkVersion flutter.ndkVersion compileOptions { - coreLibraryDesugaringEnabled true sourceCompatibility JavaVersion.VERSION_17 targetCompatibility JavaVersion.VERSION_17 } @@ -55,7 +54,6 @@ android { targetSdkVersion flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName - multiDexEnabled true } signingConfigs { release { @@ -86,8 +84,4 @@ flutter { source '../..' } -dependencies { - implementation "com.google.android.material:material:1.9.0" - implementation "androidx.window:window:1.1.0" - coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.2.2' -} +dependencies {} diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 74b9830..ea97a01 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,10 +2,6 @@ - - -` - - - - BGTaskSchedulerPermittedIdentifiers - - dev.flutter.background.refresh - CADisableMinimumFrameDurationOnPhone CFBundleDevelopmentRegion diff --git a/lib/domain/background_service_controller.dart b/lib/domain/background_service_controller.dart deleted file mode 100644 index f667c35..0000000 --- a/lib/domain/background_service_controller.dart +++ /dev/null @@ -1,35 +0,0 @@ -import 'dart:async'; -import 'dart:io'; -import 'dart:ui'; - -import 'package:flutter/material.dart'; -import 'package:flutter_background_service/flutter_background_service.dart'; -import 'package:infinite_horizons/domain/energy_level.dart'; -import 'package:infinite_horizons/domain/player_controller.dart'; -import 'package:infinite_horizons/domain/preferences_controller.dart'; -import 'package:infinite_horizons/domain/study_type_abstract.dart'; -import 'package:infinite_horizons/domain/study_type_analytical.dart'; -import 'package:infinite_horizons/domain/study_type_creatively.dart'; -import 'package:infinite_horizons/domain/tip.dart'; -import 'package:infinite_horizons/domain/vibration_controller.dart'; -import 'package:infinite_horizons/infrastructure/core/logger.dart'; -import 'package:infinite_horizons/presentation/organisms/timer_organism.dart'; -import 'package:shared_preferences/shared_preferences.dart'; - -part 'package:infinite_horizons/infrastructure/background_service_repository.dart'; - -abstract class BackgroundServiceController { - static BackgroundServiceController? _instance; - - static BackgroundServiceController get instance => - _instance ??= _BackgroundServiceRepository(); - - Future init(); - - Future startService(); - void stopService(); - - void startIterateTimerStates(); - - void stopIterateTimerStates(); -} diff --git a/lib/infrastructure/background_service_repository.dart b/lib/infrastructure/background_service_repository.dart deleted file mode 100644 index ba65038..0000000 --- a/lib/infrastructure/background_service_repository.dart +++ /dev/null @@ -1,128 +0,0 @@ -part of 'package:infinite_horizons/domain/background_service_controller.dart'; - -class _BackgroundServiceRepository extends BackgroundServiceController { - late FlutterBackgroundService service; - bool supportedPlatform = true; - - @override - Future init() async { - if (!Platform.isAndroid && !Platform.isIOS) { - supportedPlatform = false; - return; - } - service = FlutterBackgroundService(); - - service.configure( - iosConfiguration: IosConfiguration( - onForeground: onStart, - autoStart: false, - ), - androidConfiguration: AndroidConfiguration( - onStart: onStart, - isForegroundMode: true, - autoStart: false, - ), - ); - } - - @override - Future startService() => service.startService(); - - @override - void stopService() => service.invoke('stopService'); - - @override - void startIterateTimerStates() => service.invoke( - 'startIterateTimerStates', - // { - // 'tipType': tipType.name, - // 'energyType': energyType.name, - // 'timerState': timerState.name, - // 'remainingTime': 'remainingTime', - // } - ); - - @override - void stopIterateTimerStates() { - // TODO: implement stopIterateTimerStates - } - - @pragma('vm:entry-point') - static Future onIosBackground(ServiceInstance service) async { - WidgetsFlutterBinding.ensureInitialized(); - DartPluginRegistrant.ensureInitialized(); - - final SharedPreferences preferences = await SharedPreferences.getInstance(); - await preferences.reload(); - - return true; - } - - @pragma('vm:entry-point') - static Future onStart(ServiceInstance service) async { - WidgetsFlutterBinding.ensureInitialized(); - DartPluginRegistrant.ensureInitialized(); - PlayerController.instance.init(); - service.on('stopService').listen((event) { - // PreferencesController.instance - // .setString('timerState', TimerStateManager.state.name); - // PreferencesController.instance.setDuration( - // 'remainingTimerTime', - // TimerStateManager.getRemainingTime(), - // ); - - PreferencesController.instance.setDuration( - 'remainingTimerTime', - TimerStateManager.getRemainingTime() ?? Duration.zero, - ); - service.stopSelf(); - }); - - service.on('startIterateTimerStates').listen((event) async { - await PreferencesController.instance.init(); - await PreferencesController.instance.reload(); - await VibrationController.instance.init(); - PlayerController.instance.init(); - - final TimerState state = TimerStateExtension.fromString( - PreferencesController.instance.getString('timerState') ?? '', - ); - TimerStateManager.state = state; - - final Duration remainingTime = - PreferencesController.instance.getDuration('remainingTimerTime') ?? - Duration.zero; - final TipType selectedType = TipTypeExtension.fromString( - PreferencesController.instance.getString('tipType') ?? '', - ); - StudyTypeAbstract.instance = selectedType == TipType.analytical - ? StudyTypeAnalytical() - : StudyTypeCreatively(); - - final EnergyType energy = EnergyTypeExtension.fromString( - PreferencesController.instance.getString('energyType') ?? '', - ); - - StudyTypeAbstract.instance!.setTimerStates(energy); - - TimerStateManager.callback = () async { - final TimerState currentState = TimerStateManager.state; - PreferencesController.instance - .setString('timerState', currentState.name); - - if (currentState != TimerState.readyToStart) { - return; - } - PreferencesController.instance.setDuration( - 'remainingTimerTime', - TimerStateManager.getRemainingTime() ?? Duration.zero, - ); - await Future.delayed(const Duration(seconds: 5)); - logger.i('Kill process'); - service.stopSelf(); - }; - - TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); - }); - } -} diff --git a/lib/infrastructure/player_repository.dart b/lib/infrastructure/player_repository.dart index a1df847..fdf755a 100644 --- a/lib/infrastructure/player_repository.dart +++ b/lib/infrastructure/player_repository.dart @@ -6,9 +6,7 @@ class _PlayerRepository extends PlayerController { bool _isSound = true; @override - void init() { - player = AudioPlayer(); - } + void init() => player = AudioPlayer(); @override Future play(String fileName) async { diff --git a/lib/main.dart b/lib/main.dart index 670b935..89688f2 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,5 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; -import 'package:infinite_horizons/domain/background_service_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; @@ -13,7 +12,6 @@ void main() async { PlayerController.instance.init(); await VibrationController.instance.init(); await EasyLocalization.ensureInitialized(); - BackgroundServiceController.instance.init(); runApp( EasyLocalization( diff --git a/lib/presentation/atoms/progress_indicator_atom.dart b/lib/presentation/atoms/progress_indicator_atom.dart index bd7b68b..30a6d7e 100644 --- a/lib/presentation/atoms/progress_indicator_atom.dart +++ b/lib/presentation/atoms/progress_indicator_atom.dart @@ -5,7 +5,6 @@ import 'package:liquid_progress_indicator_v2/liquid_progress_indicator.dart'; class ProgressIndicatorAtom extends StatefulWidget { const ProgressIndicatorAtom({ required this.totalDuration, - required this.callback, this.inputController, this.isPdfLoader = false, this.centerWidget, @@ -13,7 +12,6 @@ class ProgressIndicatorAtom extends StatefulWidget { }); final Duration totalDuration; - final VoidCallback callback; final AnimationController? inputController; final bool isPdfLoader; final Widget? centerWidget; @@ -47,11 +45,6 @@ class _ProgressIndicatorAtomState extends State setState(() {}); }); - controller.addStatusListener((status) { - if (status == AnimationStatus.completed) { - widget.callback(); - } - }); if (!widget.isPdfLoader) { controller.forward(); } diff --git a/lib/presentation/atoms/timer_atom.dart b/lib/presentation/atoms/timer_atom.dart index 391ec0d..18a4097 100644 --- a/lib/presentation/atoms/timer_atom.dart +++ b/lib/presentation/atoms/timer_atom.dart @@ -5,11 +5,10 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; class TimerAtom extends StatelessWidget { - const TimerAtom(this.controller, this.timer, this.callback); + const TimerAtom(this.controller, this.timer); final CustomTimerController controller; final Duration timer; - final VoidCallback callback; @override Widget build(BuildContext context) { diff --git a/lib/presentation/molecules/pdf_viewer_molecule.dart b/lib/presentation/molecules/pdf_viewer_molecule.dart index c00562b..8a22217 100644 --- a/lib/presentation/molecules/pdf_viewer_molecule.dart +++ b/lib/presentation/molecules/pdf_viewer_molecule.dart @@ -36,7 +36,6 @@ class _PdfViewerMoleculeState extends State child: Center( child: ProgressIndicatorAtom( totalDuration: const Duration(seconds: 100), - callback: () {}, inputController: controller, isPdfLoader: true, centerWidget: TextAtom( diff --git a/lib/presentation/molecules/progress_indicator_molecule.dart b/lib/presentation/molecules/progress_indicator_molecule.dart index 1864456..b6a148f 100644 --- a/lib/presentation/molecules/progress_indicator_molecule.dart +++ b/lib/presentation/molecules/progress_indicator_molecule.dart @@ -7,12 +7,10 @@ import 'package:infinite_horizons/presentation/atoms/atoms.dart'; class ProgressIndicatorMolecule extends StatelessWidget { const ProgressIndicatorMolecule({ required this.duration, - required this.onComplete, this.initialValue, }); final Duration duration; - final VoidCallback onComplete; final Duration? initialValue; @override @@ -52,7 +50,6 @@ class ProgressIndicatorMolecule extends StatelessWidget { ), ProgressIndicatorAtom( totalDuration: duration, - callback: onComplete, initialValue: initialValue, ), ], diff --git a/lib/presentation/molecules/timer_molecule.dart b/lib/presentation/molecules/timer_molecule.dart index 6f51298..dcf0d9d 100644 --- a/lib/presentation/molecules/timer_molecule.dart +++ b/lib/presentation/molecules/timer_molecule.dart @@ -4,10 +4,9 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; class TimerMolecule extends StatefulWidget { - const TimerMolecule(this.onComplete, this.duration, {this.initialValue}); + const TimerMolecule(this.duration, {this.initialValue}); final Duration duration; - final VoidCallback onComplete; final Duration? initialValue; @override @@ -27,12 +26,6 @@ class _TimerMoleculeState extends State end: Duration.zero, ); controller.start(); - // TODO: Send selected time and timer start to background process - controller.addListener(() { - if (controller.state.value == CustomTimerState.finished) { - widget.onComplete(); - } - }); } @override @@ -50,7 +43,6 @@ class _TimerMoleculeState extends State child: TimerAtom( controller, widget.duration, - widget.onComplete, ), ), ), diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index 2b33432..d76d89e 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -93,10 +93,9 @@ class TimerOrganismState extends State { @override void initState() { super.initState(); - final PreferencesController prefs = PreferencesController.instance; - final bool lockScreen = prefs.getBool("isLockScreen") ?? true; + final bool lockScreen = + PreferencesController.instance.getBool("isLockScreen") ?? true; WakeLockController.instance.setWakeLock(lockScreen); - PlayerController.instance.setIsSound(prefs.getBool("isSound") ?? true); if (lockScreen) { WakeLockController.instance.setWakeLock(true); @@ -121,7 +120,6 @@ class TimerOrganismState extends State { case TimerState.study: case TimerState.breakTime: return TimerMolecule( - () {}, TimerStateManager.getTimerDuration(), initialValue: TimerStateManager.getRemainingTime(), ); @@ -131,7 +129,6 @@ class TimerOrganismState extends State { totalTime - (TimerStateManager.getRemainingTime() ?? totalTime); return ProgressIndicatorMolecule( duration: totalTime, - onComplete: () {}, initialValue: timePassed, ); case TimerState.readyToStart: diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index bc58b42..9e7994e 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -1,7 +1,6 @@ import 'dart:io'; import 'package:flutter/material.dart'; -import 'package:infinite_horizons/domain/background_service_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; @@ -30,6 +29,8 @@ class _HomePageState extends State with WidgetsBindingObserver { void initState() { super.initState(); WidgetsBinding.instance.addObserver(this); + PlayerController.instance + .setIsSound(PreferencesController.instance.getBool("isSound") ?? true); PlayerController.instance.play('start_session.wav'); VibrationController.instance.vibrate(VibrationType.heavy); TimerStateManager.iterateOverTimerStates(); @@ -45,7 +46,7 @@ class _HomePageState extends State with WidgetsBindingObserver { case AppLifecycleState.resumed: // TODO: Check for ios if (Platform.isAndroid) { - BackgroundServiceController.instance.stopService(); + // BackgroundServiceController.instance.stopService(); await Future.delayed(const Duration(milliseconds: 200)); } await PreferencesController.instance.reload(); @@ -77,8 +78,8 @@ class _HomePageState extends State with WidgetsBindingObserver { if (!Platform.isAndroid) { return; } - await BackgroundServiceController.instance.startService(); - BackgroundServiceController.instance.startIterateTimerStates(); + // await BackgroundServiceController.instance.startService(); + // BackgroundServiceController.instance.startIterateTimerStates(); return; } } From 365ffbcb7e53b75b307e9b45c4d33b49cf9b0dcc Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Tue, 11 Jun 2024 15:53:19 +0300 Subject: [PATCH 09/16] WIP added notification support --- android/app/src/main/AndroidManifest.xml | 1 + ios/Podfile | 14 +++ lib/domain/notifications_controller.dart | 15 +++ .../notifications_repository.dart | 100 ++++++++++++++++++ lib/main.dart | 2 + linux/flutter/generated_plugin_registrant.cc | 8 ++ linux/flutter/generated_plugins.cmake | 2 + pubspec.yaml | 5 +- .../flutter/generated_plugin_registrant.cc | 6 ++ windows/flutter/generated_plugins.cmake | 2 + 10 files changed, 152 insertions(+), 3 deletions(-) create mode 100644 lib/domain/notifications_controller.dart create mode 100644 lib/infrastructure/notifications_repository.dart diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index ea97a01..5e7eb46 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -2,6 +2,7 @@ + + _instance ??= _NotificationsRepository(); + + void init(); + + void send(); +} diff --git a/lib/infrastructure/notifications_repository.dart b/lib/infrastructure/notifications_repository.dart new file mode 100644 index 0000000..e67e677 --- /dev/null +++ b/lib/infrastructure/notifications_repository.dart @@ -0,0 +1,100 @@ +part of 'package:infinite_horizons/domain/notifications_controller.dart'; + +class _NotificationsRepository extends NotificationsController { + late AwesomeNotifications controller; + + @override + void init() { + controller = AwesomeNotifications(); + controller.initialize( + // set the icon to null if you want to use the default app icon + // 'resource://drawable/res_app_icon', + null, + [ + NotificationChannel( + channelGroupKey: 'basic_channel_group', + channelKey: 'basic_channel', + channelName: 'Basic notifications', + channelDescription: 'Notification channel for basic tests', + defaultColor: const Color(0xFF9D50DD), + ledColor: Colors.white, + ), + ], + // Channel groups are only visual and are not required + channelGroups: [ + NotificationChannelGroup( + channelGroupKey: 'basic_channel_group', + channelGroupName: 'Basic group', + ), + ], + debug: true, + ); + + controller.requestPermissionToSendNotifications(); + // controller.setListeners( + // onActionReceivedMethod: (ReceivedAction receivedAction){ + // onActionReceivedMethod(context, receivedAction); + // }, + // onNotificationCreatedMethod: (ReceivedNotification receivedNotification){ + // onNotificationCreatedMethod(context, receivedNotification); + // }, + // onNotificationDisplayedMethod: (ReceivedNotification receivedNotification){ + // onNotificationDisplayedMethod(context, receivedNotification); + // }, + // onDismissActionReceivedMethod: (ReceivedAction receivedAction){ + // onDismissActionReceivedMethod(context, receivedAction); + // }, + // ); + } + + @override + void send() { + controller.createNotification( + content: NotificationContent( + id: 10, + channelKey: 'basic_channel', + title: 'Hello World!', + body: 'This is my first notification!', + ), + ); + } + + /// Use this method to detect when a new notification or a schedule is created + @pragma("vm:entry-point") + static Future onNotificationCreatedMethod( + ReceivedNotification receivedNotification, + ) async { + // Your code goes here + } + + /// Use this method to detect every time that a new notification is displayed + @pragma("vm:entry-point") + static Future onNotificationDisplayedMethod( + ReceivedNotification receivedNotification, + ) async { + // Your code goes here + } + + /// Use this method to detect if the user dismissed a notification + @pragma("vm:entry-point") + static Future onDismissActionReceivedMethod( + ReceivedAction receivedAction, + ) async { + // Your code goes here + } + + /// Use this method to detect when the user taps on a notification or action button + @pragma("vm:entry-point") + static Future onActionReceivedMethod( + ReceivedAction receivedAction, + ) async { + // Your code goes here + + // Navigate into pages, avoiding to open the notification details page over another details page already opened + // MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil( + // '/notification-page', + // (route) => + // (route.settings.name != '/notification-page') || route.isFirst, + // arguments: receivedAction); + } +} diff --git a/lib/main.dart b/lib/main.dart index 89688f2..5680d62 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,5 +1,6 @@ import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; @@ -12,6 +13,7 @@ void main() async { PlayerController.instance.init(); await VibrationController.instance.init(); await EasyLocalization.ensureInitialized(); + NotificationsController.instance.init(); runApp( EasyLocalization( diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index cc10c4d..309cb23 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,12 +7,20 @@ #include "generated_plugin_registrant.h" #include +#include +#include #include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) audioplayers_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "AudioplayersLinuxPlugin"); audioplayers_linux_plugin_register_with_registrar(audioplayers_linux_registrar); + g_autoptr(FlPluginRegistrar) awesome_notifications_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsPlugin"); + awesome_notifications_plugin_register_with_registrar(awesome_notifications_registrar); + g_autoptr(FlPluginRegistrar) awesome_notifications_core_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "AwesomeNotificationsCorePlugin"); + awesome_notifications_core_plugin_register_with_registrar(awesome_notifications_core_registrar); g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index 8e2a190..cc6c6e4 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,8 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_linux + awesome_notifications + awesome_notifications_core url_launcher_linux ) diff --git a/pubspec.yaml b/pubspec.yaml index 7d1ca1d..c835354 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -9,6 +9,8 @@ environment: dependencies: animated_toggle_switch: ^0.8.2 audioplayers: ^6.0.0 + awesome_notifications: ^0.9.3+1 + awesome_notifications_core: ^0.9.3 collection: ^1.18.0 # Blast confetti confetti: ^0.7.0 @@ -17,15 +19,12 @@ dependencies: # Make an animated circular countdown custom_timer: ^0.2.3 # Internationalization (Easy translations) - device_info_plus: ^10.1.0 easy_localization: ^3.0.6 flutter: sdk: flutter - flutter_background_service: ^5.0.5 flutter_cached_pdfview: ^0.4.2 flutter_dnd: ^0.1.4+1 flutter_inappwebview: ^6.0.0 - flutter_local_notifications: ^17.1.2 flutter_vibrate: ^1.3.0 # Font Awesome Icon pack available as Flutter Icons. font_awesome_flutter: ^10.7.0 diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 43d432f..68a7227 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -7,11 +7,17 @@ #include "generated_plugin_registrant.h" #include +#include +#include #include void RegisterPlugins(flutter::PluginRegistry* registry) { AudioplayersWindowsPluginRegisterWithRegistrar( registry->GetRegistrarForPlugin("AudioplayersWindowsPlugin")); + AwesomeNotificationsPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AwesomeNotificationsPluginCApi")); + AwesomeNotificationsCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AwesomeNotificationsCorePluginCApi")); UrlLauncherWindowsRegisterWithRegistrar( registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 1772613..1d07737 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -4,6 +4,8 @@ list(APPEND FLUTTER_PLUGIN_LIST audioplayers_windows + awesome_notifications + awesome_notifications_core url_launcher_windows ) From bbadf249690dde60fb6c9be4ef9b383a1e9800c9 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Mon, 17 Jun 2024 18:31:34 +0300 Subject: [PATCH 10/16] wif Notifications works --- android/app/src/main/AndroidManifest.xml | 2 +- lib/domain/energy_level.dart | 4 +- lib/domain/notifications_controller.dart | 11 +- lib/domain/preferences_controller.dart | 4 + .../notifications_repository.dart | 110 +++++++++--------- .../preferences_repository.dart | 6 + lib/main.dart | 3 + .../study_type_selection_molecule.dart | 9 ++ .../organisms/timer_organism.dart | 34 ++++-- lib/presentation/pages/home_page.dart | 63 +++++----- 10 files changed, 147 insertions(+), 99 deletions(-) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 5e7eb46..23159fc 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -3,7 +3,7 @@ - + + controller.createNotification( + schedule: NotificationCalendar.fromDate( + date: date, + allowWhileIdle: true, + preciseAlarm: true, + ), + content: NotificationContent( + id: notificationIdCounter++, + channelKey: 'basic_channel', + title: title, + body: body, + criticalAlert: true, + wakeUpScreen: true, + ), + ); + + @override + Future getNotifications() async { + final List a = + await controller.listScheduledNotifications(); + + logger.i('All notifications $a'); } + @override + Future generalPermission() async => + controller.requestPermissionToSendNotifications(); + + @override + Future preciseAlarmPermission() async => + await AwesomeNotifications().showAlarmPage(); + + /// Use this method to detect when the user taps on a notification or action button + /// Also capture when there is a message from firebase messaging + @pragma("vm:entry-point") + static Future onActionReceivedMethod( + ReceivedAction receivedAction, + ) async {} + /// Use this method to detect when a new notification or a schedule is created @pragma("vm:entry-point") static Future onNotificationCreatedMethod( ReceivedNotification receivedNotification, - ) async { - // Your code goes here - } + ) async {} /// Use this method to detect every time that a new notification is displayed @pragma("vm:entry-point") static Future onNotificationDisplayedMethod( ReceivedNotification receivedNotification, - ) async { - // Your code goes here - } + ) async {} /// Use this method to detect if the user dismissed a notification @pragma("vm:entry-point") static Future onDismissActionReceivedMethod( ReceivedAction receivedAction, - ) async { - // Your code goes here - } - - /// Use this method to detect when the user taps on a notification or action button - @pragma("vm:entry-point") - static Future onActionReceivedMethod( - ReceivedAction receivedAction, - ) async { - // Your code goes here - - // Navigate into pages, avoiding to open the notification details page over another details page already opened - // MyApp.navigatorKey.currentState?.pushNamedAndRemoveUntil( - // '/notification-page', - // (route) => - // (route.settings.name != '/notification-page') || route.isFirst, - // arguments: receivedAction); - } + ) async {} } diff --git a/lib/infrastructure/preferences_repository.dart b/lib/infrastructure/preferences_repository.dart index 0485b3b..a7396d4 100644 --- a/lib/infrastructure/preferences_repository.dart +++ b/lib/infrastructure/preferences_repository.dart @@ -12,6 +12,9 @@ class _PreferencesRepository extends PreferencesController { @override String? getString(String key) => preferences.getString(key); + @override + int? getInt(String key) => preferences.getInt(key); + @override bool? getBool(String key) => preferences.getBool(key); @@ -27,6 +30,9 @@ class _PreferencesRepository extends PreferencesController { @override void setString(String key, String value) => preferences.setString(key, value); + @override + void setInt(String key, int value) => preferences.setInt(key, value); + @override void setBool(String key, bool value) => preferences.setBool(key, value); diff --git a/lib/main.dart b/lib/main.dart index 5680d62..b1cee16 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -14,6 +14,9 @@ void main() async { await VibrationController.instance.init(); await EasyLocalization.ensureInitialized(); NotificationsController.instance.init(); + final int loginCounter = + PreferencesController.instance.getInt('loginCounter') ?? 0; + PreferencesController.instance.setInt('loginCounter', loginCounter + 1); runApp( EasyLocalization( diff --git a/lib/presentation/molecules/study_type_selection_molecule.dart b/lib/presentation/molecules/study_type_selection_molecule.dart index 0b21161..2b9fec1 100644 --- a/lib/presentation/molecules/study_type_selection_molecule.dart +++ b/lib/presentation/molecules/study_type_selection_molecule.dart @@ -1,5 +1,6 @@ import 'package:collection/collection.dart'; import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/study_type_analytical.dart'; @@ -27,6 +28,14 @@ class _StudyTypeSelectionMoleculeState void initState() { super.initState(); selectedType = StudyTypeAbstract.instance?.tipType ?? TipType.undefined; + if (PreferencesController.instance.getInt('loginCounter')! <= 1) { + requestNotificationPermissions(); + } + } + + Future requestNotificationPermissions() async { + await NotificationsController.instance.generalPermission(); + await NotificationsController.instance.preciseAlarmPermission(); } void onChanged(TipType? type) { diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index d76d89e..70955ec 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -20,25 +20,35 @@ class TimerStateManager { static Duration _remainingTime = Duration.zero; static void incrementState() { + state = getNextState(state); switch (state) { case TimerState.study: - state = TimerState.getReadyForBreak; + timerStates.promoteSession(); + PlayerController.instance.play('start_session.wav'); + VibrationController.instance.vibrate(VibrationType.heavy); + case TimerState.getReadyForBreak: PlayerController.instance.play('session_completed.wav'); VibrationController.instance.vibrate(VibrationType.medium); - case TimerState.getReadyForBreak: - state = TimerState.breakTime; case TimerState.breakTime: - state = TimerState.readyToStart; + case TimerState.readyToStart: PlayerController.instance.play('break_ended.wav'); + } + } + + static TimerState getNextState(TimerState state) { + switch (state) { + case TimerState.study: + return TimerState.getReadyForBreak; + case TimerState.getReadyForBreak: + return TimerState.breakTime; + case TimerState.breakTime: + return TimerState.readyToStart; case TimerState.readyToStart: - timerStates.promoteSession(); - PlayerController.instance.play('start_session.wav'); - VibrationController.instance.vibrate(VibrationType.heavy); - state = TimerState.study; + return TimerState.study; } } - static Duration getTimerDuration() { + static Duration getTimerDuration(TimerState state) { switch (state) { case TimerState.study: return timerStates.getCurrentSession().study; @@ -56,8 +66,10 @@ class TimerStateManager { static void pauseTimer() => _timer?.cancel(); + static bool isTimerRunning() => _timer != null && _timer!.isActive; + static Future iterateOverTimerStates({Duration? remainingTime}) async { - final Duration stateDuration = remainingTime ?? getTimerDuration(); + final Duration stateDuration = remainingTime ?? getTimerDuration(state); if (stateDuration == Duration.zero) { return; } @@ -120,7 +132,7 @@ class TimerOrganismState extends State { case TimerState.study: case TimerState.breakTime: return TimerMolecule( - TimerStateManager.getTimerDuration(), + TimerStateManager.getTimerDuration(state), initialValue: TimerStateManager.getRemainingTime(), ); case TimerState.getReadyForBreak: diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 9e7994e..05fa5a6 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -1,9 +1,9 @@ -import 'dart:io'; - import 'package:flutter/material.dart'; +import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; +import 'package:infinite_horizons/infrastructure/core/logger.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; @@ -44,46 +44,55 @@ class _HomePageState extends State with WidgetsBindingObserver { case AppLifecycleState.hidden: return; case AppLifecycleState.resumed: - // TODO: Check for ios - if (Platform.isAndroid) { - // BackgroundServiceController.instance.stopService(); - await Future.delayed(const Duration(milliseconds: 200)); - } - await PreferencesController.instance.reload(); - final TimerState state = TimerStateExtension.fromString( - PreferencesController.instance.getString('timerState') ?? '', - ); - TimerStateManager.state = state; - - final Duration remainingTime = - PreferencesController.instance.getDuration('remainingTimerTime') ?? - Duration.zero; - TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); - timerKey.currentState?.setCurrentState(); + // + // await PreferencesController.instance.reload(); + // final TimerState state = TimerStateExtension.fromString( + // PreferencesController.instance.getString('timerState') ?? '', + // ); + // TimerStateManager.state = state; + // + // final Duration remainingTime = + // PreferencesController.instance.getDuration('remainingTimerTime') ?? + // Duration.zero; + // TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); + // timerKey.currentState?.setCurrentState(); return; case AppLifecycleState.paused: - if (TimerStateManager.state == TimerState.readyToStart) { + if (!TimerStateManager.isTimerRunning()) { return; } - TimerStateManager.pauseTimer(); PreferencesController.instance.setDuration( 'remainingTimerTime', TimerStateManager.getRemainingTime() ?? Duration.zero, ); - PreferencesController.instance .setString('timerState', TimerStateManager.state.name); - // TODO: Check how it react - if (!Platform.isAndroid) { - return; - } - // await BackgroundServiceController.instance.startService(); - // BackgroundServiceController.instance.startIterateTimerStates(); + + createNotificationsForStates(); return; } } + Future createNotificationsForStates() async { + Duration durationForState = + TimerStateManager.getRemainingTime() ?? Duration.zero; + + DateTime time = DateTime.now(); + TimerState tempState = TimerStateManager.state; + + while (durationForState != Duration.zero) { + time = time.add(durationForState); + if (tempState != TimerState.getReadyForBreak) { + await NotificationsController.instance + .send(date: time, title: 'State: $tempState', body: 'asd'); + logger.i('Time for notification $tempState is $time'); + } + tempState = TimerStateManager.getNextState(tempState); + durationForState = TimerStateManager.getTimerDuration(tempState); + } + } + @override void dispose() { TimerStateManager.pauseTimer(); From 5537acffe87fb63dc22263bda22ba4551a4a3e14 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Wed, 19 Jun 2024 18:07:25 +0300 Subject: [PATCH 11/16] Added all background notifications logic --- lib/domain/energy_level.dart | 3 +- lib/domain/notifications_controller.dart | 3 +- lib/domain/preferences_controller.dart | 4 + .../notifications_repository.dart | 14 +- .../preferences_repository.dart | 12 ++ .../molecules/timer_molecule.dart | 4 + .../organisms/timer_organism.dart | 73 ++++++++-- lib/presentation/pages/home_page.dart | 132 +++++++++++++----- 8 files changed, 188 insertions(+), 57 deletions(-) diff --git a/lib/domain/energy_level.dart b/lib/domain/energy_level.dart index 5a1c10f..2e2233e 100644 --- a/lib/domain/energy_level.dart +++ b/lib/domain/energy_level.dart @@ -58,6 +58,7 @@ class TimerSession { Duration study; Duration breakDuration; + final Duration getReadyForBreak = const Duration(seconds: 10); } enum EnergyType { @@ -65,7 +66,7 @@ enum EnergyType { 'undefined', Duration.zero, ), - veryLow('very_low', Duration(minutes: 5)), + veryLow('very_low', Duration(seconds: 30)), low('low', Duration(minutes: 10)), pomodoro('Pomodoro', Duration(minutes: 25)), high('high', Duration(minutes: 45), tipsId: ['45m/5m']), diff --git a/lib/domain/notifications_controller.dart b/lib/domain/notifications_controller.dart index 0e2371a..fc77d9e 100644 --- a/lib/domain/notifications_controller.dart +++ b/lib/domain/notifications_controller.dart @@ -1,6 +1,5 @@ import 'package:awesome_notifications/awesome_notifications.dart'; import 'package:flutter/material.dart'; -import 'package:infinite_horizons/infrastructure/core/logger.dart'; part 'package:infinite_horizons/infrastructure/notifications_repository.dart'; @@ -18,7 +17,7 @@ abstract class NotificationsController { String? body, }); - Future getNotifications(); Future generalPermission(); Future preciseAlarmPermission(); + Future cancelAllNotifications(); } diff --git a/lib/domain/preferences_controller.dart b/lib/domain/preferences_controller.dart index 5f6206a..8316175 100644 --- a/lib/domain/preferences_controller.dart +++ b/lib/domain/preferences_controller.dart @@ -19,6 +19,8 @@ abstract class PreferencesController { Duration? getDuration(String key); + DateTime? getDateTime(String key); + void remove(String key); void setString(String key, String value); @@ -28,4 +30,6 @@ abstract class PreferencesController { void setBool(String key, bool value); void setDuration(String key, Duration value); + + void setDateTime(String key, DateTime value); } diff --git a/lib/infrastructure/notifications_repository.dart b/lib/infrastructure/notifications_repository.dart index d206667..f4f02e8 100644 --- a/lib/infrastructure/notifications_repository.dart +++ b/lib/infrastructure/notifications_repository.dart @@ -54,20 +54,14 @@ class _NotificationsRepository extends NotificationsController { ); @override - Future getNotifications() async { - final List a = - await controller.listScheduledNotifications(); - - logger.i('All notifications $a'); - } + Future generalPermission() => + controller.requestPermissionToSendNotifications(); @override - Future generalPermission() async => - controller.requestPermissionToSendNotifications(); + Future preciseAlarmPermission() => controller.showAlarmPage(); @override - Future preciseAlarmPermission() async => - await AwesomeNotifications().showAlarmPage(); + Future cancelAllNotifications() => controller.cancelAll(); /// Use this method to detect when the user taps on a notification or action button /// Also capture when there is a message from firebase messaging diff --git a/lib/infrastructure/preferences_repository.dart b/lib/infrastructure/preferences_repository.dart index a7396d4..4404f63 100644 --- a/lib/infrastructure/preferences_repository.dart +++ b/lib/infrastructure/preferences_repository.dart @@ -24,6 +24,14 @@ class _PreferencesRepository extends PreferencesController { return milliseconds != null ? Duration(milliseconds: milliseconds) : null; } + @override + DateTime? getDateTime(String key) { + final int? milliseconds = preferences.getInt(key); + return milliseconds != null + ? DateTime.fromMillisecondsSinceEpoch(milliseconds) + : null; + } + @override void remove(String key) => preferences.remove(key); @@ -39,4 +47,8 @@ class _PreferencesRepository extends PreferencesController { @override void setDuration(String key, Duration value) => preferences.setInt(key, value.inMilliseconds); + + @override + void setDateTime(String key, DateTime value) => + preferences.setInt(key, value.millisecondsSinceEpoch); } diff --git a/lib/presentation/molecules/timer_molecule.dart b/lib/presentation/molecules/timer_molecule.dart index dcf0d9d..0cea5e7 100644 --- a/lib/presentation/molecules/timer_molecule.dart +++ b/lib/presentation/molecules/timer_molecule.dart @@ -20,6 +20,10 @@ class _TimerMoleculeState extends State @override void initState() { super.initState(); + setController(); + } + + void setController() { controller = CustomTimerController( vsync: this, begin: widget.initialValue ?? widget.duration, diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index 70955ec..3684d5c 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -14,7 +14,7 @@ import 'package:infinite_horizons/presentation/pages/pages.dart'; class TimerStateManager { static TimerState state = TimerState.study; static EnergyLevel timerStates = StudyTypeAbstract.instance!.getTimerStates(); - static Duration getReadyDuration = const Duration(seconds: 10); + static Timer? _timer; static VoidCallback? callback; static Duration _remainingTime = Duration.zero; @@ -53,7 +53,7 @@ class TimerStateManager { case TimerState.study: return timerStates.getCurrentSession().study; case TimerState.getReadyForBreak: - return getReadyDuration; + return timerStates.getCurrentSession().getReadyForBreak; case TimerState.breakTime: return timerStates.getCurrentSession().breakDuration; case TimerState.readyToStart: @@ -61,8 +61,11 @@ class TimerStateManager { } } - static Duration? getRemainingTime() => - _remainingTime == Duration.zero ? null : _remainingTime; + static Duration? get remainingTime => + _remainingTime <= Duration.zero ? null : _remainingTime; + + static set remainingTime(Duration? value) => + _remainingTime = value ?? Duration.zero; static void pauseTimer() => _timer?.cancel(); @@ -90,6 +93,41 @@ class TimerStateManager { }, ); } + + static List upcomingStates( + TimerState fromState, + Duration? remainingTimeForState, { + DateTime? calculateFromDate, + }) { + final List statesWithTime = []; + + Duration durationForState = remainingTimeForState ?? Duration.zero; + + DateTime endTime = calculateFromDate ?? DateTime.now(); + TimerState tempState = fromState; + + while (durationForState != Duration.zero) { + endTime = endTime.add(durationForState); + statesWithTime.add(UpcomingState(tempState, endTime, durationForState)); + tempState = getNextState(tempState); + durationForState = getTimerDuration(tempState); + } + endTime = endTime.add(durationForState); + statesWithTime.add(UpcomingState(tempState, endTime, durationForState)); + + return statesWithTime; + } +} + +/// Class representing an upcoming timer state with its corresponding time. +class UpcomingState { + UpcomingState(this.state, this.endTime, this.duration); + + final TimerState state; + final DateTime endTime; + final Duration duration; + + DateTime startTime() => endTime.subtract(duration); } class TimerOrganism extends StatefulWidget { @@ -101,7 +139,7 @@ class TimerOrganism extends StatefulWidget { class TimerOrganismState extends State { TimerState state = TimerStateManager.state; - + bool renderSizedBox = false; @override void initState() { super.initState(); @@ -121,8 +159,23 @@ class TimerOrganismState extends State { super.dispose(); } - void setCurrentState() { + Future setCurrentState() async { + if (state == TimerState.study && + TimerStateManager.state == TimerState.breakTime) { + setState(() { + renderSizedBox = true; + }); + WidgetsBinding.instance.addPostFrameCallback((_) { + setState(() { + renderSizedBox = false; + state = TimerStateManager.state; + }); + }); + return; + } + setState(() { + renderSizedBox = false; state = TimerStateManager.state; }); } @@ -133,12 +186,12 @@ class TimerOrganismState extends State { case TimerState.breakTime: return TimerMolecule( TimerStateManager.getTimerDuration(state), - initialValue: TimerStateManager.getRemainingTime(), + initialValue: TimerStateManager.remainingTime, ); case TimerState.getReadyForBreak: - final Duration totalTime = TimerStateManager.getReadyDuration; + final Duration totalTime = TimerStateManager.getTimerDuration(state); final Duration timePassed = - totalTime - (TimerStateManager.getRemainingTime() ?? totalTime); + totalTime - (TimerStateManager.remainingTime ?? totalTime); return ProgressIndicatorMolecule( duration: totalTime, initialValue: timePassed, @@ -171,7 +224,7 @@ class TimerOrganismState extends State { scaffold: false, expendChild: false, topBarRightOnTap: () => openAlertDialog(context, SettingsPage()), - child: stateWidget(), + child: renderSizedBox ? const SizedBox() : stateWidget(), ); } } diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 05fa5a6..6a35615 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -2,8 +2,8 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; +import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; -import 'package:infinite_horizons/infrastructure/core/logger.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; @@ -44,53 +44,74 @@ class _HomePageState extends State with WidgetsBindingObserver { case AppLifecycleState.hidden: return; case AppLifecycleState.resumed: - // - // await PreferencesController.instance.reload(); - // final TimerState state = TimerStateExtension.fromString( - // PreferencesController.instance.getString('timerState') ?? '', - // ); - // TimerStateManager.state = state; - // - // final Duration remainingTime = - // PreferencesController.instance.getDuration('remainingTimerTime') ?? - // Duration.zero; - // TimerStateManager.iterateOverTimerStates(remainingTime: remainingTime); - // timerKey.currentState?.setCurrentState(); + NotificationsController.instance.cancelAllNotifications(); + + final DateTime preferencePausedTime = + PreferencesController.instance.getDateTime('pausedTime')!; + final TimerState preferenceTimerState = TimerStateExtension.fromString( + PreferencesController.instance.getString('timerState') ?? '', + ); + final Duration preferenceRemainingTimerTime = + PreferencesController.instance.getDuration('remainingTimerTime') ?? + Duration.zero; + + setCurrentStateAndRemainingTime( + preferencePausedTime, + preferenceTimerState, + preferenceRemainingTimerTime, + ); + TimerStateManager.callback?.call(); + TimerStateManager.iterateOverTimerStates( + remainingTime: TimerStateManager.remainingTime, + ); return; case AppLifecycleState.paused: + PreferencesController.instance + .setDateTime('pausedTime', DateTime.now()); + PreferencesController.instance + .setString('timerState', TimerStateManager.state.name); + PreferencesController.instance.setDuration( + 'remainingTimerTime', + TimerStateManager.remainingTime ?? Duration.zero, + ); + if (!TimerStateManager.isTimerRunning()) { return; } + TimerStateManager.pauseTimer(); - PreferencesController.instance.setDuration( - 'remainingTimerTime', - TimerStateManager.getRemainingTime() ?? Duration.zero, + final upcomingStates = TimerStateManager.upcomingStates( + TimerStateManager.state, + TimerStateManager.remainingTime, ); - PreferencesController.instance - .setString('timerState', TimerStateManager.state.name); - - createNotificationsForStates(); + for (final UpcomingState stateWithTime in upcomingStates) { + if (stateWithTime.state != TimerState.getReadyForBreak && + stateWithTime.state != TimerState.readyToStart) { + await NotificationsController.instance.send( + date: stateWithTime.endTime, + title: 'State: ${stateWithTime.state}', + ); + } + } return; } } - Future createNotificationsForStates() async { - Duration durationForState = - TimerStateManager.getRemainingTime() ?? Duration.zero; + UpcomingState findLastUpcomingStateBeforeNow( + List upcomingStates, + ) { + final DateTime currentTime = DateTime.now(); + UpcomingState? lastStateBeforeNow; - DateTime time = DateTime.now(); - TimerState tempState = TimerStateManager.state; - - while (durationForState != Duration.zero) { - time = time.add(durationForState); - if (tempState != TimerState.getReadyForBreak) { - await NotificationsController.instance - .send(date: time, title: 'State: $tempState', body: 'asd'); - logger.i('Time for notification $tempState is $time'); + for (final UpcomingState state in upcomingStates) { + if (state.endTime.isBefore(currentTime)) { + lastStateBeforeNow = state; + } else { + break; } - tempState = TimerStateManager.getNextState(tempState); - durationForState = TimerStateManager.getTimerDuration(tempState); } + + return lastStateBeforeNow ?? upcomingStates.first; } @override @@ -128,4 +149,47 @@ class _HomePageState extends State with WidgetsBindingObserver { BottomNavigationBarHomePage(callback, _currentTabNum), ); } + + Duration getStateDuration(TimerState state) { + // TODO: Fix app not taking into account session number + return StudyTypeAbstract.instance!.getTimerStates().sessions.map((session) { + if (state == TimerState.study) { + return session.study; + } else if (state == TimerState.breakTime) { + return session.breakDuration; + } + return session.getReadyForBreak; + }).first; + } + + /// Set the current state and the remaining time by calculating how much time passed + void setCurrentStateAndRemainingTime( + DateTime pausedTime, + TimerState previousTimerState, + Duration previousRemainingTimerTime, + ) { + final List upcomingStates = TimerStateManager.upcomingStates( + previousTimerState, + previousRemainingTimerTime, + calculateFromDate: pausedTime, + ); + final DateTime timeNow = DateTime.now(); + + UpcomingState upcomingState = upcomingStates.first; + + for (final UpcomingState tempUpcomingState in upcomingStates) { + if (tempUpcomingState.startTime().isBefore(timeNow)) { + upcomingState = tempUpcomingState; + } else { + break; + } + } + + Duration remainingDuration = Duration.zero; + if (upcomingStates.last.state != upcomingState.state) { + remainingDuration = upcomingState.endTime.difference(timeNow); + } + TimerStateManager.state = upcomingState.state; + TimerStateManager.remainingTime = remainingDuration; + } } From 0fb8b39d242783b8535b5c3b1337e126c7ffd85c Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Sat, 22 Jun 2024 10:53:32 +0300 Subject: [PATCH 12/16] Added notification types and sound effect on Android --- .../src/main/res/drawable/res_app_icon.png | Bin 0 -> 21003 bytes android/app/src/main/res/raw/break_ended.m4a | Bin 0 -> 100938 bytes .../src/main/res/raw/session_completed.m4a | Bin 0 -> 42814 bytes lib/domain/notifications_controller.dart | 3 +- .../notifications_repository.dart | 36 +++++++++---- .../molecules/timer_molecule.dart | 2 +- .../organisms/timer_organism.dart | 2 +- lib/presentation/pages/home_page.dart | 48 +++++++----------- 8 files changed, 48 insertions(+), 43 deletions(-) create mode 100644 android/app/src/main/res/drawable/res_app_icon.png create mode 100644 android/app/src/main/res/raw/break_ended.m4a create mode 100644 android/app/src/main/res/raw/session_completed.m4a diff --git a/android/app/src/main/res/drawable/res_app_icon.png b/android/app/src/main/res/drawable/res_app_icon.png new file mode 100644 index 0000000000000000000000000000000000000000..1b7764e40852e3a66f3b84f166c33d5842d21045 GIT binary patch literal 21003 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 z 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 literal 0 HcmV?d00001 diff --git a/lib/domain/notifications_controller.dart b/lib/domain/notifications_controller.dart index fc77d9e..7e468b9 100644 --- a/lib/domain/notifications_controller.dart +++ b/lib/domain/notifications_controller.dart @@ -1,5 +1,5 @@ import 'package:awesome_notifications/awesome_notifications.dart'; -import 'package:flutter/material.dart'; +import 'package:infinite_horizons/presentation/core/theme_data.dart'; part 'package:infinite_horizons/infrastructure/notifications_repository.dart'; @@ -14,6 +14,7 @@ abstract class NotificationsController { Future send({ required DateTime date, required String title, + required NotificationVariant variant, String? body, }); diff --git a/lib/infrastructure/notifications_repository.dart b/lib/infrastructure/notifications_repository.dart index f4f02e8..c33a17f 100644 --- a/lib/infrastructure/notifications_repository.dart +++ b/lib/infrastructure/notifications_repository.dart @@ -8,16 +8,24 @@ class _NotificationsRepository extends NotificationsController { void init() { controller = AwesomeNotifications(); controller.initialize( - // set the icon to null if you want to use the default app icon - // 'resource://drawable/res_app_icon', - null, + 'resource://drawable/res_app_icon', [ NotificationChannel( - channelKey: 'basic_channel', - channelName: 'Basic notifications', - channelDescription: 'Notification channel for basic tests', - defaultColor: const Color(0xFF9D50DD), - ledColor: Colors.white, + channelKey: NotificationVariant.studyEnded.channelKey, + channelName: 'Study Ended', + channelDescription: 'Study time ended', + soundSource: 'resource://raw/session_completed', + defaultColor: AppThemeData.logoBackgroundColor, + ledColor: AppThemeData.logoBackgroundColor, + criticalAlerts: true, + ), + NotificationChannel( + channelKey: NotificationVariant.breakEnded.channelKey, + channelName: 'Break Ended', + channelDescription: 'Break time ended', + soundSource: 'resource://raw/break_ended', + defaultColor: AppThemeData.logoBackgroundColor, + ledColor: AppThemeData.logoBackgroundColor, criticalAlerts: true, ), ], @@ -35,6 +43,7 @@ class _NotificationsRepository extends NotificationsController { Future send({ required DateTime date, required String title, + required NotificationVariant variant, String? body, }) async => controller.createNotification( @@ -45,7 +54,7 @@ class _NotificationsRepository extends NotificationsController { ), content: NotificationContent( id: notificationIdCounter++, - channelKey: 'basic_channel', + channelKey: variant.channelKey, title: title, body: body, criticalAlert: true, @@ -88,3 +97,12 @@ class _NotificationsRepository extends NotificationsController { ReceivedAction receivedAction, ) async {} } + +enum NotificationVariant { + studyEnded('study_ended'), + breakEnded('break_ended'), + ; + + const NotificationVariant(this.channelKey); + final String channelKey; +} diff --git a/lib/presentation/molecules/timer_molecule.dart b/lib/presentation/molecules/timer_molecule.dart index 0cea5e7..51a5dcc 100644 --- a/lib/presentation/molecules/timer_molecule.dart +++ b/lib/presentation/molecules/timer_molecule.dart @@ -4,7 +4,7 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/presentation/atoms/atoms.dart'; class TimerMolecule extends StatefulWidget { - const TimerMolecule(this.duration, {this.initialValue}); + const TimerMolecule({required this.duration, required this.initialValue}); final Duration duration; final Duration? initialValue; diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index 3684d5c..abccdc5 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -185,7 +185,7 @@ class TimerOrganismState extends State { case TimerState.study: case TimerState.breakTime: return TimerMolecule( - TimerStateManager.getTimerDuration(state), + duration: TimerStateManager.getTimerDuration(state), initialValue: TimerStateManager.remainingTime, ); case TimerState.getReadyForBreak: diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 6a35615..03cbc60 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -2,7 +2,6 @@ import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; import 'package:infinite_horizons/domain/preferences_controller.dart'; -import 'package:infinite_horizons/domain/study_type_abstract.dart'; import 'package:infinite_horizons/domain/vibration_controller.dart'; import 'package:infinite_horizons/presentation/molecules/molecules.dart'; import 'package:infinite_horizons/presentation/organisms/organisms.dart'; @@ -87,9 +86,25 @@ class _HomePageState extends State with WidgetsBindingObserver { for (final UpcomingState stateWithTime in upcomingStates) { if (stateWithTime.state != TimerState.getReadyForBreak && stateWithTime.state != TimerState.readyToStart) { + String title; + String body; + NotificationVariant notificationVariant; + if (stateWithTime.state == TimerState.study) { + title = 'Take a break'; + body = 'You have completed the Study session, take a break'; + notificationVariant = NotificationVariant.studyEnded; + } else { + title = 'Press to start a new session'; + body = + 'Break ended, enter the app to continue to the next session'; + notificationVariant = NotificationVariant.breakEnded; + } + await NotificationsController.instance.send( date: stateWithTime.endTime, - title: 'State: ${stateWithTime.state}', + title: title, + body: body, + variant: notificationVariant, ); } } @@ -97,23 +112,6 @@ class _HomePageState extends State with WidgetsBindingObserver { } } - UpcomingState findLastUpcomingStateBeforeNow( - List upcomingStates, - ) { - final DateTime currentTime = DateTime.now(); - UpcomingState? lastStateBeforeNow; - - for (final UpcomingState state in upcomingStates) { - if (state.endTime.isBefore(currentTime)) { - lastStateBeforeNow = state; - } else { - break; - } - } - - return lastStateBeforeNow ?? upcomingStates.first; - } - @override void dispose() { TimerStateManager.pauseTimer(); @@ -150,18 +148,6 @@ class _HomePageState extends State with WidgetsBindingObserver { ); } - Duration getStateDuration(TimerState state) { - // TODO: Fix app not taking into account session number - return StudyTypeAbstract.instance!.getTimerStates().sessions.map((session) { - if (state == TimerState.study) { - return session.study; - } else if (state == TimerState.breakTime) { - return session.breakDuration; - } - return session.getReadyForBreak; - }).first; - } - /// Set the current state and the remaining time by calculating how much time passed void setCurrentStateAndRemainingTime( DateTime pausedTime, From a9b4f5c41d8907559d10b48b11164d519345b9a4 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Sat, 22 Jun 2024 12:18:24 +0300 Subject: [PATCH 13/16] Custom notification sounds working on iOS --- ios/Podfile | 12 +++++------- ios/Runner.xcodeproj/project.pbxproj | 14 ++++++++++++++ ios/Runner/break_ended.aiff | 0 ios/Runner/session_completed.aiff | 0 lib/domain/notifications_controller.dart | 5 +++++ .../notifications_repository.dart | 18 +++++++++++++++++- pubspec.yaml | 1 + 7 files changed, 42 insertions(+), 8 deletions(-) create mode 100644 ios/Runner/break_ended.aiff create mode 100644 ios/Runner/session_completed.aiff diff --git a/ios/Podfile b/ios/Podfile index a467bf0..54d4bcd 100644 --- a/ios/Podfile +++ b/ios/Podfile @@ -41,15 +41,13 @@ post_install do |installer| installer.pods_project.targets.each do |target| flutter_additional_ios_build_settings(target) end + ################ Awesome Notifications pod modification 1 ################### + awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks') + require awesome_pod_file + update_awesome_pod_build_settings(installer) + ################ Awesome Notifications pod modification 1 ################### end -################ Awesome Notifications pod modification 1 ################### -awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks') - require awesome_pod_file - update_awesome_pod_build_settings(installer) -################ Awesome Notifications pod modification 1 ################### - end - ################ Awesome Notifications pod modification 2 ################### awesome_pod_file = File.expand_path(File.join('plugins', 'awesome_notifications', 'ios', 'Scripts', 'AwesomePodFile'), '.symlinks') require awesome_pod_file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index ce88151..64350e0 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -12,6 +12,8 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 6482FAA52ED6EDE8E8436926 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F80BBDF2B702494CC7FA307 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; + 976B225A2C26CD0500DD4ED6 /* break_ended.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 976B22582C26CD0500DD4ED6 /* break_ended.aiff */; }; + 976B225B2C26CD0500DD4ED6 /* session_completed.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 976B22592C26CD0500DD4ED6 /* session_completed.aiff */; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -57,6 +59,8 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; + 976B22582C26CD0500DD4ED6 /* break_ended.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = break_ended.aiff; path = ../../../../Downloads/files/break_ended.aiff; sourceTree = ""; }; + 976B22592C26CD0500DD4ED6 /* session_completed.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = session_completed.aiff; path = ../../../../Downloads/files/session_completed.aiff; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -118,6 +122,8 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( + 976B22582C26CD0500DD4ED6 /* break_ended.aiff */, + 976B22592C26CD0500DD4ED6 /* session_completed.aiff */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -259,10 +265,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( + 976B225B2C26CD0500DD4ED6 /* session_completed.aiff in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, + 976B225A2C26CD0500DD4ED6 /* break_ended.aiff in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -468,7 +476,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -662,7 +672,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 9740EEB21CF90195004384FC /* Debug.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; CODE_SIGN_IDENTITY = "Apple Development"; CODE_SIGN_STYLE = Automatic; @@ -690,7 +702,9 @@ isa = XCBuildConfiguration; baseConfigurationReference = 7AFA3C8E1D35360C0083082E /* Release.xcconfig */; buildSettings = { + APPLICATION_EXTENSION_API_ONLY = NO; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + BUILD_LIBRARY_FOR_DISTRIBUTION = NO; CLANG_ENABLE_MODULES = YES; "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; CODE_SIGN_STYLE = Automatic; diff --git a/ios/Runner/break_ended.aiff b/ios/Runner/break_ended.aiff new file mode 100644 index 0000000..e69de29 diff --git a/ios/Runner/session_completed.aiff b/ios/Runner/session_completed.aiff new file mode 100644 index 0000000..e69de29 diff --git a/lib/domain/notifications_controller.dart b/lib/domain/notifications_controller.dart index 7e468b9..63711f4 100644 --- a/lib/domain/notifications_controller.dart +++ b/lib/domain/notifications_controller.dart @@ -1,4 +1,7 @@ +import 'dart:io'; + import 'package:awesome_notifications/awesome_notifications.dart'; +import 'package:device_info_plus/device_info_plus.dart'; import 'package:infinite_horizons/presentation/core/theme_data.dart'; part 'package:infinite_horizons/infrastructure/notifications_repository.dart'; @@ -19,6 +22,8 @@ abstract class NotificationsController { }); Future generalPermission(); + Future preciseAlarmPermission(); + Future cancelAllNotifications(); } diff --git a/lib/infrastructure/notifications_repository.dart b/lib/infrastructure/notifications_repository.dart index c33a17f..8a1c1d0 100644 --- a/lib/infrastructure/notifications_repository.dart +++ b/lib/infrastructure/notifications_repository.dart @@ -67,7 +67,22 @@ class _NotificationsRepository extends NotificationsController { controller.requestPermissionToSendNotifications(); @override - Future preciseAlarmPermission() => controller.showAlarmPage(); + Future preciseAlarmPermission() async { + if (await isAndroid12OrAbove()) { + controller.showAlarmPage(); + } + } + + Future isAndroid12OrAbove() async { + if (Platform.isAndroid) { + final deviceInfo = DeviceInfoPlugin(); + final androidInfo = await deviceInfo.androidInfo; + final sdkInt = androidInfo.version.sdkInt; + // Android 12 is API level 31 + return sdkInt >= 31; + } + return false; + } @override Future cancelAllNotifications() => controller.cancelAll(); @@ -104,5 +119,6 @@ enum NotificationVariant { ; const NotificationVariant(this.channelKey); + final String channelKey; } diff --git a/pubspec.yaml b/pubspec.yaml index c835354..d127fc1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,6 +19,7 @@ dependencies: # Make an animated circular countdown custom_timer: ^0.2.3 # Internationalization (Easy translations) + device_info_plus: ^10.1.0 easy_localization: ^3.0.6 flutter: sdk: flutter From 2147632dd9cc496581bdc2c091e492de4f3ed70d Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Mon, 24 Jun 2024 11:49:56 +0300 Subject: [PATCH 14/16] Cleaned up the code --- assets/translations/en-US.json | 14 +++++++++----- ios/Runner.xcodeproj/project.pbxproj | 12 ++++-------- ios/Runner/Base.lproj/Main.storyboard | 13 ++++++++----- ios/Runner/Info.plist | 5 ----- lib/domain/energy_level.dart | 1 + lib/domain/preferences_controller.dart | 1 - lib/infrastructure/preferences_repository.dart | 11 ++++------- lib/presentation/organisms/timer_organism.dart | 9 ++++++--- lib/presentation/pages/home_page.dart | 16 +++++++++------- 9 files changed, 41 insertions(+), 41 deletions(-) diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json index dec9a45..7227f86 100644 --- a/assets/translations/en-US.json +++ b/assets/translations/en-US.json @@ -34,12 +34,12 @@ "tip:": "Tip:", "next": "Next", "tip_description": "Tip Description", - "resource" : "Linked Resource", + "resource": "Linked Resource", "resources": "Resources:", - "dnd":"Do Not Disturb mode", + "dnd": "Do Not Disturb mode", "close": "Close", - "settings" : "Settings", - "sound" : "Sound", + "settings": "Settings", + "sound": "Sound", "screen_lock": "Screen Lock", "ready": "I am ready", "ready_for_session": "Next Round?", @@ -50,5 +50,9 @@ "notes": "Notes", "famous_methods": "Famous methods", "recommended": "Recommended", - "maximum_efficiency": "Maximum Efficiency" + "maximum_efficiency": "Maximum Efficiency", + "break": "Take a break", + "new_session": "Press to start a new session", + "study_ended": "You have completed the Study session, take a break", + "break_ended": "Break ended, enter the app to continue to the next session" } \ No newline at end of file diff --git a/ios/Runner.xcodeproj/project.pbxproj b/ios/Runner.xcodeproj/project.pbxproj index 64350e0..cd1f631 100644 --- a/ios/Runner.xcodeproj/project.pbxproj +++ b/ios/Runner.xcodeproj/project.pbxproj @@ -12,8 +12,8 @@ 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */ = {isa = PBXBuildFile; fileRef = 3B3967151E833CAA004F5970 /* AppFrameworkInfo.plist */; }; 6482FAA52ED6EDE8E8436926 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1F80BBDF2B702494CC7FA307 /* Pods_RunnerTests.framework */; }; 74858FAF1ED2DC5600515810 /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 74858FAE1ED2DC5600515810 /* AppDelegate.swift */; }; - 976B225A2C26CD0500DD4ED6 /* break_ended.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 976B22582C26CD0500DD4ED6 /* break_ended.aiff */; }; - 976B225B2C26CD0500DD4ED6 /* session_completed.aiff in Resources */ = {isa = PBXBuildFile; fileRef = 976B22592C26CD0500DD4ED6 /* session_completed.aiff */; }; + 976B225A2C26CD0500DD4ED6 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; + 976B225B2C26CD0500DD4ED6 /* BuildFile in Resources */ = {isa = PBXBuildFile; }; 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FA1CF9000F007C117D /* Main.storyboard */; }; 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FD1CF9000F007C117D /* Assets.xcassets */; }; 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 97C146FF1CF9000F007C117D /* LaunchScreen.storyboard */; }; @@ -59,8 +59,6 @@ 7AFA3C8E1D35360C0083082E /* Release.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; name = Release.xcconfig; path = Flutter/Release.xcconfig; sourceTree = ""; }; 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Debug.xcconfig; path = Flutter/Debug.xcconfig; sourceTree = ""; }; 9740EEB31CF90195004384FC /* Generated.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; name = Generated.xcconfig; path = Flutter/Generated.xcconfig; sourceTree = ""; }; - 976B22582C26CD0500DD4ED6 /* break_ended.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = break_ended.aiff; path = ../../../../Downloads/files/break_ended.aiff; sourceTree = ""; }; - 976B22592C26CD0500DD4ED6 /* session_completed.aiff */ = {isa = PBXFileReference; lastKnownFileType = audio.aiff; name = session_completed.aiff; path = ../../../../Downloads/files/session_completed.aiff; sourceTree = ""; }; 97C146EE1CF9000F007C117D /* Runner.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Runner.app; sourceTree = BUILT_PRODUCTS_DIR; }; 97C146FB1CF9000F007C117D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/Main.storyboard; sourceTree = ""; }; 97C146FD1CF9000F007C117D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; @@ -122,8 +120,6 @@ 97C146E51CF9000F007C117D = { isa = PBXGroup; children = ( - 976B22582C26CD0500DD4ED6 /* break_ended.aiff */, - 976B22592C26CD0500DD4ED6 /* session_completed.aiff */, 9740EEB11CF90186004384FC /* Flutter */, 97C146F01CF9000F007C117D /* Runner */, 97C146EF1CF9000F007C117D /* Products */, @@ -265,12 +261,12 @@ isa = PBXResourcesBuildPhase; buildActionMask = 2147483647; files = ( - 976B225B2C26CD0500DD4ED6 /* session_completed.aiff in Resources */, + 976B225B2C26CD0500DD4ED6 /* BuildFile in Resources */, 97C147011CF9000F007C117D /* LaunchScreen.storyboard in Resources */, 3B3967161E833CAA004F5970 /* AppFrameworkInfo.plist in Resources */, 97C146FE1CF9000F007C117D /* Assets.xcassets in Resources */, 97C146FC1CF9000F007C117D /* Main.storyboard in Resources */, - 976B225A2C26CD0500DD4ED6 /* break_ended.aiff in Resources */, + 976B225A2C26CD0500DD4ED6 /* BuildFile in Resources */, ); runOnlyForDeploymentPostprocessing = 0; }; diff --git a/ios/Runner/Base.lproj/Main.storyboard b/ios/Runner/Base.lproj/Main.storyboard index f3c2851..19f7517 100644 --- a/ios/Runner/Base.lproj/Main.storyboard +++ b/ios/Runner/Base.lproj/Main.storyboard @@ -1,8 +1,10 @@ - - + + + - + + @@ -14,13 +16,14 @@ - + - + + diff --git a/ios/Runner/Info.plist b/ios/Runner/Info.plist index 35ec233..3637a48 100644 --- a/ios/Runner/Info.plist +++ b/ios/Runner/Info.plist @@ -30,11 +30,6 @@ UIApplicationSupportsIndirectInputEvents - UIBackgroundModes - - audio - fetch - UILaunchStoryboardName LaunchScreen UIMainStoryboardFile diff --git a/lib/domain/energy_level.dart b/lib/domain/energy_level.dart index 2e2233e..6b75c8c 100644 --- a/lib/domain/energy_level.dart +++ b/lib/domain/energy_level.dart @@ -67,6 +67,7 @@ enum EnergyType { Duration.zero, ), veryLow('very_low', Duration(seconds: 30)), + // veryLow('very_low', Duration(minutes: 5)), low('low', Duration(minutes: 10)), pomodoro('Pomodoro', Duration(minutes: 25)), high('high', Duration(minutes: 45), tipsId: ['45m/5m']), diff --git a/lib/domain/preferences_controller.dart b/lib/domain/preferences_controller.dart index 8316175..5e124ab 100644 --- a/lib/domain/preferences_controller.dart +++ b/lib/domain/preferences_controller.dart @@ -9,7 +9,6 @@ abstract class PreferencesController { _instance ??= _PreferencesRepository(); Future init(); - Future reload(); String? getString(String key); diff --git a/lib/infrastructure/preferences_repository.dart b/lib/infrastructure/preferences_repository.dart index 4404f63..c738548 100644 --- a/lib/infrastructure/preferences_repository.dart +++ b/lib/infrastructure/preferences_repository.dart @@ -6,9 +6,6 @@ class _PreferencesRepository extends PreferencesController { @override Future init() async => preferences = await SharedPreferences.getInstance(); - @override - Future reload() => preferences.reload(); - @override String? getString(String key) => preferences.getString(key); @@ -21,15 +18,15 @@ class _PreferencesRepository extends PreferencesController { @override Duration? getDuration(String key) { final int? milliseconds = preferences.getInt(key); - return milliseconds != null ? Duration(milliseconds: milliseconds) : null; + return milliseconds == null ? null : Duration(milliseconds: milliseconds); } @override DateTime? getDateTime(String key) { final int? milliseconds = preferences.getInt(key); - return milliseconds != null - ? DateTime.fromMillisecondsSinceEpoch(milliseconds) - : null; + return milliseconds == null + ? null + : DateTime.fromMillisecondsSinceEpoch(milliseconds); } @override diff --git a/lib/presentation/organisms/timer_organism.dart b/lib/presentation/organisms/timer_organism.dart index abccdc5..47982a0 100644 --- a/lib/presentation/organisms/timer_organism.dart +++ b/lib/presentation/organisms/timer_organism.dart @@ -78,10 +78,13 @@ class TimerStateManager { } _remainingTime = stateDuration; + + const Duration interval = Duration(seconds: 1); + _timer = Timer.periodic( - const Duration(seconds: 1), + interval, (Timer timer) { - if (_remainingTime <= const Duration(seconds: 1)) { + if (_remainingTime <= interval) { _remainingTime = Duration.zero; _timer?.cancel(); incrementState(); @@ -89,7 +92,7 @@ class TimerStateManager { iterateOverTimerStates(); return; } - _remainingTime = _remainingTime - const Duration(seconds: 1); + _remainingTime = _remainingTime - interval; }, ); } diff --git a/lib/presentation/pages/home_page.dart b/lib/presentation/pages/home_page.dart index 03cbc60..6fa8cec 100644 --- a/lib/presentation/pages/home_page.dart +++ b/lib/presentation/pages/home_page.dart @@ -1,3 +1,4 @@ +import 'package:easy_localization/easy_localization.dart'; import 'package:flutter/material.dart'; import 'package:infinite_horizons/domain/notifications_controller.dart'; import 'package:infinite_horizons/domain/player_controller.dart'; @@ -65,6 +66,9 @@ class _HomePageState extends State with WidgetsBindingObserver { ); return; case AppLifecycleState.paused: + final bool isTimerRunning = TimerStateManager.isTimerRunning(); + + TimerStateManager.pauseTimer(); PreferencesController.instance .setDateTime('pausedTime', DateTime.now()); PreferencesController.instance @@ -74,11 +78,10 @@ class _HomePageState extends State with WidgetsBindingObserver { TimerStateManager.remainingTime ?? Duration.zero, ); - if (!TimerStateManager.isTimerRunning()) { + if (!isTimerRunning) { return; } - TimerStateManager.pauseTimer(); final upcomingStates = TimerStateManager.upcomingStates( TimerStateManager.state, TimerStateManager.remainingTime, @@ -90,13 +93,12 @@ class _HomePageState extends State with WidgetsBindingObserver { String body; NotificationVariant notificationVariant; if (stateWithTime.state == TimerState.study) { - title = 'Take a break'; - body = 'You have completed the Study session, take a break'; + title = 'break'.tr(); + body = 'study_ended'.tr(); notificationVariant = NotificationVariant.studyEnded; } else { - title = 'Press to start a new session'; - body = - 'Break ended, enter the app to continue to the next session'; + title = 'new_session'.tr(); + body = 'break_ended'.tr(); notificationVariant = NotificationVariant.breakEnded; } From 01aa8f432c34947dbb97fb2d5a7039ea2d01f0d7 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Mon, 24 Jun 2024 11:51:20 +0300 Subject: [PATCH 15/16] Reverted timer timing --- lib/domain/energy_level.dart | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/domain/energy_level.dart b/lib/domain/energy_level.dart index 6b75c8c..66ef2e9 100644 --- a/lib/domain/energy_level.dart +++ b/lib/domain/energy_level.dart @@ -66,8 +66,7 @@ enum EnergyType { 'undefined', Duration.zero, ), - veryLow('very_low', Duration(seconds: 30)), - // veryLow('very_low', Duration(minutes: 5)), + veryLow('very_low', Duration(minutes: 5)), low('low', Duration(minutes: 10)), pomodoro('Pomodoro', Duration(minutes: 25)), high('high', Duration(minutes: 45), tipsId: ['45m/5m']), From 943b9ed637dd3b1e2036ac1d4e724712534404e3 Mon Sep 17 00:00:00 2001 From: Guy Luz Date: Mon, 24 Jun 2024 11:58:09 +0300 Subject: [PATCH 16/16] Updated github action flutter version to 3.22.0 --- .github/workflows/dart.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/dart.yml b/.github/workflows/dart.yml index 290bf1a..488814f 100644 --- a/.github/workflows/dart.yml +++ b/.github/workflows/dart.yml @@ -9,7 +9,7 @@ on: env: flutter_channel: 'stable' # or: 'dev' or 'beta' - flutter_version: '3.19.5' + flutter_version: '3.22.0' jobs: build: