diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
new file mode 100644
index 0000000..abe86c1
--- /dev/null
+++ b/.devcontainer/devcontainer.json
@@ -0,0 +1,5 @@
+{
+ "name": "Flutter",
+ "image": "matspfeiffer/flutter:beta",
+ "extensions": ["dart-code.dart-code", "dart-code.flutter"]
+}
\ No newline at end of file
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7d31458..7e7e417 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,3 +1,6 @@
+# 1.3.0
+- Odstranit connectivity_plus
+- Přidat možnost oznámení s info o obědu v daný čas
# 1.2.0
- Přidat možnost zobrazení oznámení v případě neobjednaného jídla na příští týden
- Přidat oznámení o rozbitých uložených údajích
diff --git a/lib/lang/lang.dart b/lib/lang/lang.dart
index f73158d..6575dba 100644
--- a/lib/lang/lang.dart
+++ b/lib/lang/lang.dart
@@ -145,6 +145,10 @@ abstract class Languages {
String get checkOrdered;
+ String get notifyLunch;
+
+ String get notifyAt;
+
// Offline
String get offline;
diff --git a/lib/lang/lang_cz.dart b/lib/lang/lang_cz.dart
index fd77801..baffcba 100644
--- a/lib/lang/lang_cz.dart
+++ b/lib/lang/lang_cz.dart
@@ -207,4 +207,11 @@ class LanguageCz extends Languages {
@override
String get corrupted =>
"Nastal problém s dešifrováním uložených údajů, prosím zkuste vyčistit veškerá data této aplikace.";
+
+ @override
+ String get notifyAt => "Odeslat v";
+
+ @override
+ String get notifyLunch =>
+ "V určený čas odeslat notifikaci s informacemi o obědě";
}
diff --git a/lib/lang/lang_en.dart b/lib/lang/lang_en.dart
index 3b838db..9bd4183 100644
--- a/lib/lang/lang_en.dart
+++ b/lib/lang/lang_en.dart
@@ -206,4 +206,10 @@ class LanguageEn extends Languages {
@override
String get corrupted =>
"The saved credentials seem to be corrupted, please try clearing the application's data.";
+
+ @override
+ String get notifyAt => "Send notification at";
+
+ @override
+ String get notifyLunch => "Send a notification with meal info";
}
diff --git a/lib/main.dart b/lib/main.dart
index 2adb7f6..6b1c41c 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,18 +1,20 @@
import 'dart:convert';
import 'dart:io';
+import 'package:background_fetch/background_fetch.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
+import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/lang/lang_cz.dart';
import 'package:opencanteen/loginmanager.dart';
-import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:canteenlib/canteenlib.dart';
import 'package:opencanteen/okna/offline_jidelnicek.dart';
import 'package:opencanteen/okna/welcome.dart';
import 'package:opencanteen/util.dart';
import 'package:path_provider/path_provider.dart';
+import 'package:shared_preferences/shared_preferences.dart';
import 'lang/lang.dart';
import 'lang/lang_en.dart';
@@ -35,14 +37,75 @@ Copyright (C) 2022 Matyáš Caras a přispěvatelé
along with this program. If not, see .
*/
-void main() {
+final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
+ FlutterLocalNotificationsPlugin();
+
+// Pouze pro Android
+void backgroundFetchHeadlessTask(HeadlessTask task) async {
+ String taskId = task.taskId;
+ bool isTimeout = task.timeout;
+ if (isTimeout) {
+ // Timeout
+ debugPrint("[BackgroundFetch] Headless task má time-out: $taskId");
+ BackgroundFetch.finish(taskId);
+ return;
+ }
+ var prefs = await SharedPreferences.getInstance();
+ debugPrint('[BackgroundFetch] Přišel headless event.');
+
+ if (prefs.getBool("oznamit") ?? false) {
+ // Oznámení před obědem
+ if (prefs.getBool("offline") ?? false) {
+ // TODO možnost brát z offline dat
+ } else {
+ // bere online
+ var d = await LoginManager.getDetails(); // získat údaje
+ if (d != null) {
+ // TODO
+ }
+ }
+ }
+ BackgroundFetch.finish(taskId);
+}
+
+void main() async {
+ WidgetsFlutterBinding.ensureInitialized();
+ const AndroidInitializationSettings initializationSettingsAndroid =
+ AndroidInitializationSettings('notif_icon');
+
+ /// Note: permissions aren't requested here just to demonstrate that can be
+ /// done later
+ final IOSInitializationSettings initializationSettingsIOS =
+ IOSInitializationSettings(
+ requestAlertPermission: false,
+ requestBadgePermission: false,
+ requestSoundPermission: false,
+ onDidReceiveLocalNotification: (
+ int id,
+ String? title,
+ String? body,
+ String? payload,
+ ) async {
+ debugPrint(body);
+ });
+
+ final InitializationSettings initializationSettings = InitializationSettings(
+ android: initializationSettingsAndroid,
+ iOS: initializationSettingsIOS,
+ );
+ await flutterLocalNotificationsPlugin.initialize(initializationSettings,
+ onSelectNotification: (String? payload) async {
+ if (payload != null) {
+ debugPrint('notification payload: $payload');
+ }
+ });
runApp(const MyApp());
+ BackgroundFetch.registerHeadlessTask(backgroundFetchHeadlessTask);
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
- // This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
@@ -78,22 +141,49 @@ class _LoginPageState extends State {
TextEditingController canteenControl = TextEditingController();
bool rememberMe = false;
+ void nastavitPozadi() async {
+ // Configure BackgroundFetch.
+ int status = await BackgroundFetch.configure(
+ BackgroundFetchConfig(
+ minimumFetchInterval: 15,
+ stopOnTerminate: false,
+ enableHeadless: true,
+ requiresBatteryNotLow: false,
+ requiresCharging: false,
+ requiresStorageNotLow: false,
+ requiresDeviceIdle: false,
+ requiredNetworkType: NetworkType.ANY), (String taskId) async {
+ // Callback
+ debugPrint("[BackgroundFetch] Event získán $taskId");
+ var d = await LoginManager.getDetails();
+ if (d != null) {
+ // TODO
+ }
+ BackgroundFetch.finish(taskId);
+ }, (String taskId) async {
+ debugPrint("[BackgroundFetch] TASK TIMEOUT taskId: $taskId");
+ BackgroundFetch.finish(taskId);
+ });
+ debugPrint('[BackgroundFetch] úspěšně nakonfigurováno: $status');
+ }
+
@override
void initState() {
super.initState();
LoginManager.getDetails().then((r) async {
- var connectivityResult = await (Connectivity().checkConnectivity());
- if (connectivityResult == ConnectivityResult.none) {
- ScaffoldMessenger.of(context).hideCurrentSnackBar();
- ScaffoldMessenger.of(context).showSnackBar(
- SnackBar(
- content: Text(Languages.of(context)!.errorContacting),
- ),
- );
- goOffline();
+ if (Platform.isIOS) {
+ // žádat o oprávnění na iOS
+ await flutterLocalNotificationsPlugin
+ .resolvePlatformSpecificImplementation<
+ IOSFlutterLocalNotificationsPlugin>()
+ ?.requestPermissions(
+ alert: true,
+ badge: true,
+ sound: true,
+ );
}
-
if (r != null) {
+ // Automaticky přihlásit
showDialog(
context: context,
barrierDismissible: false,
@@ -123,6 +213,7 @@ class _LoginPageState extends State {
}
const storage = FlutterSecureStorage();
var odsouhlasil = await storage.read(key: "oc_souhlas");
+ nastavitPozadi();
if (odsouhlasil == null || odsouhlasil != "ano") {
Navigator.pushReplacement(
context,
@@ -260,6 +351,7 @@ class _LoginPageState extends State {
LoginManager.setDetails(userControl.text,
passControl.text, canteenControl.text);
}
+ nastavitPozadi();
// souhlas
const storage = FlutterSecureStorage();
var odsouhlasil =
diff --git a/lib/okna/nastaveni.dart b/lib/okna/nastaveni.dart
index 8b43e2d..d65ac6a 100644
--- a/lib/okna/nastaveni.dart
+++ b/lib/okna/nastaveni.dart
@@ -17,6 +17,8 @@ class _NastaveniState extends State {
bool _ukladatOffline = false;
bool _preskakovatVikend = false;
bool _kontrolovatTyden = false;
+ bool _oznameniObed = false;
+ String? _oznameniCas;
void najitNastaveni() async {
var preferences = await SharedPreferences.getInstance();
@@ -24,6 +26,8 @@ class _NastaveniState extends State {
_ukladatOffline = preferences.getBool("offline") ?? false;
_preskakovatVikend = preferences.getBool("skip") ?? false;
_kontrolovatTyden = preferences.getBool("tyden") ?? false;
+ _oznameniObed = preferences.getBool("oznamit") ?? false;
+ _oznameniCas = preferences.getString("oznameni_cas");
});
}
@@ -91,6 +95,26 @@ class _NastaveniState extends State {
});
})
],
+ ),
+ Row(
+ mainAxisAlignment: MainAxisAlignment.spaceBetween,
+ children: [
+ Text(Languages.of(context)!.skipWeekend),
+ Switch(
+ value: _oznameniObed,
+ onChanged: (value) {
+ setState(() {
+ _oznameniObed = value;
+ zmenitNastaveni("oznamit", value);
+ });
+ })
+ ],
+ ),
+ Text(Languages.of(context)!.notifyAt),
+ TimePickerDialog(
+ initialTime: (_oznameniCas == null)
+ ? TimeOfDay.now()
+ : TimeOfDay.fromDateTime(DateTime.parse(_oznameniCas!)),
)
],
),
diff --git a/pubspec.lock b/pubspec.lock
index da74db2..6d92ce2 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -22,6 +22,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "2.8.2"
+ background_fetch:
+ dependency: "direct main"
+ description:
+ name: background_fetch
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "1.1.0"
canteenlib:
dependency: "direct main"
description:
@@ -57,48 +64,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.16.0"
- connectivity_plus:
- dependency: "direct main"
- description:
- name: connectivity_plus
- url: "https://pub.dartlang.org"
- source: hosted
- version: "2.2.1"
- connectivity_plus_linux:
- dependency: transitive
- description:
- name: connectivity_plus_linux
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.3.0"
- connectivity_plus_macos:
- dependency: transitive
- description:
- name: connectivity_plus_macos
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.2.1"
- connectivity_plus_platform_interface:
- dependency: transitive
- description:
- name: connectivity_plus_platform_interface
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.2.0"
- connectivity_plus_web:
- dependency: transitive
- description:
- name: connectivity_plus_web
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.2.0"
- connectivity_plus_windows:
- dependency: transitive
- description:
- name: connectivity_plus_windows
- url: "https://pub.dartlang.org"
- source: hosted
- version: "1.2.0"
crypto:
dependency: transitive
description:
@@ -153,6 +118,27 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.0.4"
+ flutter_local_notifications:
+ dependency: "direct main"
+ description:
+ name: flutter_local_notifications
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "9.5.3+1"
+ flutter_local_notifications_linux:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_linux
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.4.2"
+ flutter_local_notifications_platform_interface:
+ dependency: transitive
+ description:
+ name: flutter_local_notifications_platform_interface
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "5.0.0"
flutter_localizations:
dependency: "direct main"
description: flutter
@@ -268,13 +254,6 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.7.0"
- nm:
- dependency: transitive
- description:
- name: nm
- url: "https://pub.dartlang.org"
- source: hosted
- version: "0.5.0"
path:
dependency: transitive
description:
@@ -441,6 +420,13 @@ packages:
url: "https://pub.dartlang.org"
source: hosted
version: "1.2.0"
+ timezone:
+ dependency: transitive
+ description:
+ name: timezone
+ url: "https://pub.dartlang.org"
+ source: hosted
+ version: "0.8.0"
typed_data:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index fde98a3..a5fbbf7 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -17,12 +17,13 @@ dependencies:
flutter_localizations:
sdk: flutter
canteenlib: ^0.1.0-alpha.16
- connectivity_plus: ^2.2.1
flutter_secure_storage: ^5.0.2
url_launcher: ^6.0.20
path_provider: ^2.0.9
shared_preferences: ^2.0.13
introduction_screen: ^3.0.1
+ flutter_local_notifications: ^9.5.3+1
+ background_fetch: ^1.1.0
dev_dependencies:
flutter_lints: ^1.0.0