diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..c5f3f6b --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,3 @@ +{ + "java.configuration.updateBuildConfiguration": "interactive" +} \ No newline at end of file diff --git a/app/README.md b/app/README.md index 2db82b7..4ab2ffc 100644 --- a/app/README.md +++ b/app/README.md @@ -9,12 +9,15 @@ lib/ │ ├── cabinetdb.dart │ └── profiledb.dart ├── home/ -│ ├── addMenu.dart +│ ├── add_menu.dart │ └── home.dart ├── models/ │ ├── cabinet.dart │ └── profile.dart ├── profile/ │ └── profile.dart +├── services/ +│ ├── alarm_service.dart +│ └── notification_service.dart ├── dose.dart └── main.dart \ No newline at end of file diff --git a/app/android/app/build.gradle.kts b/app/android/app/build.gradle.kts index e272c33..b661ee0 100644 --- a/app/android/app/build.gradle.kts +++ b/app/android/app/build.gradle.kts @@ -11,6 +11,7 @@ android { ndkVersion = flutter.ndkVersion compileOptions { + isCoreLibraryDesugaringEnabled = true sourceCompatibility = JavaVersion.VERSION_17 targetCompatibility = JavaVersion.VERSION_17 } @@ -42,3 +43,7 @@ android { flutter { source = "../.." } + +dependencies { + coreLibraryDesugaring("com.android.tools:desugar_jdk_libs:2.1.5") +} \ No newline at end of file diff --git a/app/android/app/src/main/AndroidManifest.xml b/app/android/app/src/main/AndroidManifest.xml index 6ee3df0..af36229 100644 --- a/app/android/app/src/main/AndroidManifest.xml +++ b/app/android/app/src/main/AndroidManifest.xml @@ -1,21 +1,26 @@ + + + + + + + + + + - - + - - - - - - - - - + + + + + + + + + + \ No newline at end of file diff --git a/app/lib/db/cabinetdb.dart b/app/lib/db/cabinetdb.dart index 86c81d4..9a0bade 100644 --- a/app/lib/db/cabinetdb.dart +++ b/app/lib/db/cabinetdb.dart @@ -8,6 +8,30 @@ class DatabaseHelper { DatabaseHelper._init(); + Future update(Cabinet cabinet) async { + final db = await instance.database; + return db.update( + 'cabinet', + cabinet.toMap(), + where: 'id = ?', + whereArgs: [cabinet.id], + ); + } + + Future readMedicine(int id) async { + final db = await instance.database; + final maps = await db.query( + 'cabinet', + where: 'id = ?', + whereArgs: [id], + ); + + if (maps.isNotEmpty) { + return Cabinet.fromMap(maps.first); + } + return null; + } + Future get database async { if (_database != null) return _database!; _database = await _initDB('dose.db'); diff --git a/app/lib/dose.dart b/app/lib/dose.dart index bf5da0f..5801601 100644 --- a/app/lib/dose.dart +++ b/app/lib/dose.dart @@ -2,7 +2,10 @@ import 'package:flutter/material.dart'; import 'package:app/home/home.dart'; import 'package:app/analytics/analytics.dart'; import 'package:app/profile/profile.dart'; -import 'package:app/home/addMenu.dart'; +import 'package:app/home/add_menu.dart'; +import 'dart:async'; +import 'package:alarm/alarm.dart'; +import 'package:app/services/alarm_ring.dart'; class Dose extends StatefulWidget { const Dose({super.key}); @@ -43,6 +46,27 @@ class _DoseState extends State { } } + StreamSubscription? ringSubscription; + + @override + void initState() { + super.initState(); + ringSubscription = Alarm.ringStream.stream.listen((alarmSettings) { + Navigator.push( + context, + MaterialPageRoute( + builder: (context) => AlarmRingScreen(alarmSettings: alarmSettings), + ), + ); + }); + } + + @override + void dispose() { + ringSubscription?.cancel(); + super.dispose(); + } + @override Widget build(BuildContext context) { return Scaffold( diff --git a/app/lib/home/addMenu.dart b/app/lib/home/add_menu.dart similarity index 93% rename from app/lib/home/addMenu.dart rename to app/lib/home/add_menu.dart index b110793..91231a9 100644 --- a/app/lib/home/addMenu.dart +++ b/app/lib/home/add_menu.dart @@ -1,6 +1,8 @@ -import 'package:app/models/cabinet.dart'; import 'package:flutter/material.dart'; +import 'package:app/models/cabinet.dart'; import 'package:app/db/cabinetdb.dart'; +import 'package:app/services/notification_service.dart'; +import 'package:app/services/alarm_service.dart'; class AddMedicineMenu extends StatefulWidget { final VoidCallback onSave; @@ -49,12 +51,21 @@ class _AddMedicineMenuState extends State { priority: _priority, ); - await DatabaseHelper.instance.create(medicine); + int newId = await DatabaseHelper.instance.create(medicine); + + await AlarmService().scheduleMedicineAlarm(newId, medicine); + await NotificationHelper().scheduleMedicineNotification( + newId, + medicine.name, + timeString + ); + widget.onSave(); if (mounted) { Navigator.pop(context); } + } } @@ -129,7 +140,7 @@ class _AddMedicineMenuState extends State { const SizedBox(width: 12), Expanded( child: DropdownButtonFormField( - value: _cycle, + initialValue: _cycle, decoration: const InputDecoration( labelText: "Cycle", border: OutlineInputBorder(), @@ -177,7 +188,7 @@ class _AddMedicineMenuState extends State { const SizedBox(height: 16), DropdownButtonFormField( - value: _priority, + initialValue: _priority, decoration: const InputDecoration( labelText: "Priority", border: OutlineInputBorder(), diff --git a/app/lib/home/home.dart b/app/lib/home/home.dart index 38d76a0..1f9de2c 100644 --- a/app/lib/home/home.dart +++ b/app/lib/home/home.dart @@ -1,6 +1,8 @@ import 'package:app/models/cabinet.dart'; import 'package:flutter/material.dart'; import 'package:app/db/cabinetdb.dart'; +import 'package:app/services/notification_service.dart'; +import 'package:app/services/alarm_service.dart'; class HomePage extends StatefulWidget { const HomePage({super.key}); @@ -44,6 +46,8 @@ class _HomePageState extends State { if (result == 'delete') { await DatabaseHelper.instance.delete(id); + await NotificationHelper().cancelNotification(id); + await AlarmService().cancelAlarm(id); setState(() { _refreshList(); }); @@ -54,121 +58,132 @@ class _HomePageState extends State { Widget build(BuildContext context) { final colorScheme = Theme.of(context).colorScheme; - return FutureBuilder>( - future: _medicinesFuture, - builder: (context, snapshot) { - if (snapshot.hasError) { - return Center( - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Text( - "Error loading data: ${snapshot.error}", - textAlign: TextAlign.center, - style: TextStyle(color: colorScheme.error), - ), - ), - ); - } - - if (snapshot.connectionState == ConnectionState.waiting) { - return const Center(child: CircularProgressIndicator()); - } + return Column( + children: [ + Padding( + padding: const EdgeInsets.all(16.0), + child: ElevatedButton( + onPressed: () async { + await AlarmService().triggerTestAlarm(); + }, + child: const Text('Test'), + ), + ), + Expanded( + child: FutureBuilder>( + future: _medicinesFuture, + builder: (context, snapshot) { + if (snapshot.hasError) { + return Center( + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Text( + "Error loading data: ${snapshot.error}", + textAlign: TextAlign.center, + style: TextStyle(color: colorScheme.error), + ), + ), + ); + } - final medicines = snapshot.data ?? []; + if (snapshot.connectionState == ConnectionState.waiting) { + return const Center(child: CircularProgressIndicator()); + } - if (medicines.isEmpty) { - return Center( - child: Text( - "No reminders added yet.", - style: TextStyle(color: colorScheme.onSurfaceVariant), - ), - ); - } + final medicines = snapshot.data ?? []; - return ListView.builder( - padding: const EdgeInsets.all(16), - itemCount: medicines.length, - itemBuilder: (context, index) { - final med = medicines[index]; - return GestureDetector( - onLongPressStart: (details) { - _deleteMedicine(med.id!, details.globalPosition); - }, - child: Card( - elevation: 0, - shape: RoundedRectangleBorder( - borderRadius: BorderRadius.circular(16), - side: BorderSide(color: colorScheme.outlineVariant), - ), - color: colorScheme.surfaceContainer, - margin: const EdgeInsets.only(bottom: 12), - child: Padding( - padding: const EdgeInsets.all(16.0), - child: Row( - children: [ - // Icon - Container( - width: 48, - height: 48, - decoration: BoxDecoration( - color: colorScheme.tertiaryContainer, - shape: BoxShape.circle, - ), - child: Icon(Icons.medication, color: colorScheme.onTertiaryContainer), + if (medicines.isEmpty) { + return Center( + child: Text( + "No reminders added yet.", + style: TextStyle(color: colorScheme.onSurfaceVariant), + ), + ); + } + + return ListView.builder( + padding: const EdgeInsets.all(16), + itemCount: medicines.length, + itemBuilder: (context, index) { + final med = medicines[index]; + return GestureDetector( + onLongPressStart: (details) { + _deleteMedicine(med.id!, details.globalPosition); + }, + child: Card( + elevation: 0, + shape: RoundedRectangleBorder( + borderRadius: BorderRadius.circular(16), + side: BorderSide(color: colorScheme.outlineVariant), ), - const SizedBox(width: 16), - // Med name - Expanded( - child: Column( - crossAxisAlignment: CrossAxisAlignment.start, + color: colorScheme.surfaceContainer, + margin: const EdgeInsets.only(bottom: 12), + child: Padding( + padding: const EdgeInsets.all(16.0), + child: Row( children: [ + Container( + width: 48, + height: 48, + decoration: BoxDecoration( + color: colorScheme.tertiaryContainer, + shape: BoxShape.circle, + ), + child: Icon(Icons.medication, color: colorScheme.onTertiaryContainer), + ), + const SizedBox(width: 16), + Expanded( + child: Column( + crossAxisAlignment: CrossAxisAlignment.start, + children: [ + Text( + med.name, + style: TextStyle( + fontSize: 16, + fontWeight: FontWeight.bold, + color: colorScheme.onSurface, + ), + ), + const SizedBox(height: 4), + Text( + med.dosage, + style: TextStyle( + fontSize: 14, + color: colorScheme.onSurfaceVariant, + ), + ), + ], + ), + ), Text( - med.name, + med.time, style: TextStyle( fontSize: 16, - fontWeight: FontWeight.bold, + fontWeight: FontWeight.w600, color: colorScheme.onSurface, ), ), - const SizedBox(height: 4), - Text( - med.dosage, - style: TextStyle( - fontSize: 14, - color: colorScheme.onSurfaceVariant, + const SizedBox(width: 16), + Container( + width: 40, + height: 40, + decoration: BoxDecoration( + color: colorScheme.primaryContainer, + borderRadius: BorderRadius.circular(12), ), + child: Icon(Icons.check, color: colorScheme.onPrimaryContainer), ), ], ), ), - // Time - Text( - med.time, - style: TextStyle( - fontSize: 16, - fontWeight: FontWeight.w600, - color: colorScheme.onSurface, - ), - ), - const SizedBox(width: 16), - // Checkbox - Container( - width: 40, - height: 40, - decoration: BoxDecoration( - color: colorScheme.primaryContainer, - borderRadius: BorderRadius.circular(12), - ), - child: Icon(Icons.check, color: colorScheme.onPrimaryContainer), - ), - ], - ), - ), - ), - ); - }, - ); - }, + ), + ); + }, + ); + }, + ), + ), + ], ); } } \ No newline at end of file diff --git a/app/lib/main.dart b/app/lib/main.dart index 0bd93c7..8e30ed9 100644 --- a/app/lib/main.dart +++ b/app/lib/main.dart @@ -1,14 +1,29 @@ import 'package:flutter/material.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:app/dose.dart'; +import 'package:app/services/notification_service.dart'; +import 'package:app/services/alarm_service.dart'; -void main() { - runApp(const MyApp()); +void main() async { + WidgetsFlutterBinding.ensureInitialized(); + await NotificationHelper().init(); + await AlarmService().init(); + runApp(const MyApp()); } class MyApp extends StatelessWidget { const MyApp({super.key}); + static final _defaultLightColorScheme = ColorScheme.fromSeed( + seedColor: Colors.deepPurple, + brightness: Brightness.light, + ); + + static final _defaultDarkColorScheme = ColorScheme.fromSeed( + seedColor: Colors.deepPurple, + brightness: Brightness.dark, + ); + @override Widget build(BuildContext context) { return DynamicColorBuilder( @@ -16,15 +31,14 @@ class MyApp extends StatelessWidget { return MaterialApp( debugShowCheckedModeBanner: false, theme: ThemeData( - colorScheme: lightDynamic, + colorScheme: lightDynamic ?? _defaultLightColorScheme, useMaterial3: true, ), darkTheme: ThemeData( - colorScheme: darkDynamic, + colorScheme: darkDynamic ?? _defaultDarkColorScheme, useMaterial3: true, ), themeMode: ThemeMode.system, - home: const Dose(), ); }, diff --git a/app/lib/services/alarm_ring.dart b/app/lib/services/alarm_ring.dart new file mode 100644 index 0000000..81774a1 --- /dev/null +++ b/app/lib/services/alarm_ring.dart @@ -0,0 +1,35 @@ +import 'package:flutter/material.dart'; +import 'package:alarm/alarm.dart'; + +class AlarmRingScreen extends StatelessWidget { + final AlarmSettings alarmSettings; + + const AlarmRingScreen({super.key, required this.alarmSettings}); + + @override + Widget build(BuildContext context) { + return Scaffold( + body: Center( + child: Column( + mainAxisAlignment: MainAxisAlignment.center, + children: [ + const Icon(Icons.alarm, size: 100), + const SizedBox(height: 20), + Text( + alarmSettings.notificationSettings.title, + style: const TextStyle(fontSize: 24, fontWeight: FontWeight.bold), + ), + const SizedBox(height: 40), + ElevatedButton( + onPressed: () async { + await Alarm.stop(alarmSettings.id); + if (context.mounted) Navigator.pop(context); + }, + child: const Text('Stop Alarm'), + ), + ], + ), + ), + ); + } +} \ No newline at end of file diff --git a/app/lib/services/alarm_service.dart b/app/lib/services/alarm_service.dart new file mode 100644 index 0000000..aeedba6 --- /dev/null +++ b/app/lib/services/alarm_service.dart @@ -0,0 +1,81 @@ +import 'package:alarm/alarm.dart'; +import 'package:app/models/cabinet.dart'; + +class AlarmService { + static final AlarmService _instance = AlarmService._internal(); + factory AlarmService() => _instance; + AlarmService._internal(); + + Future triggerTestAlarm() async { + final alarmSettings = AlarmSettings( + id: 999, + dateTime: DateTime.now().add(const Duration(seconds: 20)), + assetAudioPath: null, + loopAudio: true, + vibrate: true, + warningNotificationOnKill: true, + androidFullScreenIntent: true, + volumeSettings: VolumeSettings.fade( + volume: 1.0, + fadeDuration: const Duration(seconds: 3), + ), + notificationSettings: NotificationSettings( + title: 'Test Alarm', + body: 'Testing the alarm system.', + stopButton: 'Stop', + ), + ); + + await Alarm.set(alarmSettings: alarmSettings); + } + + Future init() async { + await Alarm.init(); + } + + Future scheduleMedicineAlarm(int id, Cabinet medicine) async { + if (medicine.priority != 2) return; + + final parts = medicine.time.split(':'); + final int hour = int.parse(parts[0]); + final int minute = int.parse(parts[1]); + + DateTime now = DateTime.now(); + DateTime scheduledDate = DateTime( + now.year, + now.month, + now.day, + hour, + minute, + ); + + if (scheduledDate.isBefore(now)) { + scheduledDate = scheduledDate.add(const Duration(days: 1)); + } + + final alarmSettings = AlarmSettings( + id: id, + dateTime: scheduledDate, + assetAudioPath: null, + loopAudio: true, + vibrate: true, + warningNotificationOnKill: true, + androidFullScreenIntent: true, + volumeSettings: VolumeSettings.fade( + volume: 1.0, + fadeDuration: const Duration(seconds: 3), + ), + notificationSettings: NotificationSettings( + title: 'Time to take ${medicine.name}', + body: 'Please take your scheduled dose.', + stopButton: 'Stop', + ), + ); + + await Alarm.set(alarmSettings: alarmSettings); +} + + Future cancelAlarm(int id) async { + await Alarm.stop(id); + } +} \ No newline at end of file diff --git a/app/lib/services/notification_service.dart b/app/lib/services/notification_service.dart new file mode 100644 index 0000000..f981b9f --- /dev/null +++ b/app/lib/services/notification_service.dart @@ -0,0 +1,93 @@ +import 'package:flutter_local_notifications/flutter_local_notifications.dart'; +import 'package:timezone/data/latest_all.dart' as tz; +import 'package:timezone/timezone.dart' as tz; +import 'package:flutter_timezone/flutter_timezone.dart'; +import 'package:app/db/cabinetdb.dart'; +import 'package:app/models/cabinet.dart'; + +class NotificationHelper { + static final NotificationHelper _instance = NotificationHelper._internal(); + factory NotificationHelper() => _instance; + NotificationHelper._internal(); + + final FlutterLocalNotificationsPlugin plugin = FlutterLocalNotificationsPlugin(); + + Future init() async { + tz.initializeTimeZones(); + final TimezoneInfo timeZoneInfo = await FlutterTimezone.getLocalTimezone(); + tz.setLocalLocation(tz.getLocation(timeZoneInfo.identifier)); + + const AndroidInitializationSettings androidSettings = AndroidInitializationSettings('@mipmap/ic_launcher'); + const InitializationSettings settings = InitializationSettings(android: androidSettings); + + await plugin.initialize( + settings: settings, + onDidReceiveNotificationResponse: (NotificationResponse response) async { + if (response.payload != null && response.payload!.startsWith('med_')) { + final int id = int.parse(response.payload!.split('_')[1]); + + if (response.actionId == 'action_done') { + final med = await DatabaseHelper.instance.readMedicine(id); + if (med != null && med.currstock > 0) { + final updatedMed = Cabinet( + id: med.id, + name: med.name, + dosage: med.dosage, + time: med.time, + initstock: med.initstock, + currstock: med.currstock - 1, + priority: med.priority, + ); + await DatabaseHelper.instance.update(updatedMed); + } + } + } + }, + ); + + final androidImplementation = plugin.resolvePlatformSpecificImplementation(); + await androidImplementation?.requestNotificationsPermission(); + await androidImplementation?.requestExactAlarmsPermission(); + } + + Future scheduleMedicineNotification(int id, String name, String timeString) async { + final parts = timeString.split(':'); + final int hour = int.parse(parts[0]); + final int minute = int.parse(parts[1]); + + final tz.TZDateTime now = tz.TZDateTime.now(tz.local); + tz.TZDateTime scheduledDate = tz.TZDateTime(tz.local, now.year, now.month, now.day, hour, minute); + + if (scheduledDate.isBefore(now)) { + scheduledDate = scheduledDate.add(const Duration(days: 1)); + } + + const AndroidNotificationDetails androidDetails = AndroidNotificationDetails( + 'priority_channel', + 'Priority Reminders', + importance: Importance.max, + priority: Priority.high, + actions: [ + AndroidNotificationAction('action_done', 'Done'), + AndroidNotificationAction('action_not_taken', 'Not taken'), + ], + ); + + const NotificationDetails details = NotificationDetails(android: androidDetails); + + await plugin.zonedSchedule( + id: id, + title: 'Time to take $name', + body: 'Did you take your dose?', + scheduledDate: scheduledDate, + notificationDetails: details, + androidScheduleMode: AndroidScheduleMode.exactAllowWhileIdle, + matchDateTimeComponents: DateTimeComponents.time, + payload: 'med_$id', + ); + } + + Future cancelNotification(int id) async { + await plugin.cancel(id: id); + } +} \ No newline at end of file diff --git a/app/linux/flutter/generated_plugin_registrant.cc b/app/linux/flutter/generated_plugin_registrant.cc index 675b719..8a93927 100644 --- a/app/linux/flutter/generated_plugin_registrant.cc +++ b/app/linux/flutter/generated_plugin_registrant.cc @@ -7,9 +7,13 @@ #include "generated_plugin_registrant.h" #include +#include void fl_register_plugins(FlPluginRegistry* registry) { g_autoptr(FlPluginRegistrar) dynamic_color_registrar = fl_plugin_registry_get_registrar_for_plugin(registry, "DynamicColorPlugin"); dynamic_color_plugin_register_with_registrar(dynamic_color_registrar); + g_autoptr(FlPluginRegistrar) flutter_timezone_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "FlutterTimezonePlugin"); + flutter_timezone_plugin_register_with_registrar(flutter_timezone_registrar); } diff --git a/app/linux/flutter/generated_plugins.cmake b/app/linux/flutter/generated_plugins.cmake index 3e303c1..35c8ab1 100644 --- a/app/linux/flutter/generated_plugins.cmake +++ b/app/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + flutter_timezone ) list(APPEND FLUTTER_FFI_PLUGIN_LIST diff --git a/app/macos/Flutter/GeneratedPluginRegistrant.swift b/app/macos/Flutter/GeneratedPluginRegistrant.swift index 0c2a31a..8d243d8 100644 --- a/app/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/app/macos/Flutter/GeneratedPluginRegistrant.swift @@ -6,9 +6,15 @@ import FlutterMacOS import Foundation import dynamic_color +import flutter_local_notifications +import flutter_timezone +import shared_preferences_foundation import sqflite_darwin func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) + FlutterLocalNotificationsPlugin.register(with: registry.registrar(forPlugin: "FlutterLocalNotificationsPlugin")) + FlutterTimezonePlugin.register(with: registry.registrar(forPlugin: "FlutterTimezonePlugin")) + SharedPreferencesPlugin.register(with: registry.registrar(forPlugin: "SharedPreferencesPlugin")) SqflitePlugin.register(with: registry.registrar(forPlugin: "SqflitePlugin")) } diff --git a/app/pubspec.lock b/app/pubspec.lock index 98b1bb0..21af346 100644 --- a/app/pubspec.lock +++ b/app/pubspec.lock @@ -1,6 +1,22 @@ # Generated by pub # See https://dart.dev/tools/pub/glossary#lockfile packages: + alarm: + dependency: "direct main" + description: + name: alarm + sha256: f50869fb28d46ce44922340a44a0ad9ee4be298349efeb20bbfd44aeba0c257d + url: "https://pub.dev" + source: hosted + version: "5.2.1" + args: + dependency: transitive + description: + name: args + sha256: d0481093c50b1da8910eb0bb301626d4d8eb7284aa739614d2b394ee09e3ea04 + url: "https://pub.dev" + source: hosted + version: "2.7.0" async: dependency: transitive description: @@ -41,14 +57,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.19.1" - cupertino_icons: - dependency: "direct main" + dbus: + dependency: transitive description: - name: cupertino_icons - sha256: ba631d1c7f7bef6b729a622b7b752645a2d076dba9976925b8f25725a30e1ee6 + name: dbus + sha256: d0c98dcd4f5169878b6cf8f6e0a52403a9dff371a3e2f019697accbf6f44a270 url: "https://pub.dev" source: hosted - version: "1.0.8" + version: "0.7.12" dynamic_color: dependency: "direct main" description: @@ -57,6 +73,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.8.1" + equatable: + dependency: transitive + description: + name: equatable + sha256: "3e0141505477fd8ad55d6eb4e7776d3fe8430be8e497ccb1521370c3f21a3e2b" + url: "https://pub.dev" + source: hosted + version: "2.0.8" fake_async: dependency: transitive description: @@ -65,11 +89,35 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.3" + ffi: + dependency: transitive + description: + name: ffi + sha256: "6d7fd89431262d8f3125e81b50d3847a091d846eafcd4fdb88dd06f36d705a45" + url: "https://pub.dev" + source: hosted + version: "2.2.0" + file: + dependency: transitive + description: + name: file + sha256: a3b4f84adafef897088c160faf7dfffb7696046cb13ae90b508c2cbc95d3b8d4 + url: "https://pub.dev" + source: hosted + version: "7.0.1" flutter: dependency: "direct main" description: flutter source: sdk version: "0.0.0" + flutter_fgbg: + dependency: transitive + description: + name: flutter_fgbg + sha256: eb6da9b2047372566a6e17b505975fe5bace94af01f6fc825c4b6f81baa6c447 + url: "https://pub.dev" + source: hosted + version: "0.7.1" flutter_lints: dependency: "direct dev" description: @@ -78,11 +126,80 @@ packages: url: "https://pub.dev" source: hosted version: "6.0.0" + flutter_local_notifications: + dependency: "direct main" + description: + name: flutter_local_notifications + sha256: "2b50e938a275e1ad77352d6a25e25770f4130baa61eaf02de7a9a884680954ad" + url: "https://pub.dev" + source: hosted + version: "20.1.0" + flutter_local_notifications_linux: + dependency: transitive + description: + name: flutter_local_notifications_linux + sha256: dce0116868cedd2cdf768af0365fc37ff1cbef7c02c4f51d0587482e625868d0 + url: "https://pub.dev" + source: hosted + version: "7.0.0" + flutter_local_notifications_platform_interface: + dependency: transitive + description: + name: flutter_local_notifications_platform_interface + sha256: "23de31678a48c084169d7ae95866df9de5c9d2a44be3e5915a2ff067aeeba899" + url: "https://pub.dev" + source: hosted + version: "10.0.0" + flutter_local_notifications_windows: + dependency: transitive + description: + name: flutter_local_notifications_windows + sha256: e97a1a3016512437d9c0b12fae7d1491c3c7b9aa7f03a69b974308840656b02a + url: "https://pub.dev" + source: hosted + version: "2.0.1" flutter_test: dependency: "direct dev" description: flutter source: sdk version: "0.0.0" + flutter_timezone: + dependency: "direct main" + description: + name: flutter_timezone + sha256: "978192f2f9ea6d019a4de4f0211d76a9af955ca24865828fa98ca4e20cf0cb3c" + url: "https://pub.dev" + source: hosted + version: "5.0.1" + flutter_web_plugins: + dependency: transitive + description: flutter + source: sdk + version: "0.0.0" + http: + dependency: transitive + description: + name: http + sha256: "87721a4a50b19c7f1d49001e51409bddc46303966ce89a65af4f4e6004896412" + url: "https://pub.dev" + source: hosted + version: "1.6.0" + http_parser: + dependency: transitive + description: + name: http_parser + sha256: "178d74305e7866013777bab2c3d8726205dc5a4dd935297175b19a23a2e66571" + url: "https://pub.dev" + source: hosted + version: "4.1.2" + json_annotation: + dependency: transitive + description: + name: json_annotation + sha256: cb09e7dac6210041fad964ed7fbee004f14258b4eca4040f72d1234062ace4c8 + url: "https://pub.dev" + source: hosted + version: "4.11.0" leak_tracker: dependency: transitive description: @@ -115,6 +232,14 @@ packages: url: "https://pub.dev" source: hosted version: "6.1.0" + logging: + dependency: transitive + description: + name: logging + sha256: c8245ada5f1717ed44271ed1c26b8ce85ca3228fd2ffdb75468ab01979309d61 + url: "https://pub.dev" + source: hosted + version: "1.3.0" matcher: dependency: transitive description: @@ -147,6 +272,86 @@ packages: url: "https://pub.dev" source: hosted version: "1.9.1" + path_provider_linux: + dependency: transitive + description: + name: path_provider_linux + sha256: f7a1fe3a634fe7734c8d3f2766ad746ae2a2884abe22e241a8b301bf5cac3279 + url: "https://pub.dev" + source: hosted + version: "2.2.1" + path_provider_platform_interface: + dependency: transitive + description: + name: path_provider_platform_interface + sha256: "88f5779f72ba699763fa3a3b06aa4bf6de76c8e5de842cf6f29e2e06476c2334" + url: "https://pub.dev" + source: hosted + version: "2.1.2" + path_provider_windows: + dependency: transitive + description: + name: path_provider_windows + sha256: bd6f00dbd873bfb70d0761682da2b3a2c2fccc2b9e84c495821639601d81afe7 + url: "https://pub.dev" + source: hosted + version: "2.3.0" + permission_handler: + dependency: "direct main" + description: + name: permission_handler + sha256: bc917da36261b00137bbc8896bf1482169cd76f866282368948f032c8c1caae1 + url: "https://pub.dev" + source: hosted + version: "12.0.1" + permission_handler_android: + dependency: transitive + description: + name: permission_handler_android + sha256: "1e3bc410ca1bf84662104b100eb126e066cb55791b7451307f9708d4007350e6" + url: "https://pub.dev" + source: hosted + version: "13.0.1" + permission_handler_apple: + dependency: transitive + description: + name: permission_handler_apple + sha256: f000131e755c54cf4d84a5d8bd6e4149e262cc31c5a8b1d698de1ac85fa41023 + url: "https://pub.dev" + source: hosted + version: "9.4.7" + permission_handler_html: + dependency: transitive + description: + name: permission_handler_html + sha256: "38f000e83355abb3392140f6bc3030660cfaef189e1f87824facb76300b4ff24" + url: "https://pub.dev" + source: hosted + version: "0.1.3+5" + permission_handler_platform_interface: + dependency: transitive + description: + name: permission_handler_platform_interface + sha256: eb99b295153abce5d683cac8c02e22faab63e50679b937fa1bf67d58bb282878 + url: "https://pub.dev" + source: hosted + version: "4.3.0" + permission_handler_windows: + dependency: transitive + description: + name: permission_handler_windows + sha256: "1a790728016f79a41216d88672dbc5df30e686e811ad4e698bfc51f76ad91f1e" + url: "https://pub.dev" + source: hosted + version: "0.2.1" + petitparser: + dependency: transitive + description: + name: petitparser + sha256: "91bd59303e9f769f108f8df05e371341b15d59e995e6806aefab827b58336675" + url: "https://pub.dev" + source: hosted + version: "7.0.2" platform: dependency: transitive description: @@ -163,6 +368,70 @@ packages: url: "https://pub.dev" source: hosted version: "2.1.8" + rxdart: + dependency: transitive + description: + name: rxdart + sha256: "5c3004a4a8dbb94bd4bf5412a4def4acdaa12e12f269737a5751369e12d1a962" + url: "https://pub.dev" + source: hosted + version: "0.28.0" + shared_preferences: + dependency: transitive + description: + name: shared_preferences + sha256: "2939ae520c9024cb197fc20dee269cd8cdbf564c8b5746374ec6cacdc5169e64" + url: "https://pub.dev" + source: hosted + version: "2.5.4" + shared_preferences_android: + dependency: transitive + description: + name: shared_preferences_android + sha256: "8374d6200ab33ac99031a852eba4c8eb2170c4bf20778b3e2c9eccb45384fb41" + url: "https://pub.dev" + source: hosted + version: "2.4.21" + shared_preferences_foundation: + dependency: transitive + description: + name: shared_preferences_foundation + sha256: "4e7eaffc2b17ba398759f1151415869a34771ba11ebbccd1b0145472a619a64f" + url: "https://pub.dev" + source: hosted + version: "2.5.6" + shared_preferences_linux: + dependency: transitive + description: + name: shared_preferences_linux + sha256: "580abfd40f415611503cae30adf626e6656dfb2f0cee8f465ece7b6defb40f2f" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_platform_interface: + dependency: transitive + description: + name: shared_preferences_platform_interface + sha256: "57cbf196c486bc2cf1f02b85784932c6094376284b3ad5779d1b1c6c6a816b80" + url: "https://pub.dev" + source: hosted + version: "2.4.1" + shared_preferences_web: + dependency: transitive + description: + name: shared_preferences_web + sha256: c49bd060261c9a3f0ff445892695d6212ff603ef3115edbb448509d407600019 + url: "https://pub.dev" + source: hosted + version: "2.4.3" + shared_preferences_windows: + dependency: transitive + description: + name: shared_preferences_windows + sha256: "94ef0f72b2d71bc3e700e025db3710911bd51a71cefb65cc609dd0d9a982e3c1" + url: "https://pub.dev" + source: hosted + version: "2.4.1" sky_engine: dependency: transitive description: flutter @@ -264,6 +533,22 @@ packages: url: "https://pub.dev" source: hosted version: "0.7.9" + timezone: + dependency: "direct main" + description: + name: timezone + sha256: dd14a3b83cfd7cb19e7888f1cbc20f258b8d71b54c06f79ac585f14093a287d1 + url: "https://pub.dev" + source: hosted + version: "0.10.1" + typed_data: + dependency: transitive + description: + name: typed_data + sha256: f9049c039ebfeb4cf7a7104a675823cd72dba8297f264b6637062516699fa006 + url: "https://pub.dev" + source: hosted + version: "1.4.0" vector_math: dependency: transitive description: @@ -280,6 +565,30 @@ packages: url: "https://pub.dev" source: hosted version: "15.0.2" + web: + dependency: transitive + description: + name: web + sha256: "868d88a33d8a87b18ffc05f9f030ba328ffefba92d6c127917a2ba740f9cfe4a" + url: "https://pub.dev" + source: hosted + version: "1.1.1" + xdg_directories: + dependency: transitive + description: + name: xdg_directories + sha256: "7a3f37b05d989967cdddcbb571f1ea834867ae2faa29725fd085180e0883aa15" + url: "https://pub.dev" + source: hosted + version: "1.1.0" + xml: + dependency: transitive + description: + name: xml + sha256: "971043b3a0d3da28727e40ed3e0b5d18b742fa5a68665cca88e74b7876d5e025" + url: "https://pub.dev" + source: hosted + version: "6.6.1" sdks: dart: ">=3.10.1 <4.0.0" - flutter: ">=3.24.0" + flutter: ">=3.35.0" diff --git a/app/pubspec.yaml b/app/pubspec.yaml index 29055bf..8c5bff2 100644 --- a/app/pubspec.yaml +++ b/app/pubspec.yaml @@ -30,13 +30,15 @@ environment: dependencies: flutter: sdk: flutter - - # The following adds the Cupertino Icons font to your application. - # Use with the CupertinoIcons class for iOS style icons. - cupertino_icons: ^1.0.8 + sqflite: ^2.4.2 path: ^1.9.1 dynamic_color: ^1.8.1 + flutter_local_notifications: ^20.0.0 + timezone: ^0.10.1 + flutter_timezone: ^5.0.1 + permission_handler: ^12.0.1 + alarm: ^5.2.1 dev_dependencies: flutter_test: diff --git a/app/windows/flutter/generated_plugin_registrant.cc b/app/windows/flutter/generated_plugin_registrant.cc index e4899a6..03882ab 100644 --- a/app/windows/flutter/generated_plugin_registrant.cc +++ b/app/windows/flutter/generated_plugin_registrant.cc @@ -7,8 +7,14 @@ #include "generated_plugin_registrant.h" #include +#include +#include void RegisterPlugins(flutter::PluginRegistry* registry) { DynamicColorPluginCApiRegisterWithRegistrar( registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); + FlutterTimezonePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FlutterTimezonePluginCApi")); + PermissionHandlerWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("PermissionHandlerWindowsPlugin")); } diff --git a/app/windows/flutter/generated_plugins.cmake b/app/windows/flutter/generated_plugins.cmake index 841e8c4..65dc5f8 100644 --- a/app/windows/flutter/generated_plugins.cmake +++ b/app/windows/flutter/generated_plugins.cmake @@ -4,9 +4,12 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + flutter_timezone + permission_handler_windows ) list(APPEND FLUTTER_FFI_PLUGIN_LIST + flutter_local_notifications_windows ) set(PLUGIN_BUNDLED_LIBRARIES)