Skip to content

Commit

Permalink
Support force mode (#17)
Browse files Browse the repository at this point in the history
* Added shared pref methods

* Added required packages

* Implemented force mode and split widgets

* Enabled windows and linux

* config native for window manager package

* bump app version

* Revert rule value

* fix minor issue on nodejs

* Used minimize instead of hide window when 20s over

* try fix dmg build issue

* Improved force mode checkbox and split rule text as widget

* updated readme

* Split countDownscreen on separate file

* Updated notification message base on case & enable fullscreen for 20s

* Renamed handleWindowState method

* Show message when take break on app

* minor fix

* Minor improve on force mode checkbox
  • Loading branch information
M97Chahboun authored Jun 7, 2024
1 parent 22a809f commit aa5fdf4
Show file tree
Hide file tree
Showing 42 changed files with 1,934 additions and 193 deletions.
7 changes: 1 addition & 6 deletions .github/workflows/desktop_build.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -94,14 +94,9 @@ jobs:
working-directory: ${{ matrix.build_path }}
- name: Setup Node.js environment
uses: actions/setup-node@v2
with:
node-version: '14'
- name: Convert build to dmg for macOS
if: matrix.target == 'macOS'
run: |
brew install [email protected]
npm config set python /usr/local/bin/python3.9
npm install -g appdmg
run: npm install -g appdmg
- name: run appdmg
if: matrix.target == 'macOS'
run: appdmg installer/dmg_creator/config.json $GITHUB_WORKSPACE/EyesCare${{ matrix.target }}.dmg
Expand Down
9 changes: 9 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,15 @@ We welcome contributions from the community to enhance the Eyes Care app. To con

[MIT License](https://opensource.org/licenses/MIT)

## Tools / Acknowledgements

- [rocket_timer](https://pub.dev/packages/rocket_timer)

Special thanks to these amazing projects from [LeanFlutter](https://github.com/leanflutter) which help power CareEyes:

- [local_notifier](https://pub.dev/packages/local_notifier)
- [window_manager](https://pub.dev/packages/window_manager)

## Support

If you encounter any issues or have any questions or suggestions, please [open an issue](https://github.com/bixat/eyes_care/issues) on the GitHub repository.
Expand Down
190 changes: 190 additions & 0 deletions lib/countdown_screen.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
import 'package:eyes_care/main.dart';
import 'package:flutter/material.dart';
import 'package:rocket_timer/rocket_timer.dart';
import 'package:window_manager/window_manager.dart';
import 'package:eyes_care/shared_pref.dart';
import 'package:eyes_care/widgets/force_mode_check_box.dart';
import 'package:eyes_care/widgets/rule_text.dart';
import 'package:eyes_care/widgets/rule_timer.dart';
import 'package:local_notifier/local_notifier.dart';

class CountdownScreen extends StatefulWidget {
const CountdownScreen({super.key});

@override
CountdownScreenState createState() => CountdownScreenState();
}

const int rule = 20;
const duration = Duration(minutes: rule);
const size = Size(400, 400);

class CountdownScreenState extends State<CountdownScreen> with WindowListener {
late RocketTimer _timer;
bool inProgress = false;
late ValueNotifier<bool> forceModeEnabled = ValueNotifier(false);
int followed = 0;
WindowOptions windowOptions = const WindowOptions(
windowButtonVisibility: false,
size: size,
minimumSize: size,
maximumSize: size,
center: true,
backgroundColor: Colors.transparent,
skipTaskbar: false,
titleBarStyle: TitleBarStyle.hidden,
);

@override
void initState() {
setUpForceMode();
windowManager.waitUntilReadyToShow(windowOptions, () async {
await windowManager.show();
await windowManager.focus();
});
windowManager.addListener(this);
localNotifier.setup(
appName: 'CareYourEyes',
shortcutPolicy: ShortcutPolicy.requireCreate,
);
super.initState();
initTimer();
}

void initTimer() {
_timer = RocketTimer(type: TimerType.countdown, duration: duration);
_timer.addListener(() {
if (_timer.kDuration == 0) {
showNotification();
_timer.kDuration = inProgress ? duration.inSeconds : rule;
inProgress = !inProgress;
setState(() {});
}
});
_timer.start();
}

setUpForceMode() {
PreferenceService.getBool(PreferenceService.forceModeKey).then((value) {
forceModeEnabled.value = value ?? false;
});
}

@override
Future<void> onWindowMinimize() async {
if (forceModeEnabled.value) {
await handleWindowState();
}
super.onWindowMinimize();
}

@override
Future<void> onWindowBlur() async {
if (forceModeEnabled.value) {
await handleWindowState();
}
super.onWindowBlur();
}

Future<void> handleWindowState() async {
if (inProgress) {
await windowManager.show();
await windowManager.focus();
await windowManager.setFullScreen(true);
} else {
windowManager.minimize();
await windowManager.setFullScreen(false);
}
}

@override
void dispose() {
_timer.dispose();
windowManager.removeListener(this);
super.dispose();
}

Future<void> showNotification() async {
LocalNotification notification = LocalNotification(
title: inProgress ? "Stay Focused 💪" : "Take a Moment 🌟",
body: inProgress
? "Keep your gaze on the screen. Remember, every 20 minutes, take a 20-second break looking at something 20 feet away."
: "Step back from the screen and focus on something 20 feet away for 20 seconds. Your eyes will thank you!",
);
notification.onShow = _onShowNotification;
notification.show();
}

_onShowNotification() async {
if (forceModeEnabled.value) {
await handleWindowState();
}
}

@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: const Text('Eyes Care'),
centerTitle: true,
actions: [
AnimatedBuilder(
animation: _timer,
builder: (context, _) {
return IconButton(
icon: Icon(_timer.status == TimerStatus.pause
? Icons.play_arrow
: Icons.pause),
onPressed: () {
if (_timer.status == TimerStatus.pause) {
_timer.start();
} else {
_timer.pause();
}
},
);
}),
IconButton(
onPressed: _timer.restart, icon: const Icon(Icons.restart_alt)),
IconButton(
onPressed: windowManager.minimize,
icon: const Icon(Icons.minimize_rounded)),
],
leading: ValueListenableBuilder(
valueListenable: themeNotifier,
builder: (context, _, __) {
final isLight = themeNotifier.value.index == 1;
return IconButton(
onPressed: () {
themeNotifier.value =
isLight ? ThemeMode.dark : ThemeMode.light;
},
icon: Icon(isLight ? Icons.dark_mode : Icons.light_mode));
})),
body: Container(
padding: const EdgeInsets.all(8.0),
child: Flex(
direction: inProgress ? Axis.vertical : Axis.horizontal,
mainAxisAlignment: MainAxisAlignment.center,
crossAxisAlignment: CrossAxisAlignment.center,
children: [
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
if (inProgress)
Text(
"look away from your screen and focus on something 20 feet away for 20 seconds.",
style: Theme.of(context).textTheme.headlineMedium)
else
const RuleText(),
ForceModeCheckBox(forceModeEnabled: forceModeEnabled)
],
),
),
RuleTimer(timer: _timer, inProgress: inProgress),
],
)),
);
}
}
Loading

0 comments on commit aa5fdf4

Please sign in to comment.