From 519750f6b484c1a19841861bde69fce2985c37c5 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sun, 4 May 2025 22:57:53 +0100 Subject: [PATCH 01/18] Adds settings bottom sheet --- lib/countdown_screen.dart | 97 +++++++++++++++++++++------ lib/widgets/edit_rule_button.dart | 43 ++---------- lib/widgets/force_mode_check_box.dart | 32 +++++---- lib/widgets/work_break_info.dart | 56 ++++++++++++++++ pubspec.yaml | 1 + 5 files changed, 157 insertions(+), 72 deletions(-) create mode 100644 lib/widgets/work_break_info.dart diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index 213556d..cd13c64 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -1,5 +1,6 @@ import 'package:eyes_care/main.dart'; import 'package:eyes_care/widgets/edit_rule_button.dart'; +import 'package:eyes_care/widgets/work_break_info.dart'; import 'package:flutter/material.dart'; import 'package:rocket_timer/rocket_timer.dart'; import 'package:window_manager/window_manager.dart'; @@ -17,7 +18,7 @@ class CountdownScreen extends StatefulWidget { CountdownScreenState createState() => CountdownScreenState(); } -const size = Size(500, 900); +const size = Size(500, 750); class CountdownScreenState extends State with WindowListener { RocketTimer? _timer; @@ -204,6 +205,10 @@ class CountdownScreenState extends State with WindowListener { Column( children: [ RuleTimer(timer: _timer!, inBreak: inBreak), + WorkBreakInfo( + reminder: reminder, + breakTime: breakTime, + ), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ @@ -226,6 +231,11 @@ class CountdownScreenState extends State with WindowListener { IconButton( onPressed: _restartTimer, icon: const Icon(Icons.restart_alt)), + IconButton( + onPressed: () { + _showSettings(context); + }, + icon: const Icon(Icons.settings)), ], ) ], @@ -234,24 +244,6 @@ class CountdownScreenState extends State with WindowListener { // Rule Text Card const RuleText(), - const SizedBox(height: 24), - - // Edit Rule Button - EditRuleButton( - reminder: reminder, - breakTime: breakTime, - onConfirm: (min, sec) { - reminder = min; - breakTime = sec; - initTimer(); - setState(() {}); - }, - ), - const SizedBox(height: 16), - - // Force Mode Toggle - ForceModeCheckBox(forceModeEnabled: forceModeEnabled), - const Spacer(), // Version Info @@ -291,6 +283,30 @@ class CountdownScreenState extends State with WindowListener { ); } + Future _showSettings(BuildContext context) { + return showModalBottomSheet( + context: context, + builder: (BuildContext context) { + return Settings( + reminder: reminder, + breakTime: breakTime, + forceModeEnabled: forceModeEnabled, + onConfirm: (min, sec) { + setState(() { + reminder = min; + breakTime = sec; + _restartTimer(); + }); + PreferenceService.setDuration(min, sec); + PreferenceService.setBool( + PreferenceService.forceModeKey, forceModeEnabled.value); + Navigator.pop(context); + }, + ); + }, + ); + } + void _restartTimer() { _timer!.restart(); inBreak = false; @@ -298,3 +314,46 @@ class CountdownScreenState extends State with WindowListener { setState(() {}); } } + +class Settings extends StatelessWidget { + final int reminder; + final int breakTime; + final ValueNotifier forceModeEnabled; + final Function(int, int) onConfirm; + + const Settings({ + Key? key, + required this.reminder, + required this.breakTime, + required this.forceModeEnabled, + required this.onConfirm, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16.0, + children: [ + EditRuleButton( + reminder: reminder, + breakTime: breakTime, + onConfirm: onConfirm, + ), + SwitcherSetting( + enabled: forceModeEnabled, + title: "Force Mode", + subtitle: "Prevent window minimization during breaks", + ), + SwitcherSetting( + enabled: forceModeEnabled, + title: "Force Mode", + subtitle: "Prevent window minimization during breaks", + ), + ], + ), + ); + } +} diff --git a/lib/widgets/edit_rule_button.dart b/lib/widgets/edit_rule_button.dart index e8ebdb3..db18205 100644 --- a/lib/widgets/edit_rule_button.dart +++ b/lib/widgets/edit_rule_button.dart @@ -1,5 +1,6 @@ import 'package:eyes_care/shared_pref.dart'; import 'package:eyes_care/widgets/duration_picker_dialog.dart'; +import 'package:eyes_care/widgets/work_break_info.dart'; import 'package:flutter/material.dart'; class EditRuleButton extends StatefulWidget { @@ -57,45 +58,9 @@ class _EditRuleButtonState extends State { ), ), const SizedBox(width: 12), - Column( - crossAxisAlignment: CrossAxisAlignment.start, - mainAxisSize: MainAxisSize.min, - children: [ - Row( - children: [ - Text( - "${widget.reminder}m", - style: theme.textTheme.titleLarge?.copyWith( - color: theme.colorScheme.onSurface, - fontWeight: FontWeight.bold, - ), - ), - Text( - " work", - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), - Row( - children: [ - Text( - "${widget.breakTime}s", - style: theme.textTheme.titleMedium?.copyWith( - color: theme.colorScheme.secondary, - fontWeight: FontWeight.bold, - ), - ), - Text( - " break", - style: theme.textTheme.bodyMedium?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), - ], - ), - ], + WorkBreakInfo( + reminder: widget.reminder, + breakTime: widget.breakTime, ), const Spacer(), Icon( diff --git a/lib/widgets/force_mode_check_box.dart b/lib/widgets/force_mode_check_box.dart index edc48ee..74f3d96 100644 --- a/lib/widgets/force_mode_check_box.dart +++ b/lib/widgets/force_mode_check_box.dart @@ -2,31 +2,35 @@ import 'package:eyes_care/shared_pref.dart'; import 'package:flutter/material.dart'; import 'package:window_manager/window_manager.dart'; -class ForceModeCheckBox extends StatelessWidget { - const ForceModeCheckBox({ +class SwitcherSetting extends StatelessWidget { + final String title; + final String subtitle; + const SwitcherSetting({ super.key, - required this.forceModeEnabled, + required this.enabled, + required this.title, + required this.subtitle, }); - final ValueNotifier forceModeEnabled; + final ValueNotifier enabled; @override Widget build(BuildContext context) { final theme = Theme.of(context); return ValueListenableBuilder( - valueListenable: forceModeEnabled, + valueListenable: enabled, builder: (context, _, __) { return AnimatedContainer( duration: const Duration(milliseconds: 300), decoration: BoxDecoration( borderRadius: BorderRadius.circular(16), - color: forceModeEnabled.value + color: enabled.value ? theme.colorScheme.primaryContainer : theme.colorScheme.surface, ), child: ListTile( - onTap: () => onChanged(!forceModeEnabled.value), + onTap: () => onChanged(!enabled.value), shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(16), ), @@ -39,32 +43,32 @@ class ForceModeCheckBox extends StatelessWidget { padding: const EdgeInsets.all(4.0), child: Icon( Icons.lock_rounded, - color: forceModeEnabled.value + color: enabled.value ? theme.colorScheme.primary : theme.colorScheme.onSurfaceVariant, ), ), ), title: Text( - "Force Mode", + title, style: theme.textTheme.titleMedium?.copyWith( fontWeight: FontWeight.w600, - color: forceModeEnabled.value + color: enabled.value ? theme.colorScheme.onPrimaryContainer : theme.colorScheme.onSurface, ), ), subtitle: Text( - "Prevent window minimization during breaks", + subtitle, style: theme.textTheme.bodySmall?.copyWith( - color: forceModeEnabled.value + color: enabled.value ? theme.colorScheme.onPrimaryContainer .withAlpha((0.8 * 255).round()) : theme.colorScheme.onSurfaceVariant, ), ), trailing: Switch( - value: forceModeEnabled.value, + value: enabled.value, onChanged: onChanged, ), ), @@ -75,7 +79,7 @@ class ForceModeCheckBox extends StatelessWidget { void onChanged(bool? value) { if (value == null) return; PreferenceService.setBool(PreferenceService.forceModeKey, value); - forceModeEnabled.value = value; + enabled.value = value; windowManager.setFullScreen(false); } } diff --git a/lib/widgets/work_break_info.dart b/lib/widgets/work_break_info.dart new file mode 100644 index 0000000..ea80c1c --- /dev/null +++ b/lib/widgets/work_break_info.dart @@ -0,0 +1,56 @@ +import 'package:flutter/material.dart'; + +class WorkBreakInfo extends StatelessWidget { + const WorkBreakInfo( + {super.key, required this.reminder, required this.breakTime}); + + final int reminder, breakTime; + + @override + Widget build(BuildContext context) { + final theme = Theme.of(context); + + return Column( + crossAxisAlignment: CrossAxisAlignment.start, + mainAxisSize: MainAxisSize.min, + children: [ + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "${reminder}m", + style: theme.textTheme.titleLarge?.copyWith( + color: theme.colorScheme.onSurface, + fontWeight: FontWeight.bold, + ), + ), + Text( + " work", + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ), + Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text( + "${breakTime}s", + style: theme.textTheme.titleMedium?.copyWith( + color: theme.colorScheme.primary, + fontWeight: FontWeight.bold, + ), + ), + Text( + " break", + style: theme.textTheme.bodyMedium?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ], + ); + } +} diff --git a/pubspec.yaml b/pubspec.yaml index c603462..6ebe7f3 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -41,6 +41,7 @@ dependencies: shared_preferences: ^2.2.3 google_fonts: ^6.1.0 url_launcher: ^6.3.1 + launch_at_startup: ^0.3.1 dev_dependencies: flutter_test: From 2e54572643a1ff4b97ca54ee6be1af3b2b5129f6 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sun, 4 May 2025 22:59:11 +0100 Subject: [PATCH 02/18] Improved settings icon position --- lib/countdown_screen.dart | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index cd13c64..5656428 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -174,7 +174,6 @@ class CountdownScreenState extends State with WindowListener { children: [ // App Bar Row( - mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ Text( 'Eyes Care', @@ -183,6 +182,12 @@ class CountdownScreenState extends State with WindowListener { color: theme.colorScheme.primary, ), ), + Spacer(), + IconButton( + onPressed: () { + _showSettings(context); + }, + icon: const Icon(Icons.settings)), IconButton( icon: Icon( themeNotifier.value == ThemeMode.light @@ -230,12 +235,7 @@ class CountdownScreenState extends State with WindowListener { }), IconButton( onPressed: _restartTimer, - icon: const Icon(Icons.restart_alt)), - IconButton( - onPressed: () { - _showSettings(context); - }, - icon: const Icon(Icons.settings)), + icon: const Icon(Icons.restart_alt)) ], ) ], From 7876d8996e743e7b90f481e46ef065f06c756be9 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sun, 4 May 2025 23:25:43 +0100 Subject: [PATCH 03/18] Configured enable launch app --- lib/countdown_screen.dart | 62 +++++++++++++++++++++------ lib/main.dart | 16 +++++++ lib/shared_pref.dart | 1 + lib/widgets/force_mode_check_box.dart | 15 +++---- macos/Runner/MainFlutterWindow.swift | 17 ++++++++ pubspec.yaml | 3 +- 6 files changed, 91 insertions(+), 23 deletions(-) diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index 5656428..4f12eff 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -2,6 +2,8 @@ import 'package:eyes_care/main.dart'; import 'package:eyes_care/widgets/edit_rule_button.dart'; import 'package:eyes_care/widgets/work_break_info.dart'; import 'package:flutter/material.dart'; +import 'package:launch_at_startup/launch_at_startup.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:rocket_timer/rocket_timer.dart'; import 'package:window_manager/window_manager.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -25,6 +27,7 @@ class CountdownScreenState extends State with WindowListener { bool inBreak = false; bool isPaused = false; late ValueNotifier forceModeEnabled = ValueNotifier(false); + late ValueNotifier startUpModeEnabled = ValueNotifier(false); WindowOptions windowOptions = const WindowOptions( windowButtonVisibility: false, size: size, @@ -41,9 +44,11 @@ class CountdownScreenState extends State with WindowListener { Duration get workDuration => Duration(minutes: reminder); Duration get breakDuration => Duration(seconds: breakTime); + final appVersion = ValueNotifier(null); + @override void initState() { - setUpForceMode(); + setUpSettings(); windowManager.waitUntilReadyToShow(windowOptions, () async { await windowManager.show(); await windowManager.focus(); @@ -80,10 +85,15 @@ class CountdownScreenState extends State with WindowListener { _timer!.start(); } - setUpForceMode() { + setUpSettings() async { + final packageInfo = await PackageInfo.fromPlatform(); + appVersion.value = packageInfo.version; PreferenceService.getBool(PreferenceService.forceModeKey).then((value) { forceModeEnabled.value = value ?? false; }); + PreferenceService.getBool(PreferenceService.startupModeKey).then((value) { + forceModeEnabled.value = value ?? false; + }); } @override @@ -250,13 +260,16 @@ class CountdownScreenState extends State with WindowListener { Column( mainAxisSize: MainAxisSize.min, children: [ - Text( - // TODO: get from pubspect dynamiclly - 'v2.0.0', - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ), + ValueListenableBuilder( + valueListenable: appVersion, + builder: (context, value, _) { + return Text( + 'v$value', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ); + }), const SizedBox(height: 4), MouseRegion( cursor: SystemMouseCursors.click, @@ -291,6 +304,7 @@ class CountdownScreenState extends State with WindowListener { reminder: reminder, breakTime: breakTime, forceModeEnabled: forceModeEnabled, + startUpModeEnabled: startUpModeEnabled, onConfirm: (min, sec) { setState(() { reminder = min; @@ -319,6 +333,7 @@ class Settings extends StatelessWidget { final int reminder; final int breakTime; final ValueNotifier forceModeEnabled; + final ValueNotifier startUpModeEnabled; final Function(int, int) onConfirm; const Settings({ @@ -327,6 +342,7 @@ class Settings extends StatelessWidget { required this.breakTime, required this.forceModeEnabled, required this.onConfirm, + required this.startUpModeEnabled, }) : super(key: key); @override @@ -344,16 +360,38 @@ class Settings extends StatelessWidget { ), SwitcherSetting( enabled: forceModeEnabled, + icon: Icons.lock_rounded, title: "Force Mode", subtitle: "Prevent window minimization during breaks", + onChanged: _onUpdateForceMode, ), SwitcherSetting( - enabled: forceModeEnabled, - title: "Force Mode", - subtitle: "Prevent window minimization during breaks", + enabled: startUpModeEnabled, + icon: Icons.start, + title: "Startup at Login", + subtitle: "Launch application automatically at system startup", + onChanged: _onUpdateStartupMode, ), ], ), ); } + + void _onUpdateForceMode(bool? value) { + if (value == null) return; + PreferenceService.setBool(PreferenceService.forceModeKey, value); + forceModeEnabled.value = value; + windowManager.setFullScreen(false); + } + + void _onUpdateStartupMode(bool? value) { + if (value == null) return; + startUpModeEnabled.value = value; + PreferenceService.setBool(PreferenceService.startupModeKey, value); + if (value) { + launchAtStartup.disable(); + } else { + launchAtStartup.disable(); + } + } } diff --git a/lib/main.dart b/lib/main.dart index 6737c9b..b0e950e 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,24 @@ +import 'dart:io'; + import 'package:eyes_care/countdown_screen.dart'; import 'package:flutter/material.dart'; +import 'package:launch_at_startup/launch_at_startup.dart'; +import 'package:package_info_plus/package_info_plus.dart'; import 'package:window_manager/window_manager.dart'; import 'package:google_fonts/google_fonts.dart'; +Future initLaunchStartup() async { + PackageInfo packageInfo = await PackageInfo.fromPlatform(); + + launchAtStartup.setup( + appName: packageInfo.appName, + appPath: Platform.resolvedExecutable, + // Set packageName parameter to support MSIX. + // TODO: update package name + packageName: 'com.example.keepYourEyes', + ); +} + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); diff --git a/lib/shared_pref.dart b/lib/shared_pref.dart index 033585d..5dabf6d 100644 --- a/lib/shared_pref.dart +++ b/lib/shared_pref.dart @@ -2,6 +2,7 @@ import 'package:shared_preferences/shared_preferences.dart'; class PreferenceService { static const forceModeKey = "force_mode"; + static const startupModeKey = "startup_mode"; static const minutes = "minutes"; static const seconds = "seconds"; diff --git a/lib/widgets/force_mode_check_box.dart b/lib/widgets/force_mode_check_box.dart index 74f3d96..7828dd8 100644 --- a/lib/widgets/force_mode_check_box.dart +++ b/lib/widgets/force_mode_check_box.dart @@ -1,18 +1,20 @@ -import 'package:eyes_care/shared_pref.dart'; import 'package:flutter/material.dart'; -import 'package:window_manager/window_manager.dart'; class SwitcherSetting extends StatelessWidget { final String title; final String subtitle; + final IconData icon; const SwitcherSetting({ super.key, required this.enabled, required this.title, required this.subtitle, + required this.onChanged, + required this.icon, }); final ValueNotifier enabled; + final Function(bool) onChanged; @override Widget build(BuildContext context) { @@ -42,7 +44,7 @@ class SwitcherSetting extends StatelessWidget { child: Padding( padding: const EdgeInsets.all(4.0), child: Icon( - Icons.lock_rounded, + icon, color: enabled.value ? theme.colorScheme.primary : theme.colorScheme.onSurfaceVariant, @@ -75,11 +77,4 @@ class SwitcherSetting extends StatelessWidget { ); }); } - - void onChanged(bool? value) { - if (value == null) return; - PreferenceService.setBool(PreferenceService.forceModeKey, value); - enabled.value = value; - windowManager.setFullScreen(false); - } } diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index ad6e8ab..07cf760 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -9,6 +9,23 @@ class MainFlutterWindow: NSWindow { self.contentViewController = flutterViewController self.setFrame(windowFrame, display: true) + FlutterMethodChannel( + name: "launch_at_startup", binaryMessenger: flutterViewController.engine.binaryMessenger + ) + .setMethodCallHandler { (_ call: FlutterMethodCall, result: @escaping FlutterResult) in + switch call.method { + case "launchAtStartupIsEnabled": + result(LaunchAtLogin.isEnabled) + case "launchAtStartupSetEnabled": + if let arguments = call.arguments as? [String: Any] { + LaunchAtLogin.isEnabled = arguments["setEnabledValue"] as! Bool + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } + } + RegisterGeneratedPlugins(registry: flutterViewController) super.awakeFromNib() diff --git a/pubspec.yaml b/pubspec.yaml index 6ebe7f3..032faf2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.0.1 +version: 2.0.2 environment: sdk: '>=3.1.2 <4.0.0' @@ -42,6 +42,7 @@ dependencies: google_fonts: ^6.1.0 url_launcher: ^6.3.1 launch_at_startup: ^0.3.1 + package_info_plus: ^8.3.0 dev_dependencies: flutter_test: From e5527b210edf1d0e4d06d905e76432e41f0ce35e Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 6 May 2025 06:38:46 +0100 Subject: [PATCH 04/18] minor fix --- lib/countdown_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index 4f12eff..c3a87a4 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -192,7 +192,7 @@ class CountdownScreenState extends State with WindowListener { color: theme.colorScheme.primary, ), ), - Spacer(), + const Spacer(), IconButton( onPressed: () { _showSettings(context); From ca58a4554aea2335df181e578168e92fd1bcea53 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 6 May 2025 06:43:47 +0100 Subject: [PATCH 05/18] Configured macos LaunchAtLogin --- macos/Flutter/GeneratedPluginRegistrant.swift | 6 +++ macos/Podfile.lock | 29 +++++++++-- macos/Runner.xcodeproj/project.pbxproj | 51 +++++++++++++++++-- .../xcshareddata/swiftpm/Package.resolved | 15 ++++++ .../xcshareddata/xcschemes/Runner.xcscheme | 8 +-- .../xcshareddata/swiftpm/Package.resolved | 15 ++++++ macos/Runner/AppDelegate.swift | 4 ++ macos/Runner/MainFlutterWindow.swift | 1 + 8 files changed, 116 insertions(+), 13 deletions(-) create mode 100644 macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 5c25153..41f6fb7 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,13 +6,19 @@ import FlutterMacOS import Foundation import local_notifier +import package_info_plus +import path_provider_foundation import screen_retriever import shared_preferences_foundation +import url_launcher_macos import window_manager func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { LocalNotifierPlugin.register(with: registry.registrar(forPlugin: "LocalNotifierPlugin")) + FPPPackageInfoPlusPlugin.register(with: registry.registrar(forPlugin: "FPPPackageInfoPlusPlugin")) + PathProviderPlugin.register(with: registry.registrar(forPlugin: "PathProviderPlugin")) ScreenRetrieverPlugin.register(with: registry.registrar(forPlugin: "ScreenRetrieverPlugin")) SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) + UrlLauncherPlugin.register(with: registry.registrar(forPlugin: "UrlLauncherPlugin")) WindowManagerPlugin.register(with: registry.registrar(forPlugin: "WindowManagerPlugin")) } diff --git a/macos/Podfile.lock b/macos/Podfile.lock index 6e87250..0e3d6b8 100644 --- a/macos/Podfile.lock +++ b/macos/Podfile.lock @@ -2,19 +2,29 @@ PODS: - FlutterMacOS (1.0.0) - local_notifier (0.1.0): - FlutterMacOS + - package_info_plus (0.0.1): + - FlutterMacOS + - path_provider_foundation (0.0.1): + - Flutter + - FlutterMacOS - screen_retriever (0.0.1): - FlutterMacOS - shared_preferences_foundation (0.0.1): - Flutter - FlutterMacOS + - url_launcher_macos (0.0.1): + - FlutterMacOS - window_manager (0.2.0): - FlutterMacOS DEPENDENCIES: - FlutterMacOS (from `Flutter/ephemeral`) - local_notifier (from `Flutter/ephemeral/.symlinks/plugins/local_notifier/macos`) + - package_info_plus (from `Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos`) + - path_provider_foundation (from `Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin`) - screen_retriever (from `Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos`) - shared_preferences_foundation (from `Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin`) + - url_launcher_macos (from `Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos`) - window_manager (from `Flutter/ephemeral/.symlinks/plugins/window_manager/macos`) EXTERNAL SOURCES: @@ -22,20 +32,29 @@ EXTERNAL SOURCES: :path: Flutter/ephemeral local_notifier: :path: Flutter/ephemeral/.symlinks/plugins/local_notifier/macos + package_info_plus: + :path: Flutter/ephemeral/.symlinks/plugins/package_info_plus/macos + path_provider_foundation: + :path: Flutter/ephemeral/.symlinks/plugins/path_provider_foundation/darwin screen_retriever: :path: Flutter/ephemeral/.symlinks/plugins/screen_retriever/macos shared_preferences_foundation: :path: Flutter/ephemeral/.symlinks/plugins/shared_preferences_foundation/darwin + url_launcher_macos: + :path: Flutter/ephemeral/.symlinks/plugins/url_launcher_macos/macos window_manager: :path: Flutter/ephemeral/.symlinks/plugins/window_manager/macos SPEC CHECKSUMS: FlutterMacOS: 8f6f14fa908a6fb3fba0cd85dbd81ec4b251fb24 - local_notifier: e9506bc66fc70311e8bc7291fb70f743c081e4ff - screen_retriever: 59634572a57080243dd1bf715e55b6c54f241a38 - shared_preferences_foundation: fcdcbc04712aee1108ac7fda236f363274528f78 - window_manager: 3a1844359a6295ab1e47659b1a777e36773cd6e8 + local_notifier: ebf072651e35ae5e47280ad52e2707375cb2ae4e + package_info_plus: f0052d280d17aa382b932f399edf32507174e870 + path_provider_foundation: 080d55be775b7414fd5a5ef3ac137b97b097e564 + screen_retriever: 4f97c103641aab8ce183fa5af3b87029df167936 + shared_preferences_foundation: 9e1978ff2562383bd5676f64ec4e9aa8fa06a6f7 + url_launcher_macos: 0fba8ddabfc33ce0a9afe7c5fef5aab3d8d2d673 + window_manager: 1d01fa7ac65a6e6f83b965471b1a7fdd3f06166c PODFILE CHECKSUM: 236401fc2c932af29a9fcf0e97baeeb2d750d367 -COCOAPODS: 1.15.0 +COCOAPODS: 1.16.2 diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index d873278..7750b29 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -28,6 +28,7 @@ 33CC10F62044A3C60003C045 /* MainMenu.xib in Resources */ = {isa = PBXBuildFile; fileRef = 33CC10F42044A3C60003C045 /* MainMenu.xib */; }; 33CC11132044BFA00003C045 /* MainFlutterWindow.swift in Sources */ = {isa = PBXBuildFile; fileRef = 33CC11122044BFA00003C045 /* MainFlutterWindow.swift */; }; 5E6D7E09B01AD7C2E9C2ADC6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6CC01A7C2A5975E2F0F99B7 /* Pods_RunnerTests.framework */; }; + BF4B68E82DC9D8490098731A /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = BF4B68E72DC9D8490098731A /* LaunchAtLogin */; }; F75DCD6FD57FD31A27565424 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF31BBBE5814D2E98AAE3EF3 /* Pods_Runner.framework */; }; /* End PBXBuildFile section */ @@ -68,7 +69,7 @@ 331C80D7294CF71000263BE5 /* RunnerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RunnerTests.swift; sourceTree = ""; }; 333000ED22D3DE5D00554162 /* Warnings.xcconfig */ = {isa = PBXFileReference; lastKnownFileType = text.xcconfig; path = Warnings.xcconfig; sourceTree = ""; }; 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GeneratedPluginRegistrant.swift; sourceTree = ""; }; - 33CC10ED2044A3C60003C045 /* eyes_care.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = eyes_care.app; sourceTree = BUILT_PRODUCTS_DIR; }; + 33CC10ED2044A3C60003C045 /* Eyes Care.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Eyes Care.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 33CC10F02044A3C60003C045 /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; }; 33CC10F22044A3C60003C045 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; name = Assets.xcassets; path = Runner/Assets.xcassets; sourceTree = ""; }; 33CC10F52044A3C60003C045 /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.xib; name = Base; path = Base.lproj/MainMenu.xib; sourceTree = ""; }; @@ -103,6 +104,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + BF4B68E82DC9D8490098731A /* LaunchAtLogin in Frameworks */, F75DCD6FD57FD31A27565424 /* Pods_Runner.framework in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; @@ -144,7 +146,7 @@ 33CC10EE2044A3C60003C045 /* Products */ = { isa = PBXGroup; children = ( - 33CC10ED2044A3C60003C045 /* eyes_care.app */, + 33CC10ED2044A3C60003C045 /* Eyes Care.app */, 331C80D5294CF71000263BE5 /* RunnerTests.xctest */, ); name = Products; @@ -204,7 +206,6 @@ 0F2BDB39F346DBCCBE5D8B9B /* Pods-RunnerTests.release.xcconfig */, 23BD65CBB4E032F4B13B0D42 /* Pods-RunnerTests.profile.xcconfig */, ); - name = Pods; path = Pods; sourceTree = ""; }; @@ -241,6 +242,7 @@ 33CC110E2044A8840003C045 /* Bundle Framework */, 3399D490228B24CF009A79C7 /* ShellScript */, 51E328072146A074882BC4D1 /* [CP] Embed Pods Frameworks */, + BF4B68E92DC9D85F0098731A /* Copy “Launch at Login Helper” */, ); buildRules = ( ); @@ -249,7 +251,7 @@ ); name = Runner; productName = Runner; - productReference = 33CC10ED2044A3C60003C045 /* eyes_care.app */; + productReference = 33CC10ED2044A3C60003C045 /* Eyes Care.app */; productType = "com.apple.product-type.application"; }; /* End PBXNativeTarget section */ @@ -291,6 +293,9 @@ Base, ); mainGroup = 33CC10E42044A3C60003C045; + packageReferences = ( + BF4B68E62DC9D8490098731A /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */, + ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; projectDirPath = ""; projectRoot = ""; @@ -399,6 +404,25 @@ shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n"; showEnvVarsInLog = 0; }; + BF4B68E92DC9D85F0098731A /* Copy “Launch at Login Helper” */ = { + isa = PBXShellScriptBuildPhase; + alwaysOutOfDate = 1; + buildActionMask = 2147483647; + files = ( + ); + inputFileListPaths = ( + ); + inputPaths = ( + ); + name = "Copy “Launch at Login Helper”"; + outputFileListPaths = ( + ); + outputPaths = ( + ); + runOnlyForDeploymentPostprocessing = 0; + shellPath = /bin/sh; + shellScript = "\"${BUILT_PRODUCTS_DIR}/LaunchAtLogin_LaunchAtLogin.bundle/Contents/Resources/copy-helper-swiftpm.sh\"\n"; + }; F07000AA9180D3D0C7AAE4C1 /* [CP] Check Pods Manifest.lock */ = { isa = PBXShellScriptBuildPhase; buildActionMask = 2147483647; @@ -786,6 +810,25 @@ defaultConfigurationName = Release; }; /* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + BF4B68E62DC9D8490098731A /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/sindresorhus/LaunchAtLogin-Legacy"; + requirement = { + branch = main; + kind = branch; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + BF4B68E72DC9D8490098731A /* LaunchAtLogin */ = { + isa = XCSwiftPackageProductDependency; + package = BF4B68E62DC9D8490098731A /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */; + productName = LaunchAtLogin; + }; +/* End XCSwiftPackageProductDependency section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..f3281e1 --- /dev/null +++ b/macos/Runner.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", + "pins" : [ + { + "identity" : "launchatlogin-legacy", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", + "state" : { + "branch" : "main", + "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" + } + } + ], + "version" : 3 +} diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index e856148..4189a87 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -15,7 +15,7 @@ @@ -31,7 +31,7 @@ @@ -65,7 +65,7 @@ @@ -82,7 +82,7 @@ diff --git a/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..f3281e1 --- /dev/null +++ b/macos/Runner.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,15 @@ +{ + "originHash" : "78526aafdff3d7bb5fccbf1f4499af4d3ea88e16e8819bf30db26c60f51192d8", + "pins" : [ + { + "identity" : "launchatlogin-legacy", + "kind" : "remoteSourceControl", + "location" : "https://github.com/sindresorhus/LaunchAtLogin-Legacy", + "state" : { + "branch" : "main", + "revision" : "9a894d799269cb591037f9f9cb0961510d4dca81" + } + } + ], + "version" : 3 +} diff --git a/macos/Runner/AppDelegate.swift b/macos/Runner/AppDelegate.swift index a6f73a8..b965977 100644 --- a/macos/Runner/AppDelegate.swift +++ b/macos/Runner/AppDelegate.swift @@ -6,4 +6,8 @@ class AppDelegate: FlutterAppDelegate { override func applicationShouldTerminateAfterLastWindowClosed(_ sender: NSApplication) -> Bool { return false } + override func applicationSupportsSecureRestorableState(_ app: NSApplication) -> Bool + { + return true + } } diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 07cf760..7b8c5b2 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -1,5 +1,6 @@ import Cocoa import FlutterMacOS +import LaunchAtLogin import window_manager class MainFlutterWindow: NSWindow { From a7edcd20b43f5c062da41cca1b6021d539e3cff2 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 6 May 2025 06:46:40 +0100 Subject: [PATCH 06/18] fixed startUpModeEnabled issue --- lib/countdown_screen.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index c3a87a4..9d7ddc0 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -92,7 +92,7 @@ class CountdownScreenState extends State with WindowListener { forceModeEnabled.value = value ?? false; }); PreferenceService.getBool(PreferenceService.startupModeKey).then((value) { - forceModeEnabled.value = value ?? false; + startUpModeEnabled.value = value ?? false; }); } From 967528d4f5dcd026a3c069af8fab86a480197976 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 6 May 2025 06:52:21 +0100 Subject: [PATCH 07/18] Updated primary color --- lib/main.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/main.dart b/lib/main.dart index b0e950e..c037164 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -34,7 +34,7 @@ class CareYourEyes extends StatelessWidget { return ValueListenableBuilder( valueListenable: themeNotifier, builder: (context, _, __) { - const primaryColor = Color(0xFF6C63FF); + const primaryColor = Color(0xFF5BE0E5); const secondaryColor = Color(0xFF32CD32); return MaterialApp( From ba5940a6a74d96d92231ccd9de6b2794b6790851 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Tue, 6 May 2025 06:55:58 +0100 Subject: [PATCH 08/18] Separate settings widget into file --- lib/countdown_screen.dart | 73 ++------------------------------------- lib/widgets/settings.dart | 73 +++++++++++++++++++++++++++++++++++++++ 2 files changed, 75 insertions(+), 71 deletions(-) create mode 100644 lib/widgets/settings.dart diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index 9d7ddc0..e90e073 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -1,14 +1,12 @@ import 'package:eyes_care/main.dart'; -import 'package:eyes_care/widgets/edit_rule_button.dart'; +import 'package:eyes_care/widgets/settings.dart'; import 'package:eyes_care/widgets/work_break_info.dart'; import 'package:flutter/material.dart'; -import 'package:launch_at_startup/launch_at_startup.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:rocket_timer/rocket_timer.dart'; import 'package:window_manager/window_manager.dart'; import 'package:url_launcher/url_launcher.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'; @@ -327,71 +325,4 @@ class CountdownScreenState extends State with WindowListener { _timer!.kDuration = workDuration.inSeconds; setState(() {}); } -} - -class Settings extends StatelessWidget { - final int reminder; - final int breakTime; - final ValueNotifier forceModeEnabled; - final ValueNotifier startUpModeEnabled; - final Function(int, int) onConfirm; - - const Settings({ - Key? key, - required this.reminder, - required this.breakTime, - required this.forceModeEnabled, - required this.onConfirm, - required this.startUpModeEnabled, - }) : super(key: key); - - @override - Widget build(BuildContext context) { - return Padding( - padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 8.0), - child: Column( - mainAxisSize: MainAxisSize.min, - spacing: 16.0, - children: [ - EditRuleButton( - reminder: reminder, - breakTime: breakTime, - onConfirm: onConfirm, - ), - SwitcherSetting( - enabled: forceModeEnabled, - icon: Icons.lock_rounded, - title: "Force Mode", - subtitle: "Prevent window minimization during breaks", - onChanged: _onUpdateForceMode, - ), - SwitcherSetting( - enabled: startUpModeEnabled, - icon: Icons.start, - title: "Startup at Login", - subtitle: "Launch application automatically at system startup", - onChanged: _onUpdateStartupMode, - ), - ], - ), - ); - } - - void _onUpdateForceMode(bool? value) { - if (value == null) return; - PreferenceService.setBool(PreferenceService.forceModeKey, value); - forceModeEnabled.value = value; - windowManager.setFullScreen(false); - } - - void _onUpdateStartupMode(bool? value) { - if (value == null) return; - startUpModeEnabled.value = value; - PreferenceService.setBool(PreferenceService.startupModeKey, value); - if (value) { - launchAtStartup.disable(); - } else { - launchAtStartup.disable(); - } - } -} +} \ No newline at end of file diff --git a/lib/widgets/settings.dart b/lib/widgets/settings.dart new file mode 100644 index 0000000..743dc53 --- /dev/null +++ b/lib/widgets/settings.dart @@ -0,0 +1,73 @@ +import 'package:eyes_care/shared_pref.dart'; +import 'package:eyes_care/widgets/edit_rule_button.dart'; +import 'package:eyes_care/widgets/force_mode_check_box.dart'; +import 'package:flutter/material.dart'; +import 'package:launch_at_startup/launch_at_startup.dart'; +import 'package:window_manager/window_manager.dart'; + +class Settings extends StatelessWidget { + final int reminder; + final int breakTime; + final ValueNotifier forceModeEnabled; + final ValueNotifier startUpModeEnabled; + final Function(int, int) onConfirm; + + const Settings({ + Key? key, + required this.reminder, + required this.breakTime, + required this.forceModeEnabled, + required this.onConfirm, + required this.startUpModeEnabled, + }) : super(key: key); + + @override + Widget build(BuildContext context) { + return Padding( + padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), + child: Column( + mainAxisSize: MainAxisSize.min, + spacing: 16.0, + children: [ + EditRuleButton( + reminder: reminder, + breakTime: breakTime, + onConfirm: onConfirm, + ), + SwitcherSetting( + enabled: forceModeEnabled, + icon: Icons.lock_rounded, + title: "Force Mode", + subtitle: "Prevent window minimization during breaks", + onChanged: _onUpdateForceMode, + ), + SwitcherSetting( + enabled: startUpModeEnabled, + icon: Icons.start, + title: "Startup at Login", + subtitle: "Launch application automatically at system startup", + onChanged: _onUpdateStartupMode, + ), + ], + ), + ); + } + + void _onUpdateForceMode(bool? value) { + if (value == null) return; + PreferenceService.setBool(PreferenceService.forceModeKey, value); + forceModeEnabled.value = value; + windowManager.setFullScreen(false); + } + + void _onUpdateStartupMode(bool? value) { + if (value == null) return; + startUpModeEnabled.value = value; + PreferenceService.setBool(PreferenceService.startupModeKey, value); + if (value) { + launchAtStartup.disable(); + } else { + launchAtStartup.disable(); + } + } +} From 22df59a315c2cf2fe25ef59cefa7e41954a9b103 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 7 May 2025 14:35:10 +0100 Subject: [PATCH 09/18] minor windows fix --- windows/runner/flutter_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index a7f984f..0823f27 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -28,7 +28,7 @@ bool FlutterWindow::OnCreate() { SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() { - + "" }); // Flutter can complete the first frame before the "show window" callback is From 49a4af65283c1904abbef664b983b5d13dedea93 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 7 May 2025 14:52:48 +0100 Subject: [PATCH 10/18] Revert "minor windows fix" This reverts commit 22df59a315c2cf2fe25ef59cefa7e41954a9b103. --- windows/runner/flutter_window.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/windows/runner/flutter_window.cpp b/windows/runner/flutter_window.cpp index 0823f27..a7f984f 100644 --- a/windows/runner/flutter_window.cpp +++ b/windows/runner/flutter_window.cpp @@ -28,7 +28,7 @@ bool FlutterWindow::OnCreate() { SetChildContent(flutter_controller_->view()->GetNativeWindow()); flutter_controller_->engine()->SetNextFrameCallback([&]() { - "" + }); // Flutter can complete the first frame before the "show window" callback is From 1a00a4148736637385c65b8bc676b3c1f1f45631 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Wed, 7 May 2025 15:52:45 +0100 Subject: [PATCH 11/18] Fix enable launch at startup setting bug --- lib/widgets/settings.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/widgets/settings.dart b/lib/widgets/settings.dart index 743dc53..0838637 100644 --- a/lib/widgets/settings.dart +++ b/lib/widgets/settings.dart @@ -65,7 +65,7 @@ class Settings extends StatelessWidget { startUpModeEnabled.value = value; PreferenceService.setBool(PreferenceService.startupModeKey, value); if (value) { - launchAtStartup.disable(); + launchAtStartup.enable(); } else { launchAtStartup.disable(); } From a3e0a309c35ce6af5d29add04008c02c35fc32d5 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Sat, 21 Jun 2025 16:44:10 +0100 Subject: [PATCH 12/18] save theme mode to shared preferences --- lib/countdown_screen.dart | 103 +++++++++++++++++++---------------- lib/main.dart | 109 +++++++++++++++++++++----------------- lib/shared_pref.dart | 10 ++++ 3 files changed, 127 insertions(+), 95 deletions(-) diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index e90e073..0ced69d 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -131,9 +131,10 @@ class CountdownScreenState extends State with WindowListener { Future showNotification() async { LocalNotification notification = LocalNotification( title: inBreak ? "Stay Focused 💪" : "Take a Moment 🌟", - body: inBreak - ? "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!", + body: + inBreak + ? "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(); @@ -175,8 +176,10 @@ class CountdownScreenState extends State with WindowListener { ), child: SafeArea( child: Padding( - padding: - const EdgeInsets.symmetric(horizontal: 20.0, vertical: 16.0), + padding: const EdgeInsets.symmetric( + horizontal: 20.0, + vertical: 16.0, + ), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ @@ -192,21 +195,25 @@ class CountdownScreenState extends State with WindowListener { ), const Spacer(), IconButton( - onPressed: () { - _showSettings(context); - }, - icon: const Icon(Icons.settings)), + onPressed: () { + _showSettings(context); + }, + icon: const Icon(Icons.settings), + ), IconButton( icon: Icon( themeNotifier.value == ThemeMode.light ? Icons.dark_mode_rounded : Icons.light_mode_rounded, ), - onPressed: () { - themeNotifier.value = - themeNotifier.value == ThemeMode.light - ? ThemeMode.dark - : ThemeMode.light; + onPressed: () async { + if (themeNotifier.value == ThemeMode.light) { + themeNotifier.value = ThemeMode.dark; + await PreferenceService.setThemeMode('dark'); + } else { + themeNotifier.value = ThemeMode.light; + await PreferenceService.setThemeMode('light'); + } }, ), ], @@ -218,34 +225,35 @@ class CountdownScreenState extends State with WindowListener { Column( children: [ RuleTimer(timer: _timer!, inBreak: inBreak), - WorkBreakInfo( - reminder: reminder, - breakTime: breakTime, - ), + WorkBreakInfo(reminder: reminder, breakTime: breakTime), Row( mainAxisAlignment: MainAxisAlignment.center, children: [ AnimatedBuilder( - animation: _timer!, - builder: (context, _) { - return IconButton( - icon: Icon(_timer!.status == TimerStatus.pause + 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(); - } - }, - ); - }), + : Icons.pause, + ), + onPressed: () { + if (_timer!.status == TimerStatus.pause) { + _timer!.start(); + } else { + _timer!.pause(); + } + }, + ); + }, + ), IconButton( - onPressed: _restartTimer, - icon: const Icon(Icons.restart_alt)) + onPressed: _restartTimer, + icon: const Icon(Icons.restart_alt), + ), ], - ) + ), ], ), const SizedBox(height: 32), @@ -259,15 +267,16 @@ class CountdownScreenState extends State with WindowListener { mainAxisSize: MainAxisSize.min, children: [ ValueListenableBuilder( - valueListenable: appVersion, - builder: (context, value, _) { - return Text( - 'v$value', - style: theme.textTheme.bodySmall?.copyWith( - color: theme.colorScheme.onSurfaceVariant, - ), - ); - }), + valueListenable: appVersion, + builder: (context, value, _) { + return Text( + 'v$value', + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + ), + ); + }, + ), const SizedBox(height: 4), MouseRegion( cursor: SystemMouseCursors.click, @@ -311,7 +320,9 @@ class CountdownScreenState extends State with WindowListener { }); PreferenceService.setDuration(min, sec); PreferenceService.setBool( - PreferenceService.forceModeKey, forceModeEnabled.value); + PreferenceService.forceModeKey, + forceModeEnabled.value, + ); Navigator.pop(context); }, ); @@ -325,4 +336,4 @@ class CountdownScreenState extends State with WindowListener { _timer!.kDuration = workDuration.inSeconds; setState(() {}); } -} \ No newline at end of file +} diff --git a/lib/main.dart b/lib/main.dart index c037164..f79dc01 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,6 +1,7 @@ import 'dart:io'; import 'package:eyes_care/countdown_screen.dart'; +import 'package:eyes_care/shared_pref.dart'; import 'package:flutter/material.dart'; import 'package:launch_at_startup/launch_at_startup.dart'; import 'package:package_info_plus/package_info_plus.dart'; @@ -13,15 +14,25 @@ Future initLaunchStartup() async { launchAtStartup.setup( appName: packageInfo.appName, appPath: Platform.resolvedExecutable, - // Set packageName parameter to support MSIX. - // TODO: update package name - packageName: 'com.example.keepYourEyes', + packageName: packageInfo.packageName, ); } +Future loadThemeMode() async { + final mode = await PreferenceService.getThemeMode(); + if (mode == 'dark') { + themeNotifier.value = ThemeMode.dark; + } else if (mode == 'light') { + themeNotifier.value = ThemeMode.light; + } else if (mode == 'system') { + themeNotifier.value = ThemeMode.system; + } +} + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); + await loadThemeMode(); runApp(const CareYourEyes()); } @@ -32,56 +43,56 @@ class CareYourEyes extends StatelessWidget { @override Widget build(BuildContext context) { return ValueListenableBuilder( - valueListenable: themeNotifier, - builder: (context, _, __) { - const primaryColor = Color(0xFF5BE0E5); - const secondaryColor = Color(0xFF32CD32); + valueListenable: themeNotifier, + builder: (context, _, __) { + const primaryColor = Color(0xFF5BE0E5); + const secondaryColor = Color(0xFF32CD32); - return MaterialApp( - title: 'Eyes Care', - debugShowCheckedModeBanner: false, - themeMode: themeNotifier.value, - theme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: primaryColor, - secondary: secondaryColor, - ), - textTheme: GoogleFonts.poppinsTextTheme(), - cardTheme: CardTheme( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - ), - appBarTheme: const AppBarTheme( - elevation: 0, - centerTitle: true, - backgroundColor: Colors.transparent, - ), + return MaterialApp( + title: 'Eyes Care', + debugShowCheckedModeBanner: false, + themeMode: themeNotifier.value, + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: primaryColor, + secondary: secondaryColor, ), - darkTheme: ThemeData.dark().copyWith( - colorScheme: ColorScheme.fromSeed( - seedColor: primaryColor, - secondary: secondaryColor, - brightness: Brightness.dark, + textTheme: GoogleFonts.poppinsTextTheme(), + cardTheme: CardThemeData( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), - textTheme: - GoogleFonts.poppinsTextTheme(ThemeData.dark().textTheme), - cardTheme: CardTheme( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - ), - ), - appBarTheme: const AppBarTheme( - elevation: 0, - centerTitle: true, - backgroundColor: Colors.transparent, + ), + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: true, + backgroundColor: Colors.transparent, + ), + ), + darkTheme: ThemeData.dark().copyWith( + colorScheme: ColorScheme.fromSeed( + seedColor: primaryColor, + secondary: secondaryColor, + brightness: Brightness.dark, + ), + textTheme: GoogleFonts.poppinsTextTheme(ThemeData.dark().textTheme), + cardTheme: CardThemeData( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), ), ), - home: const CountdownScreen(), - ); - }); + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: true, + backgroundColor: Colors.transparent, + ), + ), + home: const CountdownScreen(), + ); + }, + ); } } diff --git a/lib/shared_pref.dart b/lib/shared_pref.dart index 5dabf6d..56ef54d 100644 --- a/lib/shared_pref.dart +++ b/lib/shared_pref.dart @@ -5,6 +5,16 @@ class PreferenceService { static const startupModeKey = "startup_mode"; static const minutes = "minutes"; static const seconds = "seconds"; + static const themeModeKey = "theme_mode"; + static Future setThemeMode(String mode) async { + final prefs = await instance; + await prefs.setString(themeModeKey, mode); + } + + static Future getThemeMode() async { + final prefs = await instance; + return prefs.getString(themeModeKey); + } static Future get instance async => await SharedPreferences.getInstance(); From e33562a51fb378016925417b5b9bdf33ec933897 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 11:35:04 +0100 Subject: [PATCH 13/18] Fix startup at login issues and support main languages --- l10n.yaml | 5 + lib/countdown_screen.dart | 13 +- lib/l10n/app_ar.arb | 35 ++ lib/l10n/app_de.arb | 35 ++ lib/l10n/app_en.arb | 35 ++ lib/l10n/app_es.arb | 35 ++ lib/l10n/app_fr.arb | 35 ++ lib/l10n/app_hi.arb | 35 ++ lib/l10n/app_ja.arb | 35 ++ lib/l10n/app_localizations.dart | 373 ++++++++++++++++++ lib/l10n/app_localizations.dart.bak | 373 ++++++++++++++++++ lib/l10n/app_localizations_ar.dart | 110 ++++++ lib/l10n/app_localizations_de.dart | 113 ++++++ lib/l10n/app_localizations_en.dart | 112 ++++++ lib/l10n/app_localizations_es.dart | 111 ++++++ lib/l10n/app_localizations_fr.dart | 112 ++++++ lib/l10n/app_localizations_hi.dart | 112 ++++++ lib/l10n/app_localizations_ja.dart | 107 +++++ lib/l10n/app_localizations_pt.dart | 110 ++++++ lib/l10n/app_localizations_ru.dart | 112 ++++++ lib/l10n/app_localizations_tr.dart | 113 ++++++ lib/l10n/app_localizations_zh.dart | 106 +++++ lib/l10n/app_pt.arb | 35 ++ lib/l10n/app_ru.arb | 35 ++ lib/l10n/app_tr.arb | 35 ++ lib/l10n/app_zh.arb | 35 ++ lib/main.dart | 145 ++++--- lib/shared_pref.dart | 12 + lib/widgets/custom_slider.dart | 10 +- lib/widgets/duration_picker_dialog.dart | 16 +- lib/widgets/rule_text.dart | 27 +- lib/widgets/rule_timer.dart | 4 +- lib/widgets/settings.dart | 249 +++++++++++- lib/widgets/work_break_info.dart | 13 +- macos/Podfile | 2 +- macos/Runner.xcodeproj/project.pbxproj | 24 +- .../xcshareddata/xcschemes/Runner.xcscheme | 19 + macos/Runner/MainFlutterWindow.swift | 89 +++-- pubspec.yaml | 13 +- 39 files changed, 2871 insertions(+), 119 deletions(-) create mode 100644 l10n.yaml create mode 100644 lib/l10n/app_ar.arb create mode 100644 lib/l10n/app_de.arb create mode 100644 lib/l10n/app_en.arb create mode 100644 lib/l10n/app_es.arb create mode 100644 lib/l10n/app_fr.arb create mode 100644 lib/l10n/app_hi.arb create mode 100644 lib/l10n/app_ja.arb create mode 100644 lib/l10n/app_localizations.dart create mode 100644 lib/l10n/app_localizations.dart.bak create mode 100644 lib/l10n/app_localizations_ar.dart create mode 100644 lib/l10n/app_localizations_de.dart create mode 100644 lib/l10n/app_localizations_en.dart create mode 100644 lib/l10n/app_localizations_es.dart create mode 100644 lib/l10n/app_localizations_fr.dart create mode 100644 lib/l10n/app_localizations_hi.dart create mode 100644 lib/l10n/app_localizations_ja.dart create mode 100644 lib/l10n/app_localizations_pt.dart create mode 100644 lib/l10n/app_localizations_ru.dart create mode 100644 lib/l10n/app_localizations_tr.dart create mode 100644 lib/l10n/app_localizations_zh.dart create mode 100644 lib/l10n/app_pt.arb create mode 100644 lib/l10n/app_ru.arb create mode 100644 lib/l10n/app_tr.arb create mode 100644 lib/l10n/app_zh.arb diff --git a/l10n.yaml b/l10n.yaml new file mode 100644 index 0000000..a4d323c --- /dev/null +++ b/l10n.yaml @@ -0,0 +1,5 @@ +arb-dir: lib/l10n +template-arb-file: app_en.arb +output-localization-file: app_localizations.dart +output-class: AppLocalizations +nullable-getter: false diff --git a/lib/countdown_screen.dart b/lib/countdown_screen.dart index 0ced69d..eeb233a 100644 --- a/lib/countdown_screen.dart +++ b/lib/countdown_screen.dart @@ -1,3 +1,4 @@ +import 'package:eyes_care/l10n/app_localizations.dart'; import 'package:eyes_care/main.dart'; import 'package:eyes_care/widgets/settings.dart'; import 'package:eyes_care/widgets/work_break_info.dart'; @@ -129,12 +130,11 @@ class CountdownScreenState extends State with WindowListener { } Future showNotification() async { + final loc = AppLocalizations.of(context); LocalNotification notification = LocalNotification( - title: inBreak ? "Stay Focused 💪" : "Take a Moment 🌟", + title: inBreak ? loc.stayFocused : loc.takeAMoment, body: - inBreak - ? "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!", + inBreak ? loc.breakNotification : loc.workNotification, ); notification.onShow = _onShowNotification; notification.show(); @@ -160,6 +160,7 @@ class CountdownScreenState extends State with WindowListener { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final loc = AppLocalizations.of(context); return Scaffold( backgroundColor: Colors.transparent, @@ -187,7 +188,7 @@ class CountdownScreenState extends State with WindowListener { Row( children: [ Text( - 'Eyes Care', + loc.appTitle, style: theme.textTheme.headlineSmall?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.primary, @@ -285,7 +286,7 @@ class CountdownScreenState extends State with WindowListener { launchUrl(Uri.parse('https://bixat.dev')); }, child: Text( - 'Powered by bixat.dev team', + loc.poweredBy, style: theme.textTheme.bodySmall?.copyWith( color: theme.colorScheme.onSurfaceVariant, decoration: TextDecoration.underline, diff --git a/lib/l10n/app_ar.arb b/lib/l10n/app_ar.arb new file mode 100644 index 0000000..33c53f4 --- /dev/null +++ b/lib/l10n/app_ar.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ar", + "appTitle": "راحة عينيك", + "forceMode": "الوضع الإلزامي", + "forceModeSubtitle": "منع تصغير النافذة أثناء فترات الراحة", + "startupAtLogin": "البدء عند تسجيل الدخول", + "startupAtLoginSubtitle": "تشغيل التطبيق تلقائيًا عند بدء تشغيل النظام", + "work": "عمل", + "breakPeriod": "راحة", + "breakTime": "وقت الراحة", + "ruleTitle": "قاعدة 20-20-20", + "ruleSubtitle": "امنح عينيك راحة باتباع قاعدة 20-20-20", + "ruleEvery20Minutes": "كل 20 دقيقة،", + "ruleLookAway": "انظر بعيدًا عن شاشتك وركز على شيء ما", + "rule20FeetAway": "على بعد 20 قدمًا", + "ruleFor": "لمدة", + "rule20Seconds": "20 ثانية.", + "ruleExplanation": "هذا يساعد على تقليل إجهاد العين الناجم عن الاستخدام المطول للشاشة.", + "settings": "الإعدادات", + "editRule": "تعديل القاعدة", + "save": "حفظ", + "cancel": "إلغاء", + "minutes": "دقائق", + "seconds": "ثواني", + "stayFocused": "ابقَ مركزًا 💪", + "takeAMoment": "خذ لحظة 🌟", + "breakNotification": "حافظ على نظرك على الشاشة. تذكر، كل 20 دقيقة، خذ استراحة لمدة 20 ثانية بالنظر إلى شيء على بعد 20 قدمًا.", + "workNotification": "ابتعد عن الشاشة وركز على شيء ما على بعد 20 قدمًا لمدة 20 ثانية. ستشكرك عيناك!", + "poweredBy": "بواسطة فريق bixat.dev", + "language": "اللغة", + "theme": "السمة", + "light": "فاتح", + "dark": "داكن", + "system": "النظام" +} diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb new file mode 100644 index 0000000..9350674 --- /dev/null +++ b/lib/l10n/app_de.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "de", + "appTitle": "Augenpflege", + "forceMode": "Erzwungener Modus", + "forceModeSubtitle": "Verhindern der Fensterminimierung während der Pausen", + "startupAtLogin": "Start beim Anmelden", + "startupAtLoginSubtitle": "Anwendung automatisch beim Systemstart starten", + "work": "Arbeit", + "breakPeriod": "Pause", + "breakTime": "Pausenzeit", + "ruleTitle": "20-20-20 Regel", + "ruleSubtitle": "Gönnen Sie Ihren Augen eine Pause mit der 20-20-20 Regel", + "ruleEvery20Minutes": "Alle 20 Minuten,", + "ruleLookAway": "blicken Sie von Ihrem Bildschirm weg und fokussieren Sie etwas", + "rule20FeetAway": "in 20 Fuß Entfernung", + "ruleFor": "für", + "rule20Seconds": "20 Sekunden.", + "ruleExplanation": "Dies hilft, die durch längere Bildschirmnutzung verursachte Augenbelastung zu reduzieren.", + "settings": "Einstellungen", + "editRule": "Regel Bearbeiten", + "save": "Speichern", + "cancel": "Abbrechen", + "minutes": "Minuten", + "seconds": "Sekunden", + "stayFocused": "Bleiben Sie Fokussiert 💪", + "takeAMoment": "Nehmen Sie sich einen Moment 🌟", + "breakNotification": "Halten Sie Ihren Blick auf dem Bildschirm. Denken Sie daran, alle 20 Minuten eine 20-Sekunden-Pause zu machen und etwas in 20 Fuß Entfernung zu betrachten.", + "workNotification": "Treten Sie vom Bildschirm zurück und fokussieren Sie etwas in 20 Fuß Entfernung für 20 Sekunden. Ihre Augen werden es Ihnen danken!", + "poweredBy": "Bereitgestellt vom bixat.dev Team", + "language": "Sprache", + "theme": "Thema", + "light": "Hell", + "dark": "Dunkel", + "system": "System" +} diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb new file mode 100644 index 0000000..e0f892e --- /dev/null +++ b/lib/l10n/app_en.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "en", + "appTitle": "Eyes Care", + "forceMode": "Force Mode", + "forceModeSubtitle": "Prevent window minimization during breaks", + "startupAtLogin": "Startup at Login", + "startupAtLoginSubtitle": "Launch application automatically at system startup", + "work": "work", + "breakPeriod": "break", + "breakTime": "Break Time", + "ruleTitle": "20-20-20 Rule", + "ruleSubtitle": "Give your eyes a rest by following the 20-20-20 rule", + "ruleEvery20Minutes": "Every 20 minutes,", + "ruleLookAway": "look away from your screen and focus on something", + "rule20FeetAway": "20 feet away", + "ruleFor": "for", + "rule20Seconds": "20 seconds.", + "ruleExplanation": "This helps reduce eye strain caused by prolonged screen use.", + "settings": "Settings", + "editRule": "Edit Rule", + "save": "Save", + "cancel": "Cancel", + "minutes": "Minutes", + "seconds": "Seconds", + "stayFocused": "Stay Focused 💪", + "takeAMoment": "Take a Moment 🌟", + "breakNotification": "Keep your gaze on the screen. Remember, every 20 minutes, take a 20-second break looking at something 20 feet away.", + "workNotification": "Step back from the screen and focus on something 20 feet away for 20 seconds. Your eyes will thank you!", + "poweredBy": "Powered by bixat.dev team", + "language": "Language", + "theme": "Theme", + "light": "Light", + "dark": "Dark", + "system": "System" +} diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb new file mode 100644 index 0000000..d19fd6f --- /dev/null +++ b/lib/l10n/app_es.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "es", + "appTitle": "Cuidado de Ojos", + "forceMode": "Modo Forzado", + "forceModeSubtitle": "Evitar minimizar la ventana durante los descansos", + "startupAtLogin": "Iniciar al Iniciar Sesión", + "startupAtLoginSubtitle": "Iniciar aplicación automáticamente al iniciar el sistema", + "work": "trabajo", + "breakPeriod": "descanso", + "breakTime": "Tiempo de Descanso", + "ruleTitle": "Regla 20-20-20", + "ruleSubtitle": "Descansa tus ojos siguiendo la regla 20-20-20", + "ruleEvery20Minutes": "Cada 20 minutos,", + "ruleLookAway": "aparta la mirada de la pantalla y enfoca algo", + "rule20FeetAway": "a 20 pies de distancia", + "ruleFor": "durante", + "rule20Seconds": "20 segundos.", + "ruleExplanation": "Esto ayuda a reducir la fatiga visual causada por el uso prolongado de la pantalla.", + "settings": "Configuración", + "editRule": "Editar Regla", + "save": "Guardar", + "cancel": "Cancelar", + "minutes": "Minutos", + "seconds": "Segundos", + "stayFocused": "Mantente Concentrado 💪", + "takeAMoment": "Tómate un Momento 🌟", + "breakNotification": "Mantén tu mirada en la pantalla. Recuerda, cada 20 minutos, toma un descanso de 20 segundos mirando algo a 20 pies de distancia.", + "workNotification": "Aléjate de la pantalla y enfoca algo a 20 pies de distancia durante 20 segundos. ¡Tus ojos te lo agradecerán!", + "poweredBy": "Desarrollado por el equipo de bixat.dev", + "language": "Idioma", + "theme": "Tema", + "light": "Claro", + "dark": "Oscuro", + "system": "Sistema" +} diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb new file mode 100644 index 0000000..7e7a5f2 --- /dev/null +++ b/lib/l10n/app_fr.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "fr", + "appTitle": "Soin des Yeux", + "forceMode": "Mode Forcé", + "forceModeSubtitle": "Empêcher la réduction de la fenêtre pendant les pauses", + "startupAtLogin": "Démarrage à la Connexion", + "startupAtLoginSubtitle": "Lancer l'application automatiquement au démarrage du système", + "work": "travail", + "breakPeriod": "pause", + "breakTime": "Temps de Pause", + "ruleTitle": "Règle 20-20-20", + "ruleSubtitle": "Reposez vos yeux en suivant la règle 20-20-20", + "ruleEvery20Minutes": "Toutes les 20 minutes,", + "ruleLookAway": "détournez le regard de votre écran et concentrez-vous sur quelque chose", + "rule20FeetAway": "à 20 pieds de distance", + "ruleFor": "pendant", + "rule20Seconds": "20 secondes.", + "ruleExplanation": "Cela aide à réduire la fatigue oculaire causée par une utilisation prolongée de l'écran.", + "settings": "Paramètres", + "editRule": "Modifier la Règle", + "save": "Enregistrer", + "cancel": "Annuler", + "minutes": "Minutes", + "seconds": "Secondes", + "stayFocused": "Restez Concentré 💪", + "takeAMoment": "Prenez un Moment 🌟", + "breakNotification": "Gardez votre regard sur l'écran. Rappelez-vous, toutes les 20 minutes, faites une pause de 20 secondes en regardant quelque chose à 20 pieds.", + "workNotification": "Éloignez-vous de l'écran et concentrez-vous sur quelque chose à 20 pieds pendant 20 secondes. Vos yeux vous remercieront!", + "poweredBy": "Propulsé par l'équipe bixat.dev", + "language": "Langue", + "theme": "Thème", + "light": "Clair", + "dark": "Sombre", + "system": "Système" +} diff --git a/lib/l10n/app_hi.arb b/lib/l10n/app_hi.arb new file mode 100644 index 0000000..5d94598 --- /dev/null +++ b/lib/l10n/app_hi.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "hi", + "appTitle": "आंखों की देखभाल", + "forceMode": "फोर्स मोड", + "forceModeSubtitle": "ब्रेक के दौरान विंडो मिनिमाइजेशन को रोकें", + "startupAtLogin": "लॉगिन पर स्टार्टअप", + "startupAtLoginSubtitle": "सिस्टम स्टार्टअप पर एप्लिकेशन को स्वचालित रूप से लॉन्च करें", + "work": "काम", + "breakPeriod": "विश्राम", + "breakTime": "विश्राम समय", + "ruleTitle": "20-20-20 नियम", + "ruleSubtitle": "20-20-20 नियम का पालन करके अपनी आंखों को आराम दें", + "ruleEvery20Minutes": "हर 20 मिनट में,", + "ruleLookAway": "अपनी स्क्रीन से नजर हटाएं और किसी चीज पर ध्यान दें", + "rule20FeetAway": "20 फीट दूर", + "ruleFor": "के लिए", + "rule20Seconds": "20 सेकंड।", + "ruleExplanation": "यह लंबे समय तक स्क्रीन के उपयोग के कारण होने वाले आंखों के तनाव को कम करने में मदद करता है।", + "settings": "सेटिंग्स", + "editRule": "नियम संपादित करें", + "save": "सहेजें", + "cancel": "रद्द करें", + "minutes": "मिनट", + "seconds": "सेकंड", + "stayFocused": "केंद्रित रहें 💪", + "takeAMoment": "एक पल लें 🌟", + "breakNotification": "अपनी नजर स्क्रीन पर रखें। याद रखें, हर 20 मिनट में, 20 फीट दूर किसी चीज को देखते हुए 20 सेकंड का ब्रेक लें।", + "workNotification": "स्क्रीन से पीछे हटें और 20 सेकंड के लिए 20 फीट दूर किसी चीज पर ध्यान केंद्रित करें। आपकी आंखें धन्यवाद देंगी!", + "poweredBy": "bixat.dev टीम द्वारा संचालित", + "language": "भाषा", + "theme": "थीम", + "light": "लाइट", + "dark": "डार्क", + "system": "सिस्टम" +} diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb new file mode 100644 index 0000000..90c324c --- /dev/null +++ b/lib/l10n/app_ja.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ja", + "appTitle": "アイケア", + "forceMode": "フォースモード", + "forceModeSubtitle": "休憩中にウィンドウを最小化できないようにする", + "startupAtLogin": "ログイン時に起動", + "startupAtLoginSubtitle": "システム起動時にアプリを自動的に起動", + "work": "作業", + "breakPeriod": "休憩", + "breakTime": "休憩時間", + "ruleTitle": "20-20-20 ルール", + "ruleSubtitle": "20-20-20 ルールに従って目を休めましょう", + "ruleEvery20Minutes": "20 分ごとに、", + "ruleLookAway": "画面から目を離して、何かに焦点を当ててください", + "rule20FeetAway": "20 フィート先", + "ruleFor": "間", + "rule20Seconds": "20 秒間。", + "ruleExplanation": "これは、長時間の画面使用による眼精疲労を軽減するのに役立ちます。", + "settings": "設定", + "editRule": "ルールを編集", + "save": "保存", + "cancel": "キャンセル", + "minutes": "分", + "seconds": "秒", + "stayFocused": "集中し続けて 💪", + "takeAMoment": "少し休憩 🌟", + "breakNotification": "画面に視線を向け続けましょう。覚えておいてください、20 分ごとに、20 フィート先を 20 秒間見て休憩しましょう。", + "workNotification": "画面から離れて、20 フィート先を 20 秒間見つめましょう。目が感謝します!", + "poweredBy": "bixat.dev チームによって提供", + "language": "言語", + "theme": "テーマ", + "light": "ライト", + "dark": "ダーク", + "system": "システム" +} diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart new file mode 100644 index 0000000..0957cc4 --- /dev/null +++ b/lib/l10n/app_localizations.dart @@ -0,0 +1,373 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_ar.dart'; +import 'app_localizations_de.dart'; +import 'app_localizations_en.dart'; +import 'app_localizations_es.dart'; +import 'app_localizations_fr.dart'; +import 'app_localizations_hi.dart'; +import 'app_localizations_ja.dart'; +import 'app_localizations_pt.dart'; +import 'app_localizations_ru.dart'; +import 'app_localizations_tr.dart'; +import 'app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('ar'), + Locale('de'), + Locale('en'), + Locale('es'), + Locale('fr'), + Locale('hi'), + Locale('ja'), + Locale('pt'), + Locale('ru'), + Locale('tr'), + Locale('zh'), + ]; + + /// No description provided for @appTitle. + /// + /// In en, this message translates to: + /// **'Eyes Care'** + String get appTitle; + + /// No description provided for @forceMode. + /// + /// In en, this message translates to: + /// **'Force Mode'** + String get forceMode; + + /// No description provided for @forceModeSubtitle. + /// + /// In en, this message translates to: + /// **'Prevent window minimization during breaks'** + String get forceModeSubtitle; + + /// No description provided for @startupAtLogin. + /// + /// In en, this message translates to: + /// **'Startup at Login'** + String get startupAtLogin; + + /// No description provided for @startupAtLoginSubtitle. + /// + /// In en, this message translates to: + /// **'Launch application automatically at system startup'** + String get startupAtLoginSubtitle; + + /// No description provided for @work. + /// + /// In en, this message translates to: + /// **'work'** + String get work; + + /// No description provided for @breakPeriod. + /// + /// In en, this message translates to: + /// **'break'** + String get breakPeriod; + + /// No description provided for @breakTime. + /// + /// In en, this message translates to: + /// **'Break Time'** + String get breakTime; + + /// No description provided for @ruleTitle. + /// + /// In en, this message translates to: + /// **'20-20-20 Rule'** + String get ruleTitle; + + /// No description provided for @ruleSubtitle. + /// + /// In en, this message translates to: + /// **'Give your eyes a rest by following the 20-20-20 rule'** + String get ruleSubtitle; + + /// No description provided for @ruleEvery20Minutes. + /// + /// In en, this message translates to: + /// **'Every 20 minutes,'** + String get ruleEvery20Minutes; + + /// No description provided for @ruleLookAway. + /// + /// In en, this message translates to: + /// **'look away from your screen and focus on something'** + String get ruleLookAway; + + /// No description provided for @rule20FeetAway. + /// + /// In en, this message translates to: + /// **'20 feet away'** + String get rule20FeetAway; + + /// No description provided for @ruleFor. + /// + /// In en, this message translates to: + /// **'for'** + String get ruleFor; + + /// No description provided for @rule20Seconds. + /// + /// In en, this message translates to: + /// **'20 seconds.'** + String get rule20Seconds; + + /// No description provided for @ruleExplanation. + /// + /// In en, this message translates to: + /// **'This helps reduce eye strain caused by prolonged screen use.'** + String get ruleExplanation; + + /// No description provided for @settings. + /// + /// In en, this message translates to: + /// **'Settings'** + String get settings; + + /// No description provided for @editRule. + /// + /// In en, this message translates to: + /// **'Edit Rule'** + String get editRule; + + /// No description provided for @save. + /// + /// In en, this message translates to: + /// **'Save'** + String get save; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @minutes. + /// + /// In en, this message translates to: + /// **'Minutes'** + String get minutes; + + /// No description provided for @seconds. + /// + /// In en, this message translates to: + /// **'Seconds'** + String get seconds; + + /// No description provided for @stayFocused. + /// + /// In en, this message translates to: + /// **'Stay Focused 💪'** + String get stayFocused; + + /// No description provided for @takeAMoment. + /// + /// In en, this message translates to: + /// **'Take a Moment 🌟'** + String get takeAMoment; + + /// No description provided for @breakNotification. + /// + /// In en, this message translates to: + /// **'Keep your gaze on the screen. Remember, every 20 minutes, take a 20-second break looking at something 20 feet away.'** + String get breakNotification; + + /// No description provided for @workNotification. + /// + /// In en, this message translates to: + /// **'Step back from the screen and focus on something 20 feet away for 20 seconds. Your eyes will thank you!'** + String get workNotification; + + /// No description provided for @poweredBy. + /// + /// In en, this message translates to: + /// **'Powered by bixat.dev team'** + String get poweredBy; + + /// No description provided for @language. + /// + /// In en, this message translates to: + /// **'Language'** + String get language; + + /// No description provided for @theme. + /// + /// In en, this message translates to: + /// **'Theme'** + String get theme; + + /// No description provided for @light. + /// + /// In en, this message translates to: + /// **'Light'** + String get light; + + /// No description provided for @dark. + /// + /// In en, this message translates to: + /// **'Dark'** + String get dark; + + /// No description provided for @system. + /// + /// In en, this message translates to: + /// **'System'** + String get system; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => [ + 'ar', + 'de', + 'en', + 'es', + 'fr', + 'hi', + 'ja', + 'pt', + 'ru', + 'tr', + 'zh', + ].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'ar': + return AppLocalizationsAr(); + case 'de': + return AppLocalizationsDe(); + case 'en': + return AppLocalizationsEn(); + case 'es': + return AppLocalizationsEs(); + case 'fr': + return AppLocalizationsFr(); + case 'hi': + return AppLocalizationsHi(); + case 'ja': + return AppLocalizationsJa(); + case 'pt': + return AppLocalizationsPt(); + case 'ru': + return AppLocalizationsRu(); + case 'tr': + return AppLocalizationsTr(); + case 'zh': + return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/lib/l10n/app_localizations.dart.bak b/lib/l10n/app_localizations.dart.bak new file mode 100644 index 0000000..0957cc4 --- /dev/null +++ b/lib/l10n/app_localizations.dart.bak @@ -0,0 +1,373 @@ +import 'dart:async'; + +import 'package:flutter/foundation.dart'; +import 'package:flutter/widgets.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:intl/intl.dart' as intl; + +import 'app_localizations_ar.dart'; +import 'app_localizations_de.dart'; +import 'app_localizations_en.dart'; +import 'app_localizations_es.dart'; +import 'app_localizations_fr.dart'; +import 'app_localizations_hi.dart'; +import 'app_localizations_ja.dart'; +import 'app_localizations_pt.dart'; +import 'app_localizations_ru.dart'; +import 'app_localizations_tr.dart'; +import 'app_localizations_zh.dart'; + +// ignore_for_file: type=lint + +/// Callers can lookup localized strings with an instance of AppLocalizations +/// returned by `AppLocalizations.of(context)`. +/// +/// Applications need to include `AppLocalizations.delegate()` in their app's +/// `localizationDelegates` list, and the locales they support in the app's +/// `supportedLocales` list. For example: +/// +/// ```dart +/// import 'l10n/app_localizations.dart'; +/// +/// return MaterialApp( +/// localizationsDelegates: AppLocalizations.localizationsDelegates, +/// supportedLocales: AppLocalizations.supportedLocales, +/// home: MyApplicationHome(), +/// ); +/// ``` +/// +/// ## Update pubspec.yaml +/// +/// Please make sure to update your pubspec.yaml to include the following +/// packages: +/// +/// ```yaml +/// dependencies: +/// # Internationalization support. +/// flutter_localizations: +/// sdk: flutter +/// intl: any # Use the pinned version from flutter_localizations +/// +/// # Rest of dependencies +/// ``` +/// +/// ## iOS Applications +/// +/// iOS applications define key application metadata, including supported +/// locales, in an Info.plist file that is built into the application bundle. +/// To configure the locales supported by your app, you’ll need to edit this +/// file. +/// +/// First, open your project’s ios/Runner.xcworkspace Xcode workspace file. +/// Then, in the Project Navigator, open the Info.plist file under the Runner +/// project’s Runner folder. +/// +/// Next, select the Information Property List item, select Add Item from the +/// Editor menu, then select Localizations from the pop-up menu. +/// +/// Select and expand the newly-created Localizations item then, for each +/// locale your application supports, add a new item and select the locale +/// you wish to add from the pop-up menu in the Value field. This list should +/// be consistent with the languages listed in the AppLocalizations.supportedLocales +/// property. +abstract class AppLocalizations { + AppLocalizations(String locale) + : localeName = intl.Intl.canonicalizedLocale(locale.toString()); + + final String localeName; + + static AppLocalizations of(BuildContext context) { + return Localizations.of(context, AppLocalizations)!; + } + + static const LocalizationsDelegate delegate = + _AppLocalizationsDelegate(); + + /// A list of this localizations delegate along with the default localizations + /// delegates. + /// + /// Returns a list of localizations delegates containing this delegate along with + /// GlobalMaterialLocalizations.delegate, GlobalCupertinoLocalizations.delegate, + /// and GlobalWidgetsLocalizations.delegate. + /// + /// Additional delegates can be added by appending to this list in + /// MaterialApp. This list does not have to be used at all if a custom list + /// of delegates is preferred or required. + static const List> localizationsDelegates = + >[ + delegate, + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + ]; + + /// A list of this localizations delegate's supported locales. + static const List supportedLocales = [ + Locale('ar'), + Locale('de'), + Locale('en'), + Locale('es'), + Locale('fr'), + Locale('hi'), + Locale('ja'), + Locale('pt'), + Locale('ru'), + Locale('tr'), + Locale('zh'), + ]; + + /// No description provided for @appTitle. + /// + /// In en, this message translates to: + /// **'Eyes Care'** + String get appTitle; + + /// No description provided for @forceMode. + /// + /// In en, this message translates to: + /// **'Force Mode'** + String get forceMode; + + /// No description provided for @forceModeSubtitle. + /// + /// In en, this message translates to: + /// **'Prevent window minimization during breaks'** + String get forceModeSubtitle; + + /// No description provided for @startupAtLogin. + /// + /// In en, this message translates to: + /// **'Startup at Login'** + String get startupAtLogin; + + /// No description provided for @startupAtLoginSubtitle. + /// + /// In en, this message translates to: + /// **'Launch application automatically at system startup'** + String get startupAtLoginSubtitle; + + /// No description provided for @work. + /// + /// In en, this message translates to: + /// **'work'** + String get work; + + /// No description provided for @breakPeriod. + /// + /// In en, this message translates to: + /// **'break'** + String get breakPeriod; + + /// No description provided for @breakTime. + /// + /// In en, this message translates to: + /// **'Break Time'** + String get breakTime; + + /// No description provided for @ruleTitle. + /// + /// In en, this message translates to: + /// **'20-20-20 Rule'** + String get ruleTitle; + + /// No description provided for @ruleSubtitle. + /// + /// In en, this message translates to: + /// **'Give your eyes a rest by following the 20-20-20 rule'** + String get ruleSubtitle; + + /// No description provided for @ruleEvery20Minutes. + /// + /// In en, this message translates to: + /// **'Every 20 minutes,'** + String get ruleEvery20Minutes; + + /// No description provided for @ruleLookAway. + /// + /// In en, this message translates to: + /// **'look away from your screen and focus on something'** + String get ruleLookAway; + + /// No description provided for @rule20FeetAway. + /// + /// In en, this message translates to: + /// **'20 feet away'** + String get rule20FeetAway; + + /// No description provided for @ruleFor. + /// + /// In en, this message translates to: + /// **'for'** + String get ruleFor; + + /// No description provided for @rule20Seconds. + /// + /// In en, this message translates to: + /// **'20 seconds.'** + String get rule20Seconds; + + /// No description provided for @ruleExplanation. + /// + /// In en, this message translates to: + /// **'This helps reduce eye strain caused by prolonged screen use.'** + String get ruleExplanation; + + /// No description provided for @settings. + /// + /// In en, this message translates to: + /// **'Settings'** + String get settings; + + /// No description provided for @editRule. + /// + /// In en, this message translates to: + /// **'Edit Rule'** + String get editRule; + + /// No description provided for @save. + /// + /// In en, this message translates to: + /// **'Save'** + String get save; + + /// No description provided for @cancel. + /// + /// In en, this message translates to: + /// **'Cancel'** + String get cancel; + + /// No description provided for @minutes. + /// + /// In en, this message translates to: + /// **'Minutes'** + String get minutes; + + /// No description provided for @seconds. + /// + /// In en, this message translates to: + /// **'Seconds'** + String get seconds; + + /// No description provided for @stayFocused. + /// + /// In en, this message translates to: + /// **'Stay Focused 💪'** + String get stayFocused; + + /// No description provided for @takeAMoment. + /// + /// In en, this message translates to: + /// **'Take a Moment 🌟'** + String get takeAMoment; + + /// No description provided for @breakNotification. + /// + /// In en, this message translates to: + /// **'Keep your gaze on the screen. Remember, every 20 minutes, take a 20-second break looking at something 20 feet away.'** + String get breakNotification; + + /// No description provided for @workNotification. + /// + /// In en, this message translates to: + /// **'Step back from the screen and focus on something 20 feet away for 20 seconds. Your eyes will thank you!'** + String get workNotification; + + /// No description provided for @poweredBy. + /// + /// In en, this message translates to: + /// **'Powered by bixat.dev team'** + String get poweredBy; + + /// No description provided for @language. + /// + /// In en, this message translates to: + /// **'Language'** + String get language; + + /// No description provided for @theme. + /// + /// In en, this message translates to: + /// **'Theme'** + String get theme; + + /// No description provided for @light. + /// + /// In en, this message translates to: + /// **'Light'** + String get light; + + /// No description provided for @dark. + /// + /// In en, this message translates to: + /// **'Dark'** + String get dark; + + /// No description provided for @system. + /// + /// In en, this message translates to: + /// **'System'** + String get system; +} + +class _AppLocalizationsDelegate + extends LocalizationsDelegate { + const _AppLocalizationsDelegate(); + + @override + Future load(Locale locale) { + return SynchronousFuture(lookupAppLocalizations(locale)); + } + + @override + bool isSupported(Locale locale) => [ + 'ar', + 'de', + 'en', + 'es', + 'fr', + 'hi', + 'ja', + 'pt', + 'ru', + 'tr', + 'zh', + ].contains(locale.languageCode); + + @override + bool shouldReload(_AppLocalizationsDelegate old) => false; +} + +AppLocalizations lookupAppLocalizations(Locale locale) { + // Lookup logic when only language code is specified. + switch (locale.languageCode) { + case 'ar': + return AppLocalizationsAr(); + case 'de': + return AppLocalizationsDe(); + case 'en': + return AppLocalizationsEn(); + case 'es': + return AppLocalizationsEs(); + case 'fr': + return AppLocalizationsFr(); + case 'hi': + return AppLocalizationsHi(); + case 'ja': + return AppLocalizationsJa(); + case 'pt': + return AppLocalizationsPt(); + case 'ru': + return AppLocalizationsRu(); + case 'tr': + return AppLocalizationsTr(); + case 'zh': + return AppLocalizationsZh(); + } + + throw FlutterError( + 'AppLocalizations.delegate failed to load unsupported locale "$locale". This is likely ' + 'an issue with the localizations generation tool. Please file an issue ' + 'on GitHub with a reproducible sample app and the gen-l10n configuration ' + 'that was used.', + ); +} diff --git a/lib/l10n/app_localizations_ar.dart b/lib/l10n/app_localizations_ar.dart new file mode 100644 index 0000000..41b97e2 --- /dev/null +++ b/lib/l10n/app_localizations_ar.dart @@ -0,0 +1,110 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Arabic (`ar`). +class AppLocalizationsAr extends AppLocalizations { + AppLocalizationsAr([String locale = 'ar']) : super(locale); + + @override + String get appTitle => 'راحة عينيك'; + + @override + String get forceMode => 'الوضع الإلزامي'; + + @override + String get forceModeSubtitle => 'منع تصغير النافذة أثناء فترات الراحة'; + + @override + String get startupAtLogin => 'البدء عند تسجيل الدخول'; + + @override + String get startupAtLoginSubtitle => + 'تشغيل التطبيق تلقائيًا عند بدء تشغيل النظام'; + + @override + String get work => 'عمل'; + + @override + String get breakPeriod => 'راحة'; + + @override + String get breakTime => 'وقت الراحة'; + + @override + String get ruleTitle => 'قاعدة 20-20-20'; + + @override + String get ruleSubtitle => 'امنح عينيك راحة باتباع قاعدة 20-20-20'; + + @override + String get ruleEvery20Minutes => 'كل 20 دقيقة،'; + + @override + String get ruleLookAway => 'انظر بعيدًا عن شاشتك وركز على شيء ما'; + + @override + String get rule20FeetAway => 'على بعد 20 قدمًا'; + + @override + String get ruleFor => 'لمدة'; + + @override + String get rule20Seconds => '20 ثانية.'; + + @override + String get ruleExplanation => + 'هذا يساعد على تقليل إجهاد العين الناجم عن الاستخدام المطول للشاشة.'; + + @override + String get settings => 'الإعدادات'; + + @override + String get editRule => 'تعديل القاعدة'; + + @override + String get save => 'حفظ'; + + @override + String get cancel => 'إلغاء'; + + @override + String get minutes => 'دقائق'; + + @override + String get seconds => 'ثواني'; + + @override + String get stayFocused => 'ابقَ مركزًا 💪'; + + @override + String get takeAMoment => 'خذ لحظة 🌟'; + + @override + String get breakNotification => + 'حافظ على نظرك على الشاشة. تذكر، كل 20 دقيقة، خذ استراحة لمدة 20 ثانية بالنظر إلى شيء على بعد 20 قدمًا.'; + + @override + String get workNotification => + 'ابتعد عن الشاشة وركز على شيء ما على بعد 20 قدمًا لمدة 20 ثانية. ستشكرك عيناك!'; + + @override + String get poweredBy => 'بواسطة فريق bixat.dev'; + + @override + String get language => 'اللغة'; + + @override + String get theme => 'السمة'; + + @override + String get light => 'فاتح'; + + @override + String get dark => 'داكن'; + + @override + String get system => 'النظام'; +} diff --git a/lib/l10n/app_localizations_de.dart b/lib/l10n/app_localizations_de.dart new file mode 100644 index 0000000..ec08c22 --- /dev/null +++ b/lib/l10n/app_localizations_de.dart @@ -0,0 +1,113 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for German (`de`). +class AppLocalizationsDe extends AppLocalizations { + AppLocalizationsDe([String locale = 'de']) : super(locale); + + @override + String get appTitle => 'Augenpflege'; + + @override + String get forceMode => 'Erzwungener Modus'; + + @override + String get forceModeSubtitle => + 'Verhindern der Fensterminimierung während der Pausen'; + + @override + String get startupAtLogin => 'Start beim Anmelden'; + + @override + String get startupAtLoginSubtitle => + 'Anwendung automatisch beim Systemstart starten'; + + @override + String get work => 'Arbeit'; + + @override + String get breakPeriod => 'Pause'; + + @override + String get breakTime => 'Pausenzeit'; + + @override + String get ruleTitle => '20-20-20 Regel'; + + @override + String get ruleSubtitle => + 'Gönnen Sie Ihren Augen eine Pause mit der 20-20-20 Regel'; + + @override + String get ruleEvery20Minutes => 'Alle 20 Minuten,'; + + @override + String get ruleLookAway => + 'blicken Sie von Ihrem Bildschirm weg und fokussieren Sie etwas'; + + @override + String get rule20FeetAway => 'in 20 Fuß Entfernung'; + + @override + String get ruleFor => 'für'; + + @override + String get rule20Seconds => '20 Sekunden.'; + + @override + String get ruleExplanation => + 'Dies hilft, die durch längere Bildschirmnutzung verursachte Augenbelastung zu reduzieren.'; + + @override + String get settings => 'Einstellungen'; + + @override + String get editRule => 'Regel Bearbeiten'; + + @override + String get save => 'Speichern'; + + @override + String get cancel => 'Abbrechen'; + + @override + String get minutes => 'Minuten'; + + @override + String get seconds => 'Sekunden'; + + @override + String get stayFocused => 'Bleiben Sie Fokussiert 💪'; + + @override + String get takeAMoment => 'Nehmen Sie sich einen Moment 🌟'; + + @override + String get breakNotification => + 'Halten Sie Ihren Blick auf dem Bildschirm. Denken Sie daran, alle 20 Minuten eine 20-Sekunden-Pause zu machen und etwas in 20 Fuß Entfernung zu betrachten.'; + + @override + String get workNotification => + 'Treten Sie vom Bildschirm zurück und fokussieren Sie etwas in 20 Fuß Entfernung für 20 Sekunden. Ihre Augen werden es Ihnen danken!'; + + @override + String get poweredBy => 'Bereitgestellt vom bixat.dev Team'; + + @override + String get language => 'Sprache'; + + @override + String get theme => 'Thema'; + + @override + String get light => 'Hell'; + + @override + String get dark => 'Dunkel'; + + @override + String get system => 'System'; +} diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart new file mode 100644 index 0000000..8262727 --- /dev/null +++ b/lib/l10n/app_localizations_en.dart @@ -0,0 +1,112 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for English (`en`). +class AppLocalizationsEn extends AppLocalizations { + AppLocalizationsEn([String locale = 'en']) : super(locale); + + @override + String get appTitle => 'Eyes Care'; + + @override + String get forceMode => 'Force Mode'; + + @override + String get forceModeSubtitle => 'Prevent window minimization during breaks'; + + @override + String get startupAtLogin => 'Startup at Login'; + + @override + String get startupAtLoginSubtitle => + 'Launch application automatically at system startup'; + + @override + String get work => 'work'; + + @override + String get breakPeriod => 'break'; + + @override + String get breakTime => 'Break Time'; + + @override + String get ruleTitle => '20-20-20 Rule'; + + @override + String get ruleSubtitle => + 'Give your eyes a rest by following the 20-20-20 rule'; + + @override + String get ruleEvery20Minutes => 'Every 20 minutes,'; + + @override + String get ruleLookAway => + 'look away from your screen and focus on something'; + + @override + String get rule20FeetAway => '20 feet away'; + + @override + String get ruleFor => 'for'; + + @override + String get rule20Seconds => '20 seconds.'; + + @override + String get ruleExplanation => + 'This helps reduce eye strain caused by prolonged screen use.'; + + @override + String get settings => 'Settings'; + + @override + String get editRule => 'Edit Rule'; + + @override + String get save => 'Save'; + + @override + String get cancel => 'Cancel'; + + @override + String get minutes => 'Minutes'; + + @override + String get seconds => 'Seconds'; + + @override + String get stayFocused => 'Stay Focused 💪'; + + @override + String get takeAMoment => 'Take a Moment 🌟'; + + @override + String get breakNotification => + 'Keep your gaze on the screen. Remember, every 20 minutes, take a 20-second break looking at something 20 feet away.'; + + @override + String get workNotification => + 'Step back from the screen and focus on something 20 feet away for 20 seconds. Your eyes will thank you!'; + + @override + String get poweredBy => 'Powered by bixat.dev team'; + + @override + String get language => 'Language'; + + @override + String get theme => 'Theme'; + + @override + String get light => 'Light'; + + @override + String get dark => 'Dark'; + + @override + String get system => 'System'; +} diff --git a/lib/l10n/app_localizations_es.dart b/lib/l10n/app_localizations_es.dart new file mode 100644 index 0000000..e4f90b2 --- /dev/null +++ b/lib/l10n/app_localizations_es.dart @@ -0,0 +1,111 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Spanish Castilian (`es`). +class AppLocalizationsEs extends AppLocalizations { + AppLocalizationsEs([String locale = 'es']) : super(locale); + + @override + String get appTitle => 'Cuidado de Ojos'; + + @override + String get forceMode => 'Modo Forzado'; + + @override + String get forceModeSubtitle => + 'Evitar minimizar la ventana durante los descansos'; + + @override + String get startupAtLogin => 'Iniciar al Iniciar Sesión'; + + @override + String get startupAtLoginSubtitle => + 'Iniciar aplicación automáticamente al iniciar el sistema'; + + @override + String get work => 'trabajo'; + + @override + String get breakPeriod => 'descanso'; + + @override + String get breakTime => 'Tiempo de Descanso'; + + @override + String get ruleTitle => 'Regla 20-20-20'; + + @override + String get ruleSubtitle => 'Descansa tus ojos siguiendo la regla 20-20-20'; + + @override + String get ruleEvery20Minutes => 'Cada 20 minutos,'; + + @override + String get ruleLookAway => 'aparta la mirada de la pantalla y enfoca algo'; + + @override + String get rule20FeetAway => 'a 20 pies de distancia'; + + @override + String get ruleFor => 'durante'; + + @override + String get rule20Seconds => '20 segundos.'; + + @override + String get ruleExplanation => + 'Esto ayuda a reducir la fatiga visual causada por el uso prolongado de la pantalla.'; + + @override + String get settings => 'Configuración'; + + @override + String get editRule => 'Editar Regla'; + + @override + String get save => 'Guardar'; + + @override + String get cancel => 'Cancelar'; + + @override + String get minutes => 'Minutos'; + + @override + String get seconds => 'Segundos'; + + @override + String get stayFocused => 'Mantente Concentrado 💪'; + + @override + String get takeAMoment => 'Tómate un Momento 🌟'; + + @override + String get breakNotification => + 'Mantén tu mirada en la pantalla. Recuerda, cada 20 minutos, toma un descanso de 20 segundos mirando algo a 20 pies de distancia.'; + + @override + String get workNotification => + 'Aléjate de la pantalla y enfoca algo a 20 pies de distancia durante 20 segundos. ¡Tus ojos te lo agradecerán!'; + + @override + String get poweredBy => 'Desarrollado por el equipo de bixat.dev'; + + @override + String get language => 'Idioma'; + + @override + String get theme => 'Tema'; + + @override + String get light => 'Claro'; + + @override + String get dark => 'Oscuro'; + + @override + String get system => 'Sistema'; +} diff --git a/lib/l10n/app_localizations_fr.dart b/lib/l10n/app_localizations_fr.dart new file mode 100644 index 0000000..63e6bf8 --- /dev/null +++ b/lib/l10n/app_localizations_fr.dart @@ -0,0 +1,112 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for French (`fr`). +class AppLocalizationsFr extends AppLocalizations { + AppLocalizationsFr([String locale = 'fr']) : super(locale); + + @override + String get appTitle => 'Soin des Yeux'; + + @override + String get forceMode => 'Mode Forcé'; + + @override + String get forceModeSubtitle => + 'Empêcher la réduction de la fenêtre pendant les pauses'; + + @override + String get startupAtLogin => 'Démarrage à la Connexion'; + + @override + String get startupAtLoginSubtitle => + 'Lancer l\'application automatiquement au démarrage du système'; + + @override + String get work => 'travail'; + + @override + String get breakPeriod => 'pause'; + + @override + String get breakTime => 'Temps de Pause'; + + @override + String get ruleTitle => 'Règle 20-20-20'; + + @override + String get ruleSubtitle => 'Reposez vos yeux en suivant la règle 20-20-20'; + + @override + String get ruleEvery20Minutes => 'Toutes les 20 minutes,'; + + @override + String get ruleLookAway => + 'détournez le regard de votre écran et concentrez-vous sur quelque chose'; + + @override + String get rule20FeetAway => 'à 20 pieds de distance'; + + @override + String get ruleFor => 'pendant'; + + @override + String get rule20Seconds => '20 secondes.'; + + @override + String get ruleExplanation => + 'Cela aide à réduire la fatigue oculaire causée par une utilisation prolongée de l\'écran.'; + + @override + String get settings => 'Paramètres'; + + @override + String get editRule => 'Modifier la Règle'; + + @override + String get save => 'Enregistrer'; + + @override + String get cancel => 'Annuler'; + + @override + String get minutes => 'Minutes'; + + @override + String get seconds => 'Secondes'; + + @override + String get stayFocused => 'Restez Concentré 💪'; + + @override + String get takeAMoment => 'Prenez un Moment 🌟'; + + @override + String get breakNotification => + 'Gardez votre regard sur l\'écran. Rappelez-vous, toutes les 20 minutes, faites une pause de 20 secondes en regardant quelque chose à 20 pieds.'; + + @override + String get workNotification => + 'Éloignez-vous de l\'écran et concentrez-vous sur quelque chose à 20 pieds pendant 20 secondes. Vos yeux vous remercieront!'; + + @override + String get poweredBy => 'Propulsé par l\'équipe bixat.dev'; + + @override + String get language => 'Langue'; + + @override + String get theme => 'Thème'; + + @override + String get light => 'Clair'; + + @override + String get dark => 'Sombre'; + + @override + String get system => 'Système'; +} diff --git a/lib/l10n/app_localizations_hi.dart b/lib/l10n/app_localizations_hi.dart new file mode 100644 index 0000000..e0d6b41 --- /dev/null +++ b/lib/l10n/app_localizations_hi.dart @@ -0,0 +1,112 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Hindi (`hi`). +class AppLocalizationsHi extends AppLocalizations { + AppLocalizationsHi([String locale = 'hi']) : super(locale); + + @override + String get appTitle => 'आंखों की देखभाल'; + + @override + String get forceMode => 'फोर्स मोड'; + + @override + String get forceModeSubtitle => 'ब्रेक के दौरान विंडो मिनिमाइजेशन को रोकें'; + + @override + String get startupAtLogin => 'लॉगिन पर स्टार्टअप'; + + @override + String get startupAtLoginSubtitle => + 'सिस्टम स्टार्टअप पर एप्लिकेशन को स्वचालित रूप से लॉन्च करें'; + + @override + String get work => 'काम'; + + @override + String get breakPeriod => 'विश्राम'; + + @override + String get breakTime => 'विश्राम समय'; + + @override + String get ruleTitle => '20-20-20 नियम'; + + @override + String get ruleSubtitle => + '20-20-20 नियम का पालन करके अपनी आंखों को आराम दें'; + + @override + String get ruleEvery20Minutes => 'हर 20 मिनट में,'; + + @override + String get ruleLookAway => + 'अपनी स्क्रीन से नजर हटाएं और किसी चीज पर ध्यान दें'; + + @override + String get rule20FeetAway => '20 फीट दूर'; + + @override + String get ruleFor => 'के लिए'; + + @override + String get rule20Seconds => '20 सेकंड।'; + + @override + String get ruleExplanation => + 'यह लंबे समय तक स्क्रीन के उपयोग के कारण होने वाले आंखों के तनाव को कम करने में मदद करता है।'; + + @override + String get settings => 'सेटिंग्स'; + + @override + String get editRule => 'नियम संपादित करें'; + + @override + String get save => 'सहेजें'; + + @override + String get cancel => 'रद्द करें'; + + @override + String get minutes => 'मिनट'; + + @override + String get seconds => 'सेकंड'; + + @override + String get stayFocused => 'केंद्रित रहें 💪'; + + @override + String get takeAMoment => 'एक पल लें 🌟'; + + @override + String get breakNotification => + 'अपनी नजर स्क्रीन पर रखें। याद रखें, हर 20 मिनट में, 20 फीट दूर किसी चीज को देखते हुए 20 सेकंड का ब्रेक लें।'; + + @override + String get workNotification => + 'स्क्रीन से पीछे हटें और 20 सेकंड के लिए 20 फीट दूर किसी चीज पर ध्यान केंद्रित करें। आपकी आंखें धन्यवाद देंगी!'; + + @override + String get poweredBy => 'bixat.dev टीम द्वारा संचालित'; + + @override + String get language => 'भाषा'; + + @override + String get theme => 'थीम'; + + @override + String get light => 'लाइट'; + + @override + String get dark => 'डार्क'; + + @override + String get system => 'सिस्टम'; +} diff --git a/lib/l10n/app_localizations_ja.dart b/lib/l10n/app_localizations_ja.dart new file mode 100644 index 0000000..2d2dbc0 --- /dev/null +++ b/lib/l10n/app_localizations_ja.dart @@ -0,0 +1,107 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Japanese (`ja`). +class AppLocalizationsJa extends AppLocalizations { + AppLocalizationsJa([String locale = 'ja']) : super(locale); + + @override + String get appTitle => 'アイケア'; + + @override + String get forceMode => 'フォースモード'; + + @override + String get forceModeSubtitle => '休憩中にウィンドウを最小化できないようにする'; + + @override + String get startupAtLogin => 'ログイン時に起動'; + + @override + String get startupAtLoginSubtitle => 'システム起動時にアプリを自動的に起動'; + + @override + String get work => '作業'; + + @override + String get breakPeriod => '休憩'; + + @override + String get breakTime => '休憩時間'; + + @override + String get ruleTitle => '20-20-20 ルール'; + + @override + String get ruleSubtitle => '20-20-20 ルールに従って目を休めましょう'; + + @override + String get ruleEvery20Minutes => '20 分ごとに、'; + + @override + String get ruleLookAway => '画面から目を離して、何かに焦点を当ててください'; + + @override + String get rule20FeetAway => '20 フィート先'; + + @override + String get ruleFor => '間'; + + @override + String get rule20Seconds => '20 秒間。'; + + @override + String get ruleExplanation => 'これは、長時間の画面使用による眼精疲労を軽減するのに役立ちます。'; + + @override + String get settings => '設定'; + + @override + String get editRule => 'ルールを編集'; + + @override + String get save => '保存'; + + @override + String get cancel => 'キャンセル'; + + @override + String get minutes => '分'; + + @override + String get seconds => '秒'; + + @override + String get stayFocused => '集中し続けて 💪'; + + @override + String get takeAMoment => '少し休憩 🌟'; + + @override + String get breakNotification => + '画面に視線を向け続けましょう。覚えておいてください、20 分ごとに、20 フィート先を 20 秒間見て休憩しましょう。'; + + @override + String get workNotification => '画面から離れて、20 フィート先を 20 秒間見つめましょう。目が感謝します!'; + + @override + String get poweredBy => 'bixat.dev チームによって提供'; + + @override + String get language => '言語'; + + @override + String get theme => 'テーマ'; + + @override + String get light => 'ライト'; + + @override + String get dark => 'ダーク'; + + @override + String get system => 'システム'; +} diff --git a/lib/l10n/app_localizations_pt.dart b/lib/l10n/app_localizations_pt.dart new file mode 100644 index 0000000..e60ec96 --- /dev/null +++ b/lib/l10n/app_localizations_pt.dart @@ -0,0 +1,110 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Portuguese (`pt`). +class AppLocalizationsPt extends AppLocalizations { + AppLocalizationsPt([String locale = 'pt']) : super(locale); + + @override + String get appTitle => 'Cuidado dos Olhos'; + + @override + String get forceMode => 'Modo Forçado'; + + @override + String get forceModeSubtitle => 'Evitar minimizar a janela durante as pausas'; + + @override + String get startupAtLogin => 'Iniciar ao Fazer Login'; + + @override + String get startupAtLoginSubtitle => + 'Iniciar aplicativo automaticamente ao iniciar o sistema'; + + @override + String get work => 'trabalho'; + + @override + String get breakPeriod => 'pausa'; + + @override + String get breakTime => 'Tempo de Pausa'; + + @override + String get ruleTitle => 'Regra 20-20-20'; + + @override + String get ruleSubtitle => 'Descanse seus olhos seguindo a regra 20-20-20'; + + @override + String get ruleEvery20Minutes => 'A cada 20 minutos,'; + + @override + String get ruleLookAway => 'desvie o olhar da tela e foque em algo'; + + @override + String get rule20FeetAway => 'a 20 pés de distância'; + + @override + String get ruleFor => 'por'; + + @override + String get rule20Seconds => '20 segundos.'; + + @override + String get ruleExplanation => + 'Isso ajuda a reduzir a fadiga ocular causada pelo uso prolongado da tela.'; + + @override + String get settings => 'Configurações'; + + @override + String get editRule => 'Editar Regra'; + + @override + String get save => 'Salvar'; + + @override + String get cancel => 'Cancelar'; + + @override + String get minutes => 'Minutos'; + + @override + String get seconds => 'Segundos'; + + @override + String get stayFocused => 'Mantenha-se Focado 💪'; + + @override + String get takeAMoment => 'Dê uma Pausa 🌟'; + + @override + String get breakNotification => + 'Mantenha seu olhar na tela. Lembre-se, a cada 20 minutos, faça uma pausa de 20 segundos olhando para algo a 20 pés de distância.'; + + @override + String get workNotification => + 'Afaste-se da tela e foque em algo a 20 pés de distância por 20 segundos. Seus olhos agradecerão!'; + + @override + String get poweredBy => 'Desenvolvido pela equipe bixat.dev'; + + @override + String get language => 'Idioma'; + + @override + String get theme => 'Tema'; + + @override + String get light => 'Claro'; + + @override + String get dark => 'Escuro'; + + @override + String get system => 'Sistema'; +} diff --git a/lib/l10n/app_localizations_ru.dart b/lib/l10n/app_localizations_ru.dart new file mode 100644 index 0000000..cd01f25 --- /dev/null +++ b/lib/l10n/app_localizations_ru.dart @@ -0,0 +1,112 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Russian (`ru`). +class AppLocalizationsRu extends AppLocalizations { + AppLocalizationsRu([String locale = 'ru']) : super(locale); + + @override + String get appTitle => 'Забота о Глазах'; + + @override + String get forceMode => 'Принудительный Режим'; + + @override + String get forceModeSubtitle => + 'Предотвратить сворачивание окна во время перерывов'; + + @override + String get startupAtLogin => 'Запуск при Входе'; + + @override + String get startupAtLoginSubtitle => + 'Автоматически запускать приложение при запуске системы'; + + @override + String get work => 'работа'; + + @override + String get breakPeriod => 'перерыв'; + + @override + String get breakTime => 'Время Перерыва'; + + @override + String get ruleTitle => 'Правило 20-20-20'; + + @override + String get ruleSubtitle => 'Дайте отдых глазам, следуя правилу 20-20-20'; + + @override + String get ruleEvery20Minutes => 'Каждые 20 минут,'; + + @override + String get ruleLookAway => + 'отводите взгляд от экрана и фокусируйтесь на чем-то'; + + @override + String get rule20FeetAway => 'в 20 футах'; + + @override + String get ruleFor => 'на'; + + @override + String get rule20Seconds => '20 секунд.'; + + @override + String get ruleExplanation => + 'Это помогает уменьшить зрительное напряжение, вызванное длительным использованием экрана.'; + + @override + String get settings => 'Настройки'; + + @override + String get editRule => 'Редактировать Правило'; + + @override + String get save => 'Сохранить'; + + @override + String get cancel => 'Отмена'; + + @override + String get minutes => 'Минуты'; + + @override + String get seconds => 'Секунды'; + + @override + String get stayFocused => 'Оставайтесь Сосредоточенными 💪'; + + @override + String get takeAMoment => 'Сделайте Паузу 🌟'; + + @override + String get breakNotification => + 'Держите взгляд на экране. Помните, каждые 20 минут делайте 20-секундный перерыв, глядя на что-то в 20 футах.'; + + @override + String get workNotification => + 'Отойдите от экрана и сфокусируйтесь на чем-то в 20 футах на 20 секунд. Ваши глаза скажут вам спасибо!'; + + @override + String get poweredBy => 'Разработано командой bixat.dev'; + + @override + String get language => 'Язык'; + + @override + String get theme => 'Тема'; + + @override + String get light => 'Светлая'; + + @override + String get dark => 'Темная'; + + @override + String get system => 'Системная'; +} diff --git a/lib/l10n/app_localizations_tr.dart b/lib/l10n/app_localizations_tr.dart new file mode 100644 index 0000000..eeacf60 --- /dev/null +++ b/lib/l10n/app_localizations_tr.dart @@ -0,0 +1,113 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Turkish (`tr`). +class AppLocalizationsTr extends AppLocalizations { + AppLocalizationsTr([String locale = 'tr']) : super(locale); + + @override + String get appTitle => 'Göz Bakımı'; + + @override + String get forceMode => 'Zorlu Mod'; + + @override + String get forceModeSubtitle => + 'Molalar sırasında pencere simge durumuna küçültmeyi önle'; + + @override + String get startupAtLogin => 'Oturum Açma'; + + @override + String get startupAtLoginSubtitle => + 'Sistem başlatıldığında uygulamayı otomatik olarak başlat'; + + @override + String get work => 'çalışma'; + + @override + String get breakPeriod => 'mola'; + + @override + String get breakTime => 'Mola Zamanı'; + + @override + String get ruleTitle => '20-20-20 Kuralı'; + + @override + String get ruleSubtitle => + '20-20-20 kuralını uygulayarak gözlerinizi dinlendirin'; + + @override + String get ruleEvery20Minutes => 'Her 20 dakikada bir,'; + + @override + String get ruleLookAway => + 'ekranınızdan gözünüzü ayırın ve bir şeye odaklanın'; + + @override + String get rule20FeetAway => '20 feet uzakta'; + + @override + String get ruleFor => 'süreyle'; + + @override + String get rule20Seconds => '20 saniye.'; + + @override + String get ruleExplanation => + 'Bu, uzun süreli ekran kullanımının neden olduğu göz yorgunluğunu azaltmaya yardımcı olur.'; + + @override + String get settings => 'Ayarlar'; + + @override + String get editRule => 'Kuralı Düzenle'; + + @override + String get save => 'Kaydet'; + + @override + String get cancel => 'İptal'; + + @override + String get minutes => 'Dakika'; + + @override + String get seconds => 'Saniye'; + + @override + String get stayFocused => 'Odaklanmaya Devam Et 💪'; + + @override + String get takeAMoment => 'Bir Mola Ver 🌟'; + + @override + String get breakNotification => + 'Bakışınızı ekranda tutun. Unutmayın, her 20 dakikada bir, 20 feet uzaktaki bir şeye bakarak 20 saniyelik mola verin.'; + + @override + String get workNotification => + 'Ekrandan uzaklaşın ve 20 saniye boyunca 20 feet uzaktaki bir şeye odaklanın. Gözleriniz teşekkür edecek!'; + + @override + String get poweredBy => 'bixat.dev ekibi tarafından sunulmuştur'; + + @override + String get language => 'Dil'; + + @override + String get theme => 'Tema'; + + @override + String get light => 'Açık'; + + @override + String get dark => 'Koyu'; + + @override + String get system => 'Sistem'; +} diff --git a/lib/l10n/app_localizations_zh.dart b/lib/l10n/app_localizations_zh.dart new file mode 100644 index 0000000..340f9a5 --- /dev/null +++ b/lib/l10n/app_localizations_zh.dart @@ -0,0 +1,106 @@ +// ignore: unused_import +import 'package:intl/intl.dart' as intl; +import 'app_localizations.dart'; + +// ignore_for_file: type=lint + +/// The translations for Chinese (`zh`). +class AppLocalizationsZh extends AppLocalizations { + AppLocalizationsZh([String locale = 'zh']) : super(locale); + + @override + String get appTitle => '眼睛护理'; + + @override + String get forceMode => '强制模式'; + + @override + String get forceModeSubtitle => '防止休息期间窗口最小化'; + + @override + String get startupAtLogin => '登录时启动'; + + @override + String get startupAtLoginSubtitle => '系统启动时自动启动应用程序'; + + @override + String get work => '工作'; + + @override + String get breakPeriod => '休息'; + + @override + String get breakTime => '休息时间'; + + @override + String get ruleTitle => '20-20-20 法则'; + + @override + String get ruleSubtitle => '遵循 20-20-20 法则让您的眼睛休息'; + + @override + String get ruleEvery20Minutes => '每 20 分钟,'; + + @override + String get ruleLookAway => '将视线从屏幕上移开,专注于某物'; + + @override + String get rule20FeetAway => '20 英尺外'; + + @override + String get ruleFor => '持续'; + + @override + String get rule20Seconds => '20 秒。'; + + @override + String get ruleExplanation => '这有助于减少长时间使用屏幕造成的眼睛疲劳。'; + + @override + String get settings => '设置'; + + @override + String get editRule => '编辑规则'; + + @override + String get save => '保存'; + + @override + String get cancel => '取消'; + + @override + String get minutes => '分钟'; + + @override + String get seconds => '秒'; + + @override + String get stayFocused => '保持专注 💪'; + + @override + String get takeAMoment => '休息一下 🌟'; + + @override + String get breakNotification => '保持视线在屏幕上。记住,每 20 分钟,花 20 秒时间看看 20 英尺外的东西。'; + + @override + String get workNotification => '远离屏幕,专注于 20 英尺外的东西 20 秒。你的眼睛会感谢你的!'; + + @override + String get poweredBy => '由 bixat.dev 团队提供支持'; + + @override + String get language => '语言'; + + @override + String get theme => '主题'; + + @override + String get light => '浅色'; + + @override + String get dark => '深色'; + + @override + String get system => '系统'; +} diff --git a/lib/l10n/app_pt.arb b/lib/l10n/app_pt.arb new file mode 100644 index 0000000..8073a40 --- /dev/null +++ b/lib/l10n/app_pt.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "pt", + "appTitle": "Cuidado dos Olhos", + "forceMode": "Modo Forçado", + "forceModeSubtitle": "Evitar minimizar a janela durante as pausas", + "startupAtLogin": "Iniciar ao Fazer Login", + "startupAtLoginSubtitle": "Iniciar aplicativo automaticamente ao iniciar o sistema", + "work": "trabalho", + "breakPeriod": "pausa", + "breakTime": "Tempo de Pausa", + "ruleTitle": "Regra 20-20-20", + "ruleSubtitle": "Descanse seus olhos seguindo a regra 20-20-20", + "ruleEvery20Minutes": "A cada 20 minutos,", + "ruleLookAway": "desvie o olhar da tela e foque em algo", + "rule20FeetAway": "a 20 pés de distância", + "ruleFor": "por", + "rule20Seconds": "20 segundos.", + "ruleExplanation": "Isso ajuda a reduzir a fadiga ocular causada pelo uso prolongado da tela.", + "settings": "Configurações", + "editRule": "Editar Regra", + "save": "Salvar", + "cancel": "Cancelar", + "minutes": "Minutos", + "seconds": "Segundos", + "stayFocused": "Mantenha-se Focado 💪", + "takeAMoment": "Dê uma Pausa 🌟", + "breakNotification": "Mantenha seu olhar na tela. Lembre-se, a cada 20 minutos, faça uma pausa de 20 segundos olhando para algo a 20 pés de distância.", + "workNotification": "Afaste-se da tela e foque em algo a 20 pés de distância por 20 segundos. Seus olhos agradecerão!", + "poweredBy": "Desenvolvido pela equipe bixat.dev", + "language": "Idioma", + "theme": "Tema", + "light": "Claro", + "dark": "Escuro", + "system": "Sistema" +} diff --git a/lib/l10n/app_ru.arb b/lib/l10n/app_ru.arb new file mode 100644 index 0000000..cebffe1 --- /dev/null +++ b/lib/l10n/app_ru.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "ru", + "appTitle": "Забота о Глазах", + "forceMode": "Принудительный Режим", + "forceModeSubtitle": "Предотвратить сворачивание окна во время перерывов", + "startupAtLogin": "Запуск при Входе", + "startupAtLoginSubtitle": "Автоматически запускать приложение при запуске системы", + "work": "работа", + "breakPeriod": "перерыв", + "breakTime": "Время Перерыва", + "ruleTitle": "Правило 20-20-20", + "ruleSubtitle": "Дайте отдых глазам, следуя правилу 20-20-20", + "ruleEvery20Minutes": "Каждые 20 минут,", + "ruleLookAway": "отводите взгляд от экрана и фокусируйтесь на чем-то", + "rule20FeetAway": "в 20 футах", + "ruleFor": "на", + "rule20Seconds": "20 секунд.", + "ruleExplanation": "Это помогает уменьшить зрительное напряжение, вызванное длительным использованием экрана.", + "settings": "Настройки", + "editRule": "Редактировать Правило", + "save": "Сохранить", + "cancel": "Отмена", + "minutes": "Минуты", + "seconds": "Секунды", + "stayFocused": "Оставайтесь Сосредоточенными 💪", + "takeAMoment": "Сделайте Паузу 🌟", + "breakNotification": "Держите взгляд на экране. Помните, каждые 20 минут делайте 20-секундный перерыв, глядя на что-то в 20 футах.", + "workNotification": "Отойдите от экрана и сфокусируйтесь на чем-то в 20 футах на 20 секунд. Ваши глаза скажут вам спасибо!", + "poweredBy": "Разработано командой bixat.dev", + "language": "Язык", + "theme": "Тема", + "light": "Светлая", + "dark": "Темная", + "system": "Системная" +} diff --git a/lib/l10n/app_tr.arb b/lib/l10n/app_tr.arb new file mode 100644 index 0000000..2ba3574 --- /dev/null +++ b/lib/l10n/app_tr.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "tr", + "appTitle": "Göz Bakımı", + "forceMode": "Zorlu Mod", + "forceModeSubtitle": "Molalar sırasında pencere simge durumuna küçültmeyi önle", + "startupAtLogin": "Oturum Açma", + "startupAtLoginSubtitle": "Sistem başlatıldığında uygulamayı otomatik olarak başlat", + "work": "çalışma", + "breakPeriod": "mola", + "breakTime": "Mola Zamanı", + "ruleTitle": "20-20-20 Kuralı", + "ruleSubtitle": "20-20-20 kuralını uygulayarak gözlerinizi dinlendirin", + "ruleEvery20Minutes": "Her 20 dakikada bir,", + "ruleLookAway": "ekranınızdan gözünüzü ayırın ve bir şeye odaklanın", + "rule20FeetAway": "20 feet uzakta", + "ruleFor": "süreyle", + "rule20Seconds": "20 saniye.", + "ruleExplanation": "Bu, uzun süreli ekran kullanımının neden olduğu göz yorgunluğunu azaltmaya yardımcı olur.", + "settings": "Ayarlar", + "editRule": "Kuralı Düzenle", + "save": "Kaydet", + "cancel": "İptal", + "minutes": "Dakika", + "seconds": "Saniye", + "stayFocused": "Odaklanmaya Devam Et 💪", + "takeAMoment": "Bir Mola Ver 🌟", + "breakNotification": "Bakışınızı ekranda tutun. Unutmayın, her 20 dakikada bir, 20 feet uzaktaki bir şeye bakarak 20 saniyelik mola verin.", + "workNotification": "Ekrandan uzaklaşın ve 20 saniye boyunca 20 feet uzaktaki bir şeye odaklanın. Gözleriniz teşekkür edecek!", + "poweredBy": "bixat.dev ekibi tarafından sunulmuştur", + "language": "Dil", + "theme": "Tema", + "light": "Açık", + "dark": "Koyu", + "system": "Sistem" +} diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb new file mode 100644 index 0000000..ae655b7 --- /dev/null +++ b/lib/l10n/app_zh.arb @@ -0,0 +1,35 @@ +{ + "@@locale": "zh", + "appTitle": "眼睛护理", + "forceMode": "强制模式", + "forceModeSubtitle": "防止休息期间窗口最小化", + "startupAtLogin": "登录时启动", + "startupAtLoginSubtitle": "系统启动时自动启动应用程序", + "work": "工作", + "breakPeriod": "休息", + "breakTime": "休息时间", + "ruleTitle": "20-20-20 法则", + "ruleSubtitle": "遵循 20-20-20 法则让您的眼睛休息", + "ruleEvery20Minutes": "每 20 分钟,", + "ruleLookAway": "将视线从屏幕上移开,专注于某物", + "rule20FeetAway": "20 英尺外", + "ruleFor": "持续", + "rule20Seconds": "20 秒。", + "ruleExplanation": "这有助于减少长时间使用屏幕造成的眼睛疲劳。", + "settings": "设置", + "editRule": "编辑规则", + "save": "保存", + "cancel": "取消", + "minutes": "分钟", + "seconds": "秒", + "stayFocused": "保持专注 💪", + "takeAMoment": "休息一下 🌟", + "breakNotification": "保持视线在屏幕上。记住,每 20 分钟,花 20 秒时间看看 20 英尺外的东西。", + "workNotification": "远离屏幕,专注于 20 英尺外的东西 20 秒。你的眼睛会感谢你的!", + "poweredBy": "由 bixat.dev 团队提供支持", + "language": "语言", + "theme": "主题", + "light": "浅色", + "dark": "深色", + "system": "系统" +} diff --git a/lib/main.dart b/lib/main.dart index f79dc01..7022c8f 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,8 +1,10 @@ import 'dart:io'; import 'package:eyes_care/countdown_screen.dart'; +import 'package:eyes_care/l10n/app_localizations.dart'; import 'package:eyes_care/shared_pref.dart'; import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; import 'package:launch_at_startup/launch_at_startup.dart'; import 'package:package_info_plus/package_info_plus.dart'; import 'package:window_manager/window_manager.dart'; @@ -29,14 +31,24 @@ Future loadThemeMode() async { } } +Future loadLanguage() async { + final lang = await PreferenceService.getLanguage(); + if (lang != null) { + localeNotifier.value = Locale(lang); + } +} + Future main() async { WidgetsFlutterBinding.ensureInitialized(); await windowManager.ensureInitialized(); await loadThemeMode(); + await loadLanguage(); + await initLaunchStartup(); runApp(const CareYourEyes()); } final themeNotifier = ValueNotifier(ThemeMode.light); +final localeNotifier = ValueNotifier(const Locale('en')); class CareYourEyes extends StatelessWidget { const CareYourEyes({super.key}); @@ -44,55 +56,100 @@ class CareYourEyes extends StatelessWidget { Widget build(BuildContext context) { return ValueListenableBuilder( valueListenable: themeNotifier, - builder: (context, _, __) { - const primaryColor = Color(0xFF5BE0E5); - const secondaryColor = Color(0xFF32CD32); + builder: (context, themeValue, _) { + return ValueListenableBuilder( + valueListenable: localeNotifier, + builder: (context, localeValue, _) { + const primaryColor = Color(0xFF5BE0E5); + const secondaryColor = Color(0xFF32CD32); - return MaterialApp( - title: 'Eyes Care', - debugShowCheckedModeBanner: false, - themeMode: themeNotifier.value, - theme: ThemeData( - useMaterial3: true, - colorScheme: ColorScheme.fromSeed( - seedColor: primaryColor, - secondary: secondaryColor, - ), - textTheme: GoogleFonts.poppinsTextTheme(), - cardTheme: CardThemeData( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + return MaterialApp( + title: 'Eyes Care', + debugShowCheckedModeBanner: false, + locale: localeValue, + localizationsDelegates: const [ + AppLocalizations.delegate, + GlobalMaterialLocalizations.delegate, + GlobalWidgetsLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + ], + supportedLocales: const [ + Locale('en'), + Locale('ar'), + Locale('es'), + Locale('fr'), + Locale('de'), + Locale('zh'), + Locale('ja'), + Locale('ru'), + Locale('pt'), + Locale('hi'), + Locale('tr'), + ], + localeResolutionCallback: (locale, supportedLocales) { + for (var supportedLocale in supportedLocales) { + if (supportedLocale.languageCode == locale?.languageCode) { + return supportedLocale; + } + } + return supportedLocales.first; + }, + themeMode: themeValue, + theme: ThemeData( + useMaterial3: true, + colorScheme: ColorScheme.fromSeed( + seedColor: primaryColor, + secondary: secondaryColor, + ), + textTheme: _getTextTheme(localeValue), + cardTheme: CardThemeData( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: true, + backgroundColor: Colors.transparent, + ), ), - ), - appBarTheme: const AppBarTheme( - elevation: 0, - centerTitle: true, - backgroundColor: Colors.transparent, - ), - ), - darkTheme: ThemeData.dark().copyWith( - colorScheme: ColorScheme.fromSeed( - seedColor: primaryColor, - secondary: secondaryColor, - brightness: Brightness.dark, - ), - textTheme: GoogleFonts.poppinsTextTheme(ThemeData.dark().textTheme), - cardTheme: CardThemeData( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), + darkTheme: ThemeData.dark().copyWith( + colorScheme: ColorScheme.fromSeed( + seedColor: primaryColor, + secondary: secondaryColor, + brightness: Brightness.dark, + ), + textTheme: _getTextTheme(localeValue, isDark: true), + cardTheme: CardThemeData( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + ), + ), + appBarTheme: const AppBarTheme( + elevation: 0, + centerTitle: true, + backgroundColor: Colors.transparent, + ), ), - ), - appBarTheme: const AppBarTheme( - elevation: 0, - centerTitle: true, - backgroundColor: Colors.transparent, - ), - ), - home: const CountdownScreen(), + home: const CountdownScreen(), + ); + }, ); }, ); } + + TextTheme _getTextTheme(Locale locale, {bool isDark = false}) { + // Use Tajawal font for Arabic, Poppins for other languages + if (locale.languageCode == 'ar') { + return GoogleFonts.tajawalTextTheme( + isDark ? ThemeData.dark().textTheme : ThemeData.light().textTheme, + ); + } + return GoogleFonts.poppinsTextTheme( + isDark ? ThemeData.dark().textTheme : ThemeData.light().textTheme, + ); + } } diff --git a/lib/shared_pref.dart b/lib/shared_pref.dart index 56ef54d..ebf61e1 100644 --- a/lib/shared_pref.dart +++ b/lib/shared_pref.dart @@ -6,6 +6,8 @@ class PreferenceService { static const minutes = "minutes"; static const seconds = "seconds"; static const themeModeKey = "theme_mode"; + static const languageKey = "language"; + static Future setThemeMode(String mode) async { final prefs = await instance; await prefs.setString(themeModeKey, mode); @@ -16,6 +18,16 @@ class PreferenceService { return prefs.getString(themeModeKey); } + static Future setLanguage(String lang) async { + final prefs = await instance; + await prefs.setString(languageKey, lang); + } + + static Future getLanguage() async { + final prefs = await instance; + return prefs.getString(languageKey); + } + static Future get instance async => await SharedPreferences.getInstance(); diff --git a/lib/widgets/custom_slider.dart b/lib/widgets/custom_slider.dart index 58abc89..4d1900c 100644 --- a/lib/widgets/custom_slider.dart +++ b/lib/widgets/custom_slider.dart @@ -4,12 +4,16 @@ class CustomSlider extends StatelessWidget { final int value; final String title; final ValueChanged onChanged; + final double min; + final double max; const CustomSlider({ Key? key, required this.value, required this.onChanged, required this.title, + this.min = 1, + this.max = 60, }) : super(key: key); @override @@ -36,7 +40,7 @@ class CustomSlider extends StatelessWidget { borderRadius: BorderRadius.circular(20), ), child: Text( - '$value ${title.contains('Break') ? 'seconds' : 'minutes'}', + '$value', style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onPrimaryContainer, fontWeight: FontWeight.w600, @@ -64,8 +68,8 @@ class CustomSlider extends StatelessWidget { ), child: Slider( value: value.toDouble(), - min: 1, - max: 60, + min: min, + max: max, divisions: 50, onChanged: onChanged, ), diff --git a/lib/widgets/duration_picker_dialog.dart b/lib/widgets/duration_picker_dialog.dart index d6046b9..029baa7 100644 --- a/lib/widgets/duration_picker_dialog.dart +++ b/lib/widgets/duration_picker_dialog.dart @@ -1,6 +1,7 @@ import 'package:eyes_care/shared_pref.dart'; import 'package:eyes_care/widgets/custom_slider.dart'; import 'package:flutter/material.dart'; +import 'package:eyes_care/l10n/app_localizations.dart'; class DurationPickerDialog extends StatefulWidget { final Function(int minutes, int seconds) onConfirm; @@ -32,6 +33,7 @@ class DurationPickerDialogState extends State { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final loc = AppLocalizations.of(context)!; return Dialog( shape: RoundedRectangleBorder( @@ -59,7 +61,7 @@ class DurationPickerDialogState extends State { ), const SizedBox(width: 12), Text( - 'Set Break Duration', + loc.editRule, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onSurface, @@ -69,8 +71,10 @@ class DurationPickerDialogState extends State { ), const SizedBox(height: 24), CustomSlider( - title: "Work Duration", + title: "${loc.minutes} ($_minutes)", value: _minutes, + min: 1, + max: 60, onChanged: (double value) { setState(() { _minutes = value.round(); @@ -80,8 +84,10 @@ class DurationPickerDialogState extends State { ), const SizedBox(height: 16), CustomSlider( - title: "Break Duration", + title: "${loc.seconds} ($_seconds)", value: _seconds, + min: 1, + max: 60, onChanged: (double value) { setState(() { _seconds = value.round(); @@ -95,7 +101,7 @@ class DurationPickerDialogState extends State { TextButton( onPressed: () => Navigator.pop(context), child: Text( - 'Cancel', + loc.cancel, style: TextStyle( color: theme.colorScheme.onSurfaceVariant, ), @@ -107,7 +113,7 @@ class DurationPickerDialogState extends State { widget.onConfirm(_minutes, _seconds); Navigator.pop(context); }, - child: const Text('Save'), + child: Text(loc.save), ), ], ), diff --git a/lib/widgets/rule_text.dart b/lib/widgets/rule_text.dart index 9915afc..d862823 100644 --- a/lib/widgets/rule_text.dart +++ b/lib/widgets/rule_text.dart @@ -1,3 +1,4 @@ +import 'package:eyes_care/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; class RuleText extends StatelessWidget { @@ -6,6 +7,7 @@ class RuleText extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final loc = AppLocalizations.of(context)!; return Card( elevation: 0, @@ -32,7 +34,7 @@ class RuleText extends StatelessWidget { ), const SizedBox(width: 12), Text( - "20-20-20 Rule", + loc.ruleTitle, style: theme.textTheme.titleLarge?.copyWith( fontWeight: FontWeight.bold, color: theme.colorScheme.onPrimaryContainer, @@ -42,7 +44,7 @@ class RuleText extends StatelessWidget { ), const SizedBox(height: 16), Text( - "Give your eyes a rest by following the 20-20-20 rule", + loc.ruleSubtitle, style: theme.textTheme.bodyLarge?.copyWith( color: theme.colorScheme.onPrimaryContainer, fontWeight: FontWeight.w600, @@ -56,26 +58,25 @@ class RuleText extends StatelessWidget { .withAlpha((0.8 * 255).round()), height: 1.5, ), - children: const [ + children: [ TextSpan( - text: "Every 20 minutes, ", - style: TextStyle(fontWeight: FontWeight.bold), + text: "${loc.ruleEvery20Minutes} ", + style: const TextStyle(fontWeight: FontWeight.bold), ), TextSpan( - text: "look away from your screen and focus on something ", + text: "${loc.ruleLookAway} ", ), TextSpan( - text: "20 feet away ", - style: TextStyle(fontWeight: FontWeight.bold), + text: "${loc.rule20FeetAway} ", + style: const TextStyle(fontWeight: FontWeight.bold), ), - TextSpan(text: "for "), + TextSpan(text: "${loc.ruleFor} "), TextSpan( - text: "20 seconds. ", - style: TextStyle(fontWeight: FontWeight.bold), + text: loc.rule20Seconds, + style: const TextStyle(fontWeight: FontWeight.bold), ), TextSpan( - text: - "This helps reduce eye strain caused by prolonged screen use.", + text: loc.ruleExplanation, ), ], ), diff --git a/lib/widgets/rule_timer.dart b/lib/widgets/rule_timer.dart index 3f1a795..2138f39 100644 --- a/lib/widgets/rule_timer.dart +++ b/lib/widgets/rule_timer.dart @@ -1,3 +1,4 @@ +import 'package:eyes_care/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; import 'package:rocket_timer/rocket_timer.dart'; @@ -16,6 +17,7 @@ class RuleTimer extends StatelessWidget { @override Widget build(BuildContext context) { final theme = Theme.of(context); + final loc = AppLocalizations.of(context); return SizedBox( height: circleSize + 40, @@ -63,7 +65,7 @@ class RuleTimer extends StatelessWidget { ), if (inBreak) Text( - 'Break Time', + loc.breakTime, style: theme.textTheme.titleMedium?.copyWith( color: theme.colorScheme.secondary, ), diff --git a/lib/widgets/settings.dart b/lib/widgets/settings.dart index 0838637..5826ddd 100644 --- a/lib/widgets/settings.dart +++ b/lib/widgets/settings.dart @@ -1,3 +1,5 @@ +import 'package:eyes_care/l10n/app_localizations.dart'; +import 'package:eyes_care/main.dart'; import 'package:eyes_care/shared_pref.dart'; import 'package:eyes_care/widgets/edit_rule_button.dart'; import 'package:eyes_care/widgets/force_mode_check_box.dart'; @@ -23,12 +25,31 @@ class Settings extends StatelessWidget { @override Widget build(BuildContext context) { + final loc = AppLocalizations.of(context); + final theme = Theme.of(context); + + final languages = [ + {'code': 'en', 'name': 'English', 'flag': '🇬🇧'}, + {'code': 'ar', 'name': 'العربية', 'flag': '🇸🇦'}, + {'code': 'es', 'name': 'Español', 'flag': '🇪🇸'}, + {'code': 'fr', 'name': 'Français', 'flag': '🇫🇷'}, + {'code': 'de', 'name': 'Deutsch', 'flag': '🇩🇪'}, + {'code': 'zh', 'name': '中文', 'flag': '🇨🇳'}, + {'code': 'ja', 'name': '日本語', 'flag': '🇯🇵'}, + {'code': 'ru', 'name': 'Русский', 'flag': '🇷🇺'}, + {'code': 'pt', 'name': 'Português', 'flag': '🇵🇹'}, + {'code': 'hi', 'name': 'हिन्दी', 'flag': '🇮🇳'}, + {'code': 'tr', 'name': 'Türkçe', 'flag': '🇹🇷'}, + ]; + return Padding( padding: const EdgeInsets.symmetric(horizontal: 16.0, vertical: 16.0), child: Column( mainAxisSize: MainAxisSize.min, spacing: 16.0, children: [ + // Language Selector + _buildLanguageCard(context, loc, theme, languages), EditRuleButton( reminder: reminder, breakTime: breakTime, @@ -37,15 +58,15 @@ class Settings extends StatelessWidget { SwitcherSetting( enabled: forceModeEnabled, icon: Icons.lock_rounded, - title: "Force Mode", - subtitle: "Prevent window minimization during breaks", + title: loc.forceMode, + subtitle: loc.forceModeSubtitle, onChanged: _onUpdateForceMode, ), SwitcherSetting( enabled: startUpModeEnabled, icon: Icons.start, - title: "Startup at Login", - subtitle: "Launch application automatically at system startup", + title: loc.startupAtLogin, + subtitle: loc.startupAtLoginSubtitle, onChanged: _onUpdateStartupMode, ), ], @@ -53,6 +74,226 @@ class Settings extends StatelessWidget { ); } + Widget _buildLanguageCard( + BuildContext context, + AppLocalizations loc, + ThemeData theme, + List> languages, + ) { + final currentLang = languages.firstWhere( + (lang) => lang['code'] == localeNotifier.value.languageCode, + orElse: () => languages.first, + ); + + return Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: theme.colorScheme.outlineVariant, width: 1), + ), + child: InkWell( + onTap: () => _showLanguageDialog(context, theme, languages), + borderRadius: BorderRadius.circular(16), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( + children: [ + Container( + padding: const EdgeInsets.all(12), + decoration: BoxDecoration( + gradient: LinearGradient( + colors: [ + theme.colorScheme.primaryContainer, + theme.colorScheme.primaryContainer.withAlpha(180), + ], + ), + borderRadius: BorderRadius.circular(12), + ), + child: Icon( + Icons.language_rounded, + color: theme.colorScheme.primary, + size: 28, + ), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + loc.language, + style: theme.textTheme.bodySmall?.copyWith( + color: theme.colorScheme.onSurfaceVariant, + fontWeight: FontWeight.w500, + ), + ), + const SizedBox(height: 4), + Row( + children: [ + Text( + currentLang['flag']!, + style: const TextStyle(fontSize: 20), + ), + const SizedBox(width: 8), + Text( + currentLang['name']!, + style: theme.textTheme.titleMedium?.copyWith( + color: theme.colorScheme.onSurface, + fontWeight: FontWeight.w600, + ), + ), + ], + ), + ], + ), + ), + Container( + padding: const EdgeInsets.all(8), + decoration: BoxDecoration( + color: theme.colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(8), + ), + child: Icon( + Icons.arrow_forward_ios_rounded, + color: theme.colorScheme.onSurfaceVariant, + size: 16, + ), + ), + ], + ), + ), + ), + ); + } + + void _showLanguageDialog( + BuildContext context, + ThemeData theme, + List> languages, + ) { + showModalBottomSheet( + context: context, + backgroundColor: Colors.transparent, + builder: (BuildContext context) { + return Container( + decoration: BoxDecoration( + color: theme.colorScheme.surface, + borderRadius: const BorderRadius.vertical(top: Radius.circular(24)), + ), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + // Handle bar + Container( + margin: const EdgeInsets.only(top: 12), + width: 40, + height: 4, + decoration: BoxDecoration( + color: theme.colorScheme.outlineVariant, + borderRadius: BorderRadius.circular(2), + ), + ), + Padding( + padding: const EdgeInsets.all(20), + child: Text( + AppLocalizations.of(context).language, + style: theme.textTheme.headlineSmall?.copyWith( + fontWeight: FontWeight.bold, + color: theme.colorScheme.onSurface, + ), + ), + ), + const Divider(height: 1), + Flexible( + child: GridView.builder( + shrinkWrap: true, + padding: const EdgeInsets.all(16), + gridDelegate: const SliverGridDelegateWithFixedCrossAxisCount( + crossAxisCount: 2, + crossAxisSpacing: 12, + mainAxisSpacing: 12, + childAspectRatio: 4, + ), + itemCount: languages.length, + itemBuilder: (context, index) { + final lang = languages[index]; + final isSelected = + lang['code'] == localeNotifier.value.languageCode; + + return _buildLanguageItem(context, theme, lang, isSelected); + }, + ), + ), + const SizedBox(height: 16), + ], + ), + ); + }, + ); + } + + Widget _buildLanguageItem( + BuildContext context, + ThemeData theme, + Map lang, + bool isSelected, + ) { + return InkWell( + onTap: () { + localeNotifier.value = Locale(lang['code']!); + PreferenceService.setLanguage(lang['code']!); + Navigator.pop(context); + }, + borderRadius: BorderRadius.circular(12), + child: Container( + decoration: BoxDecoration( + color: + isSelected + ? theme.colorScheme.primaryContainer + : theme.colorScheme.surfaceContainerHighest, + borderRadius: BorderRadius.circular(12), + border: Border.all( + color: + isSelected + ? theme.colorScheme.primary + : theme.colorScheme.outlineVariant, + width: isSelected ? 2 : 1, + ), + ), + padding: const EdgeInsets.all(8), + child: Row( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + Text(lang['flag']!, style: const TextStyle(fontSize: 24)), + const SizedBox(width: 8), + Expanded( + child: Text( + lang['name']!, + style: theme.textTheme.bodyMedium?.copyWith( + color: + isSelected + ? theme.colorScheme.onPrimaryContainer + : theme.colorScheme.onSurface, + fontWeight: isSelected ? FontWeight.w600 : FontWeight.normal, + ), + maxLines: 1, + overflow: TextOverflow.ellipsis, + ), + ), + if (isSelected) ...[ + const SizedBox(width: 4), + Icon( + Icons.check_circle_rounded, + color: theme.colorScheme.primary, + size: 18, + ), + ], + ], + ), + ), + ); + } + void _onUpdateForceMode(bool? value) { if (value == null) return; PreferenceService.setBool(PreferenceService.forceModeKey, value); diff --git a/lib/widgets/work_break_info.dart b/lib/widgets/work_break_info.dart index ea80c1c..a7421c9 100644 --- a/lib/widgets/work_break_info.dart +++ b/lib/widgets/work_break_info.dart @@ -1,14 +1,19 @@ +import 'package:eyes_care/l10n/app_localizations.dart'; import 'package:flutter/material.dart'; class WorkBreakInfo extends StatelessWidget { - const WorkBreakInfo( - {super.key, required this.reminder, required this.breakTime}); + const WorkBreakInfo({ + super.key, + required this.reminder, + required this.breakTime, + }); final int reminder, breakTime; @override Widget build(BuildContext context) { final theme = Theme.of(context); + final loc = AppLocalizations.of(context)!; return Column( crossAxisAlignment: CrossAxisAlignment.start, @@ -25,7 +30,7 @@ class WorkBreakInfo extends StatelessWidget { ), ), Text( - " work", + " ${loc.work}", style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), @@ -43,7 +48,7 @@ class WorkBreakInfo extends StatelessWidget { ), ), Text( - " break", + " ${loc.breakPeriod}", style: theme.textTheme.bodyMedium?.copyWith( color: theme.colorScheme.onSurfaceVariant, ), diff --git a/macos/Podfile b/macos/Podfile index c795730..b52666a 100644 --- a/macos/Podfile +++ b/macos/Podfile @@ -1,4 +1,4 @@ -platform :osx, '10.14' +platform :osx, '10.15' # CocoaPods analytics sends network stats synchronously affecting flutter build latency. ENV['COCOAPODS_DISABLE_STATS'] = 'true' diff --git a/macos/Runner.xcodeproj/project.pbxproj b/macos/Runner.xcodeproj/project.pbxproj index 7750b29..ef32731 100644 --- a/macos/Runner.xcodeproj/project.pbxproj +++ b/macos/Runner.xcodeproj/project.pbxproj @@ -30,6 +30,7 @@ 5E6D7E09B01AD7C2E9C2ADC6 /* Pods_RunnerTests.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = A6CC01A7C2A5975E2F0F99B7 /* Pods_RunnerTests.framework */; }; BF4B68E82DC9D8490098731A /* LaunchAtLogin in Frameworks */ = {isa = PBXBuildFile; productRef = BF4B68E72DC9D8490098731A /* LaunchAtLogin */; }; F75DCD6FD57FD31A27565424 /* Pods_Runner.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = DF31BBBE5814D2E98AAE3EF3 /* Pods_Runner.framework */; }; + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */ = {isa = PBXBuildFile; productRef = 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -89,6 +90,7 @@ 9740EEB21CF90195004384FC /* Debug.xcconfig */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.xcconfig; path = Debug.xcconfig; sourceTree = ""; }; A6CC01A7C2A5975E2F0F99B7 /* Pods_RunnerTests.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_RunnerTests.framework; sourceTree = BUILT_PRODUCTS_DIR; }; DF31BBBE5814D2E98AAE3EF3 /* Pods_Runner.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = Pods_Runner.framework; sourceTree = BUILT_PRODUCTS_DIR; }; + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */ = {isa = PBXFileReference; lastKnownFileType = wrapper; name = FlutterGeneratedPluginSwiftPackage; path = ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -104,6 +106,7 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 78A318202AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage in Frameworks */, BF4B68E82DC9D8490098731A /* LaunchAtLogin in Frameworks */, F75DCD6FD57FD31A27565424 /* Pods_Runner.framework in Frameworks */, ); @@ -166,6 +169,7 @@ 33CEB47122A05771004F2AC0 /* Flutter */ = { isa = PBXGroup; children = ( + 78E0A7A72DC9AD7400C4905E /* FlutterGeneratedPluginSwiftPackage */, 335BBD1A22A9A15E00E9071D /* GeneratedPluginRegistrant.swift */, 33CEB47222A05771004F2AC0 /* Flutter-Debug.xcconfig */, 33CEB47422A05771004F2AC0 /* Flutter-Release.xcconfig */, @@ -232,6 +236,9 @@ productType = "com.apple.product-type.bundle.unit-test"; }; 33CC10EC2044A3C60003C045 /* Runner */ = { + packageProductDependencies = ( + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */, + ); isa = PBXNativeTarget; buildConfigurationList = 33CC10FB2044A3C60003C045 /* Build configuration list for PBXNativeTarget "Runner" */; buildPhases = ( @@ -294,6 +301,7 @@ ); mainGroup = 33CC10E42044A3C60003C045; packageReferences = ( + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */, BF4B68E62DC9D8490098731A /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */, ); productRefGroup = 33CC10EE2044A3C60003C045 /* Products */; @@ -577,7 +585,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -656,7 +664,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = YES; ONLY_ACTIVE_ARCH = YES; SDKROOT = macosx; @@ -703,7 +711,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - MACOSX_DEPLOYMENT_TARGET = 10.14; + MACOSX_DEPLOYMENT_TARGET = 10.15; MTL_ENABLE_DEBUG_INFO = NO; SDKROOT = macosx; SWIFT_COMPILATION_MODE = wholemodule; @@ -828,7 +836,17 @@ package = BF4B68E62DC9D8490098731A /* XCRemoteSwiftPackageReference "LaunchAtLogin-Legacy" */; productName = LaunchAtLogin; }; + 78A3181F2AECB46A00862997 /* FlutterGeneratedPluginSwiftPackage */ = { + isa = XCSwiftPackageProductDependency; + productName = FlutterGeneratedPluginSwiftPackage; + }; /* End XCSwiftPackageProductDependency section */ +/* Begin XCLocalSwiftPackageReference section */ + 781AD8BC2B33823900A9FFBB /* XCLocalSwiftPackageReference "Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage" */ = { + isa = XCLocalSwiftPackageReference; + relativePath = Flutter/ephemeral/Packages/FlutterGeneratedPluginSwiftPackage; + }; +/* End XCLocalSwiftPackageReference section */ }; rootObject = 33CC10E52044A3C60003C045 /* Project object */; } diff --git a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme index 4189a87..0b3ab2d 100644 --- a/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme +++ b/macos/Runner.xcodeproj/xcshareddata/xcschemes/Runner.xcscheme @@ -5,6 +5,24 @@ + + + + + + + + + + diff --git a/macos/Runner/MainFlutterWindow.swift b/macos/Runner/MainFlutterWindow.swift index 7b8c5b2..b88c2d6 100644 --- a/macos/Runner/MainFlutterWindow.swift +++ b/macos/Runner/MainFlutterWindow.swift @@ -4,36 +4,63 @@ import LaunchAtLogin import window_manager class MainFlutterWindow: NSWindow { - override func awakeFromNib() { - let flutterViewController = FlutterViewController() - let windowFrame = self.frame - self.contentViewController = flutterViewController - self.setFrame(windowFrame, display: true) - - FlutterMethodChannel( - name: "launch_at_startup", binaryMessenger: flutterViewController.engine.binaryMessenger - ) - .setMethodCallHandler { (_ call: FlutterMethodCall, result: @escaping FlutterResult) in - switch call.method { - case "launchAtStartupIsEnabled": - result(LaunchAtLogin.isEnabled) - case "launchAtStartupSetEnabled": - if let arguments = call.arguments as? [String: Any] { - LaunchAtLogin.isEnabled = arguments["setEnabledValue"] as! Bool + override func awakeFromNib() { + let flutterViewController = FlutterViewController() + let windowFrame = self.frame + self.contentViewController = flutterViewController + self.setFrame(windowFrame, display: true) + + FlutterMethodChannel( + name: "launch_at_startup", binaryMessenger: flutterViewController.engine.binaryMessenger + ) + .setMethodCallHandler { (_ call: FlutterMethodCall, result: @escaping FlutterResult) in + switch call.method { + case "launchAtStartupIsEnabled": + result(LaunchAtLogin.isEnabled) + case "launchAtStartupSetEnabled": + if let arguments = call.arguments as? [String: Any] { + LaunchAtLogin.isEnabled = arguments["setEnabledValue"] as! Bool + } + result(nil) + default: + result(FlutterMethodNotImplemented) + } } - result(nil) - default: - result(FlutterMethodNotImplemented) - } + + RegisterGeneratedPlugins(registry: flutterViewController) + + // Add the context menu + self.addContextMenu() + + super.awakeFromNib() + } + + private func addContextMenu() { + let menu = NSMenu() + + // Add custom menu items + let customItem = NSMenuItem(title: "Custom Action", action: #selector(customAction), keyEquivalent: "") + menu.addItem(customItem) + + let anotherItem = NSMenuItem(title: "Another Action", action: #selector(anotherAction), keyEquivalent: "") + menu.addItem(anotherItem) + + // Set the context menu + self.menu = menu + } + + @objc private func customAction() { + print("Custom Action Triggered") + // Implement your custom action here + } + + @objc private func anotherAction() { + print("Another Action Triggered") + // Implement another action here + } + + override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { + super.order(place, relativeTo: otherWin) + hiddenWindowAtLaunch() } - - RegisterGeneratedPlugins(registry: flutterViewController) - - super.awakeFromNib() - } - - override public func order(_ place: NSWindow.OrderingMode, relativeTo otherWin: Int) { - super.order(place, relativeTo: otherWin) - hiddenWindowAtLaunch() - } -} +} \ No newline at end of file diff --git a/pubspec.yaml b/pubspec.yaml index 032faf2..aba4c6e 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -19,7 +19,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev version: 2.0.2 environment: - sdk: '>=3.1.2 <4.0.0' + sdk: '>=3.7.0 <4.0.0' # Dependencies specify other packages that your package needs in order to work. # To automatically upgrade your package dependencies to the latest versions @@ -37,12 +37,16 @@ dependencies: cupertino_icons: ^1.0.2 rocket_timer: ^0.0.2 local_notifier: ^0.1.6 - window_manager: ^0.3.9 + window_manager: ^0.4.3 shared_preferences: ^2.2.3 google_fonts: ^6.1.0 url_launcher: ^6.3.1 - launch_at_startup: ^0.3.1 + launch_at_startup: ^0.5.1 package_info_plus: ^8.3.0 + flutter_localizations: + sdk: flutter + intl: any + dev_dependencies: flutter_test: @@ -53,13 +57,14 @@ dev_dependencies: # activated in the `analysis_options.yaml` file located at the root of your # package. See that file for information about deactivating specific lint # rules and activating additional ones. - flutter_lints: ^2.0.0 + flutter_lints: ^5.0.0 # For information on the generic Dart part of this file, see the # following page: https://dart.dev/tools/pub/pubspec # The following section is specific to Flutter packages. flutter: + generate: true # The following line ensures that the Material Icons font is # included with your application, so that you can use the icons in From 7eb64107a279003319b86253aaf3fd386dabf0c7 Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 11:44:05 +0100 Subject: [PATCH 14/18] bump version --- pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pubspec.yaml b/pubspec.yaml index aba4c6e..b84c797 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.0.2 +version: 2.0.3 environment: sdk: '>=3.7.0 <4.0.0' From 18c495c63068a706afe486d8db149143ede1911e Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 21:03:35 +0100 Subject: [PATCH 15/18] Update brand name from Eyes Care to Taline Co-authored-by: Qwen-Coder --- .github/workflows/desktop_build.yaml | 14 +++++++------- README.md | 14 +++++++------- android/app/src/main/AndroidManifest.xml | 2 +- installer/dmg_creator/config.json | 4 ++-- ios/Runner/Info.plist | 2 +- lib/l10n/app_en.arb | 2 +- lib/l10n/app_localizations_en.dart | 2 +- lib/main.dart | 2 +- macos/Runner/Configs/AppInfo.xcconfig | 2 +- 9 files changed, 22 insertions(+), 22 deletions(-) diff --git a/.github/workflows/desktop_build.yaml b/.github/workflows/desktop_build.yaml index 89acbcf..e2efc66 100644 --- a/.github/workflows/desktop_build.yaml +++ b/.github/workflows/desktop_build.yaml @@ -69,8 +69,8 @@ jobs: sudo apt-get install -y libgtk-3-dev libx11-dev pkg-config cmake ninja-build libblkid-dev - run: flutter doctor -v - # Checkout EyesCare code, recreate missing files, and get packages. - - name: Checkout EyesCare code + # Checkout Taline code, recreate missing files, and get packages. + - name: Checkout Taline code uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1 - run: flutter create . - run: flutter pub get @@ -90,7 +90,7 @@ jobs: working-directory: ${{ matrix.build_path }} - name: Compress build for Linux if: matrix.target == 'Linux' - run: tar czf $GITHUB_WORKSPACE/EyesCare${{ matrix.target }}.tar.gz * + run: tar czf $GITHUB_WORKSPACE/Taline${{ matrix.target }}.tar.gz * working-directory: ${{ matrix.build_path }} - name: Setup Node.js environment uses: actions/setup-node@v2 @@ -99,10 +99,10 @@ jobs: 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 + run: appdmg installer/dmg_creator/config.json $GITHUB_WORKSPACE/Taline${{ matrix.target }}.dmg - name: Compress build for Windows if: matrix.target == 'Windows' - run: compress-archive -Path * -DestinationPath ${env:GITHUB_WORKSPACE}\EyesCare${{ matrix.target }}.zip + run: compress-archive -Path * -DestinationPath ${env:GITHUB_WORKSPACE}\Taline${{ matrix.target }}.zip working-directory: ${{ matrix.build_path }} # Upload the build. @@ -113,6 +113,6 @@ jobs: GITHUB_TOKEN: ${{ secrets.TOKEN }} with: upload_url: ${{ needs.draft-release.outputs.upload_url }} - asset_path: ./EyesCare${{ matrix.target }}${{ matrix.asset_extension }} - asset_name: EyesCare${{ matrix.target }}${{ matrix.asset_extension }} + asset_path: ./Taline${{ matrix.target }}${{ matrix.asset_extension }} + asset_name: Taline${{ matrix.target }}${{ matrix.asset_extension }} asset_content_type: ${{ matrix.asset_content_type }} \ No newline at end of file diff --git a/README.md b/README.md index b031869..1c8aab0 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ -EyesCare - Health & Fitness, Productivity  | Product Hunt -# Eyes Care Desktop App +Taline - Health & Fitness, Productivity  | Product Hunt +# Taline Desktop App -Eyes Care is a desktop application built with Flutter, designed to help users maintain healthy eyes by following the 20-20-20 rule. The app reminds users to take regular breaks and rest their eyes to reduce eye strain and prevent digital eye fatigue. +Taline is a desktop application built with Flutter, designed to help users maintain healthy eyes by following the 20-20-20 rule. The app reminds users to take regular breaks and rest their eyes to reduce eye strain and prevent digital eye fatigue. ## Screenshots @@ -11,7 +11,7 @@ Eyes Care is a desktop application built with Flutter, designed to help users ma ## Support - + @@ -44,13 +44,13 @@ Eyes Care is a desktop application built with Flutter, designed to help users ma ## Usage -1. Launch the Eyes Care app on your desktop. +1. Launch the Taline app on your desktop. 2. Start using the app, and it will notify you when it's time to take a break. 4. During each break, follow the 20-20-20 rule by looking at an object 20 feet away for 20 seconds. ## Contributing -We welcome contributions from the community to enhance the Eyes Care app. To contribute, please follow these steps: +We welcome contributions from the community to enhance the Taline app. To contribute, please follow these steps: 1. Fork the repository. 2. Create a new branch: `git checkout -b feature/your-feature-name` @@ -66,7 +66,7 @@ We welcome contributions from the community to enhance the Eyes Care app. To con - [rocket_timer](https://pub.dev/packages/rocket_timer) -Special thanks to these amazing projects from [LeanFlutter](https://github.com/leanflutter) which help power CareEyes: +Special thanks to these amazing projects from [LeanFlutter](https://github.com/leanflutter) which help power Taline: - [local_notifier](https://pub.dev/packages/local_notifier) - [window_manager](https://pub.dev/packages/window_manager) diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index a8ff0aa..6c64504 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -1,6 +1,6 @@ CFBundleDevelopmentRegion $(DEVELOPMENT_LANGUAGE) CFBundleDisplayName - Keep Your Eyes + Taline CFBundleExecutable $(EXECUTABLE_NAME) CFBundleIdentifier diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index e0f892e..39bf45a 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -1,6 +1,6 @@ { "@@locale": "en", - "appTitle": "Eyes Care", + "appTitle": "Taline", "forceMode": "Force Mode", "forceModeSubtitle": "Prevent window minimization during breaks", "startupAtLogin": "Startup at Login", diff --git a/lib/l10n/app_localizations_en.dart b/lib/l10n/app_localizations_en.dart index 8262727..dc5f67f 100644 --- a/lib/l10n/app_localizations_en.dart +++ b/lib/l10n/app_localizations_en.dart @@ -9,7 +9,7 @@ class AppLocalizationsEn extends AppLocalizations { AppLocalizationsEn([String locale = 'en']) : super(locale); @override - String get appTitle => 'Eyes Care'; + String get appTitle => 'Taline'; @override String get forceMode => 'Force Mode'; diff --git a/lib/main.dart b/lib/main.dart index 7022c8f..fd1ad31 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -64,7 +64,7 @@ class CareYourEyes extends StatelessWidget { const secondaryColor = Color(0xFF32CD32); return MaterialApp( - title: 'Eyes Care', + title: 'Taline', debugShowCheckedModeBanner: false, locale: localeValue, localizationsDelegates: const [ diff --git a/macos/Runner/Configs/AppInfo.xcconfig b/macos/Runner/Configs/AppInfo.xcconfig index 45f2367..3da89a0 100644 --- a/macos/Runner/Configs/AppInfo.xcconfig +++ b/macos/Runner/Configs/AppInfo.xcconfig @@ -5,7 +5,7 @@ // 'flutter create' template. // The application's name. By default this is also the title of the Flutter window. -PRODUCT_NAME = Eyes Care +PRODUCT_NAME = Taline // The application's bundle identifier PRODUCT_BUNDLE_IDENTIFIER = com.example.keepYourEyes From 03f64ce4ff1e6a72b0b356df486fe656aa55660e Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 21:05:34 +0100 Subject: [PATCH 16/18] bump version --- lib/l10n/app_localizations.dart | 2 +- pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/lib/l10n/app_localizations.dart b/lib/l10n/app_localizations.dart index 0957cc4..416d1aa 100644 --- a/lib/l10n/app_localizations.dart +++ b/lib/l10n/app_localizations.dart @@ -119,7 +119,7 @@ abstract class AppLocalizations { /// No description provided for @appTitle. /// /// In en, this message translates to: - /// **'Eyes Care'** + /// **'Taline'** String get appTitle; /// No description provided for @forceMode. diff --git a/pubspec.yaml b/pubspec.yaml index b84c797..ff18dc1 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html # In Windows, build-name is used as the major, minor, and patch parts # of the product and file versions while build-number is used as the build suffix. -version: 2.0.3 +version: 2.0.4 environment: sdk: '>=3.7.0 <4.0.0' From 60eac34ddf304155eefbb85f620eadcca7b7386b Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 21:08:44 +0100 Subject: [PATCH 17/18] minor fix --- lib/widgets/settings.dart | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/lib/widgets/settings.dart b/lib/widgets/settings.dart index 5826ddd..2cf9826 100644 --- a/lib/widgets/settings.dart +++ b/lib/widgets/settings.dart @@ -15,13 +15,13 @@ class Settings extends StatelessWidget { final Function(int, int) onConfirm; const Settings({ - Key? key, + super.key, required this.reminder, required this.breakTime, required this.forceModeEnabled, required this.onConfirm, required this.startUpModeEnabled, - }) : super(key: key); + }); @override Widget build(BuildContext context) { @@ -244,6 +244,7 @@ class Settings extends StatelessWidget { PreferenceService.setLanguage(lang['code']!); Navigator.pop(context); }, + borderRadius: BorderRadius.circular(12), child: Container( decoration: BoxDecoration( From 805830d2b44b8ccb3ecdeaff709e94b753f8262f Mon Sep 17 00:00:00 2001 From: Mohammed Date: Thu, 26 Mar 2026 21:11:52 +0100 Subject: [PATCH 18/18] Update README with multi-language and startup at login features Co-authored-by: Qwen-Coder --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 1c8aab0..3fbd77e 100644 --- a/README.md +++ b/README.md @@ -29,6 +29,8 @@ Taline is a desktop application built with Flutter, designed to help users maint - **Customizable Reminder:** Set personalized reminders to take breaks and rest your eyes based on your preferences, with options to follow the 20-20-20 rule or a custom time interval. - **Countdown Timer:** Monitor the time remaining until your next scheduled eye break, with a fully adjustable countdown timer feature to suit your needs. - **Notifications:** Get customizable desktop notifications or alerts to remind you when it's time for your next eye break with options for forcing take break mode. +- **Multi-language Support:** Available in multiple languages including English, Arabic, Spanish, French, German, Portuguese, Russian, Chinese, Japanese, Hindi, and Turkish. +- **Startup at Login:** Control whether Taline launches automatically when you log in to your computer. ## Coming soon