feat: oznámení před jídlem + tlačítko pro dnešek

PR #17
This commit is contained in:
Matyáš Caras 2022-06-08 19:12:00 +02:00 committed by GitHub
commit 6bbc9c2d0a
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
18 changed files with 416 additions and 110 deletions

View file

@ -0,0 +1,5 @@
{
"name": "Flutter",
"image": "matspfeiffer/flutter:beta",
"extensions": ["dart-code.dart-code", "dart-code.flutter"]
}

View file

@ -1,3 +1,7 @@
# 1.3.0
- Odstranit connectivity_plus
- Přidat možnost oznámení s info o obědu v daný čas
- Přidat k jídelníčku tlačítko, které zobrazí dnešní jídelníček
# 1.2.0 # 1.2.0
- Přidat možnost zobrazení oznámení v případě neobjednaného jídla na příští týden - 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 - Přidat oznámení o rozbitých uložených údajích

View file

@ -47,12 +47,12 @@ android {
} }
defaultConfig { defaultConfig {
// TODO: Specify your own unique Application ID (https://developer.android.com/studio/build/application-id.html).
applicationId "cz.hernikplays.opencanteen" applicationId "cz.hernikplays.opencanteen"
minSdkVersion 18 minSdkVersion 18
targetSdkVersion flutter.targetSdkVersion targetSdkVersion flutter.targetSdkVersion
versionCode flutterVersionCode.toInteger() versionCode flutterVersionCode.toInteger()
versionName flutterVersionName versionName flutterVersionName
multiDexEnabled true
} }
signingConfigs { signingConfigs {
@ -76,4 +76,6 @@ flutter {
dependencies { dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version"
implementation 'com.android.support:multidex:1.0.3'
implementation 'androidx.appcompat:appcompat:1.3.1'
} }

View file

@ -15,7 +15,8 @@
android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode" android:configChanges="orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode"
android:hardwareAccelerated="true" android:hardwareAccelerated="true"
android:windowSoftInputMode="adjustResize" android:windowSoftInputMode="adjustResize"
android:screenOrientation="portrait"> android:screenOrientation="portrait"
android:showWhenLocked="true">
<!-- Specifies an Android theme to apply to this Activity as soon as <!-- Specifies an Android theme to apply to this Activity as soon as
the Android process has started. This theme is visible to the user the Android process has started. This theme is visible to the user
while the Flutter UI initializes. After that, this theme continues while the Flutter UI initializes. After that, this theme continues

View file

@ -0,0 +1,10 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="48dp"
android:height="48dp"
android:viewportWidth="48"
android:viewportHeight="48"
android:tint="?attr/colorControlNormal">
<path
android:fillColor="@android:color/white"
android:pathData="M8.25,42 L6.15,39.9 27.15,18.9Q26.05,16.5 26.65,13.925Q27.25,11.35 29.5,9.1Q31.75,6.9 34.95,6.275Q38.15,5.65 40.2,7.7Q42.3,9.8 41.625,12.95Q40.95,16.1 38.6,18.5Q36.55,20.6 34.025,21.25Q31.5,21.9 29.3,20.95L25.95,24.3L41.55,39.9L39.45,42L23.85,26.4ZM14.5,24.45 L8.55,18.5Q6,15.95 5.9,12.45Q5.8,8.95 8.2,6.25L20.45,18.5Z"/>
</vector>

View file

@ -145,8 +145,19 @@ abstract class Languages {
String get checkOrdered; String get checkOrdered;
String get notifyLunch;
String get notifyAt;
// Offline // Offline
String get offline; String get offline;
String get mustLogout; String get mustLogout;
// Oznámit před obědem
String get lunchNotif;
String get error;
String get needRemember;
} }

View file

@ -207,4 +207,21 @@ class LanguageCz extends Languages {
@override @override
String get corrupted => 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."; "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ě";
@override
String get lunchNotif => "Dnes máte objednáno";
@override
String get error => "Chyba";
@override
String get needRemember =>
"Musíte své přihlašovací údaje uložit na přihlašovací obrazovce";
} }

View file

@ -206,4 +206,20 @@ class LanguageEn extends Languages {
@override @override
String get corrupted => String get corrupted =>
"The saved credentials seem to be corrupted, please try clearing the application's data."; "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";
@override
String get lunchNotif => "Today's ordered meal";
@override
String get error => "Error";
@override
String get needRemember =>
"You need to save your login details on the login screen first";
} }

View file

@ -3,16 +3,21 @@ import 'dart:io';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter/services.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_localizations/flutter_localizations.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/lang/lang_cz.dart'; import 'package:opencanteen/lang/lang_cz.dart';
import 'package:opencanteen/loginmanager.dart'; import 'package:opencanteen/loginmanager.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
import 'package:canteenlib/canteenlib.dart'; import 'package:canteenlib/canteenlib.dart';
import 'package:opencanteen/okna/offline_jidelnicek.dart'; import 'package:opencanteen/okna/offline_jidelnicek.dart';
import 'package:opencanteen/okna/welcome.dart'; import 'package:opencanteen/okna/welcome.dart';
import 'package:opencanteen/util.dart'; import 'package:opencanteen/util.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'package:intl/intl.dart';
import 'package:timezone/data/latest_all.dart' as tz;
import 'package:timezone/timezone.dart' as tz;
import 'lang/lang.dart'; import 'lang/lang.dart';
import 'lang/lang_en.dart'; import 'lang/lang_en.dart';
@ -35,14 +40,102 @@ Copyright (C) 2022 Matyáš Caras a přispěvatelé
along with this program. If not, see <http://www.gnu.org/licenses/>. along with this program. If not, see <http://www.gnu.org/licenses/>.
*/ */
void main() { final FlutterLocalNotificationsPlugin flutterLocalNotificationsPlugin =
FlutterLocalNotificationsPlugin();
void oznamitPredem(SharedPreferences prefs, tz.Location l) async {
String title;
String locale = Intl.getCurrentLocale();
switch (locale) {
case "cs_CZ":
title = LanguageCz().lunchNotif;
break;
default:
title = LanguageEn().lunchNotif;
}
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) {
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere((element) => element.objednano);
var kdy = DateTime.parse(prefs.getString("oznameni_cas")!);
const AndroidNotificationDetails androidSpec =
AndroidNotificationDetails('opencanteen', 'predobjedem',
channelDescription: 'Oznámení o dnešním jídle',
importance: Importance.max,
priority: Priority.high,
ticker: 'today meal');
const IOSNotificationDetails iOSpec =
IOSNotificationDetails(presentAlert: true, presentBadge: true);
await flutterLocalNotificationsPlugin.zonedSchedule(
0,
title,
"${jidlo.nazev} - ${jidlo.varianta}",
tz.TZDateTime.from(
casNaDate(
TimeOfDay(hour: kdy.hour, minute: kdy.minute),
),
l),
const NotificationDetails(android: androidSpec, iOS: iOSpec),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
}
}
}
}
}
void main() async {
WidgetsFlutterBinding.ensureInitialized();
tz.initializeTimeZones();
var l = tz.getLocation(await FlutterNativeTimezone.getLocalTimezone());
tz.setLocalLocation(l);
var prefs = await SharedPreferences.getInstance();
if (prefs.getBool("oznamit") ?? false) {
oznamitPredem(prefs, l);
}
const AndroidInitializationSettings initializationSettingsAndroid =
AndroidInitializationSettings('notif_icon');
final IOSInitializationSettings initializationSettingsIOS =
IOSInitializationSettings(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()); runApp(const MyApp());
} }
class MyApp extends StatelessWidget { class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key); const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return MaterialApp( return MaterialApp(
@ -82,18 +175,19 @@ class _LoginPageState extends State<LoginPage> {
void initState() { void initState() {
super.initState(); super.initState();
LoginManager.getDetails().then((r) async { LoginManager.getDetails().then((r) async {
var connectivityResult = await (Connectivity().checkConnectivity()); if (Platform.isIOS) {
if (connectivityResult == ConnectivityResult.none) { // žádat o oprávnění na iOS
ScaffoldMessenger.of(context).hideCurrentSnackBar(); await flutterLocalNotificationsPlugin
ScaffoldMessenger.of(context).showSnackBar( .resolvePlatformSpecificImplementation<
SnackBar( IOSFlutterLocalNotificationsPlugin>()
content: Text(Languages.of(context)!.errorContacting), ?.requestPermissions(
), alert: true,
); badge: true,
goOffline(); sound: true,
);
} }
if (r != null) { if (r != null) {
// Automaticky přihlásit
showDialog( showDialog(
context: context, context: context,
barrierDismissible: false, barrierDismissible: false,
@ -127,14 +221,14 @@ class _LoginPageState extends State<LoginPage> {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (c) => WelcomeScreen(canteen: canteen))); builder: (c) => WelcomeScreen(
canteen: canteen, n: flutterLocalNotificationsPlugin)));
} else { } else {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => JidelnicekPage( builder: (context) => JidelnicekPage(
canteen: canteen, canteen: canteen, n: flutterLocalNotificationsPlugin)),
)),
); );
} }
} on PlatformException { } on PlatformException {
@ -268,15 +362,16 @@ class _LoginPageState extends State<LoginPage> {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (c) => builder: (c) => WelcomeScreen(
WelcomeScreen(canteen: canteen))); canteen: canteen,
n: flutterLocalNotificationsPlugin)));
} else { } else {
Navigator.pushReplacement( Navigator.pushReplacement(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => JidelnicekPage( builder: (context) => JidelnicekPage(
canteen: canteen, canteen: canteen,
)), n: flutterLocalNotificationsPlugin)),
); );
} }
} on PlatformException { } on PlatformException {

View file

@ -43,11 +43,6 @@ class _AboutPageState extends State<AboutPage> {
"Copyright 2017 German Saprykin. All rights reserved, licence BSD 3-Clause", "Copyright 2017 German Saprykin. All rights reserved, licence BSD 3-Clause",
"https://github.com/mogol/flutter_secure_storage/blob/develop/flutter_secure_storage/LICENSE"), "https://github.com/mogol/flutter_secure_storage/blob/develop/flutter_secure_storage/LICENSE"),
const SizedBox(height: 10), const SizedBox(height: 10),
cudlik(
"connectivity_plus",
"Copyright 2017 The Chromium Authors. All rights reserved, licence BSD 3-Clause",
"https://github.com/fluttercommunity/plus_plugins/blob/main/packages/connectivity_plus/connectivity_plus/LICENSE"),
const SizedBox(height: 10),
cudlik( cudlik(
"url_launcher", "url_launcher",
"Copyright 2013 The Flutter Authors. All rights reserved, licence BSD 3-Clause", "Copyright 2013 The Flutter Authors. All rights reserved, licence BSD 3-Clause",
@ -60,18 +55,33 @@ class _AboutPageState extends State<AboutPage> {
const SizedBox(height: 10), const SizedBox(height: 10),
cudlik( cudlik(
"path_provider", "path_provider",
"Copyright 2013 The Flutter Authors. All rights reserved., licence BSD-3-Clause", "Copyright 2013 The Flutter Authors. All rights reserved, licence BSD-3-Clause",
"https://github.com/flutter/plugins/blob/main/packages/path_provider/path_provider/LICENSE"), "https://github.com/flutter/plugins/blob/main/packages/path_provider/path_provider/LICENSE"),
const SizedBox(height: 10), const SizedBox(height: 10),
cudlik( cudlik(
"shared_preferences", "shared_preferences",
"Copyright 2013 The Flutter Authors. All rights reserved., licence BSD-3-Clause", "Copyright 2013 The Flutter Authors. All rights reserved, licence BSD-3-Clause",
"https://github.com/flutter/plugins/blob/main/packages/path_provider/path_provider/LICENSE"), "https://github.com/flutter/plugins/blob/main/packages/path_provider/path_provider/LICENSE"),
const SizedBox(height: 10), const SizedBox(height: 10),
cudlik( cudlik(
"introduction_screen", "introduction_screen",
"Copyright 2019 Jean-Charles Moussé, licence MIT", "Copyright 2019 Jean-Charles Moussé, licence MIT",
"https://github.com/Pyozer/introduction_screen/blob/master/LICENSE") "https://github.com/Pyozer/introduction_screen/blob/master/LICENSE"),
const SizedBox(height: 10),
cudlik(
"flutter_local_notifications",
"Copyright 2018 Michael Bui. All rights reserved, licence BSD-3-Clause",
"https://github.com/MaikuB/flutter_local_notifications/blob/master/flutter_local_notifications/LICENSE"),
const SizedBox(height: 10),
cudlik(
"timezone",
"Copyright 2014, timezone project authors, licence BSD-2-Clause",
"https://github.com/srawlins/timezone/blob/master/LICENSE"),
const SizedBox(height: 10),
cudlik(
"flutter_native_timezone",
"Copyright 2019 pinkfish, licence Apache 2.0",
"https://github.com/pinkfish/flutter_native_timezone/blob/master/LICENSE"),
]), ]),
), ),
), ),

View file

@ -1,13 +1,16 @@
import 'package:canteenlib/canteenlib.dart'; import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:opencanteen/util.dart'; import 'package:opencanteen/util.dart';
import '../lang/lang.dart'; import '../lang/lang.dart';
import '../main.dart'; import '../main.dart';
class BurzaPage extends StatefulWidget { class BurzaPage extends StatefulWidget {
const BurzaPage({Key? key, required this.canteen}) : super(key: key); const BurzaPage({Key? key, required this.canteen, required this.n})
: super(key: key);
final Canteen canteen; final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override @override
State<BurzaPage> createState() => _BurzaPageState(); State<BurzaPage> createState() => _BurzaPageState();
} }
@ -112,7 +115,7 @@ class _BurzaPageState extends State<BurzaPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 3), drawer: drawerGenerator(context, widget.canteen, 3, widget.n),
appBar: AppBar( appBar: AppBar(
title: Text(Languages.of(context)!.exchange), title: Text(Languages.of(context)!.exchange),
), ),

View file

@ -3,6 +3,7 @@ import 'dart:io';
import 'package:canteenlib/canteenlib.dart'; import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/nastaveni.dart'; import 'package:opencanteen/okna/nastaveni.dart';
import 'package:opencanteen/util.dart'; import 'package:opencanteen/util.dart';
@ -15,8 +16,10 @@ import '../main.dart';
import 'about.dart'; import 'about.dart';
class JidelnicekPage extends StatefulWidget { class JidelnicekPage extends StatefulWidget {
const JidelnicekPage({Key? key, required this.canteen}) : super(key: key); const JidelnicekPage({Key? key, required this.canteen, required this.n})
: super(key: key);
final Canteen canteen; final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override @override
State<JidelnicekPage> createState() => _JidelnicekPageState(); State<JidelnicekPage> createState() => _JidelnicekPageState();
} }
@ -301,7 +304,8 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
}); });
} }
void kliknuti(String value, BuildContext context) { void kliknuti(
String value, BuildContext context, FlutterLocalNotificationsPlugin n) {
if (value == Languages.of(context)!.signOut) { if (value == Languages.of(context)!.signOut) {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
storage.deleteAll(); storage.deleteAll();
@ -314,7 +318,7 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
context, MaterialPageRoute(builder: (c) => const AboutPage())); context, MaterialPageRoute(builder: (c) => const AboutPage()));
} else if (value == Languages.of(context)!.settings) { } else if (value == Languages.of(context)!.settings) {
Navigator.push( Navigator.push(
context, MaterialPageRoute(builder: (c) => const Nastaveni())); context, MaterialPageRoute(builder: (c) => Nastaveni(n: n)));
} }
} }
@ -365,12 +369,12 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
@override @override
Widget build(BuildContext context) { Widget build(BuildContext context) {
return Scaffold( return Scaffold(
drawer: drawerGenerator(context, widget.canteen, 1), drawer: drawerGenerator(context, widget.canteen, 1, widget.n),
appBar: AppBar( appBar: AppBar(
title: Text(Languages.of(context)!.menu), title: Text(Languages.of(context)!.menu),
actions: [ actions: [
PopupMenuButton( PopupMenuButton(
onSelected: ((String value) => kliknuti(value, context)), onSelected: ((String value) => kliknuti(value, context, widget.n)),
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
return { return {
Languages.of(context)!.reportBugs, Languages.of(context)!.reportBugs,
@ -424,16 +428,23 @@ class _JidelnicekPageState extends State<JidelnicekPage> {
child: Text( child: Text(
"${den.day}. ${den.month}. ${den.year} - $denTydne")), "${den.day}. ${den.month}. ${den.year} - $denTydne")),
IconButton( IconButton(
onPressed: () { onPressed: () {
setState(() { setState(() {
den = den.add(const Duration(days: 1)); den = den.add(const Duration(days: 1));
if (den.weekday == 6 && _skipWeekend) { if (den.weekday == 6 && _skipWeekend) {
den = den.add(const Duration(days: 2)); den = den.add(const Duration(days: 2));
} }
nactiJidlo(); nactiJidlo();
}); });
}, },
icon: const Icon(Icons.arrow_right)), icon: const Icon(Icons.arrow_right),
),
IconButton(
onPressed: () => setState(() {
den = DateTime.now();
nactiJidlo();
}),
icon: const Icon(Icons.calendar_today))
]), ]),
SingleChildScrollView( SingleChildScrollView(
physics: const AlwaysScrollableScrollPhysics(), physics: const AlwaysScrollableScrollPhysics(),

View file

@ -1,13 +1,21 @@
import 'dart:io'; import 'dart:io';
import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_native_timezone/flutter_native_timezone.dart';
import 'package:path_provider/path_provider.dart'; import 'package:path_provider/path_provider.dart';
import 'package:shared_preferences/shared_preferences.dart'; import 'package:shared_preferences/shared_preferences.dart';
import 'package:timezone/timezone.dart' as tz;
import '../lang/lang.dart'; import '../lang/lang.dart';
import '../loginmanager.dart';
import '../util.dart';
class Nastaveni extends StatefulWidget { class Nastaveni extends StatefulWidget {
const Nastaveni({Key? key}) : super(key: key); const Nastaveni({Key? key, required this.n}) : super(key: key);
final FlutterLocalNotificationsPlugin n;
@override @override
State<Nastaveni> createState() => _NastaveniState(); State<Nastaveni> createState() => _NastaveniState();
@ -17,13 +25,26 @@ class _NastaveniState extends State<Nastaveni> {
bool _ukladatOffline = false; bool _ukladatOffline = false;
bool _preskakovatVikend = false; bool _preskakovatVikend = false;
bool _kontrolovatTyden = false; bool _kontrolovatTyden = false;
bool _oznameniObed = false;
bool _zapamatovany = false;
TimeOfDay _oznameniCas = TimeOfDay.now();
void najitNastaveni() async { void najitNastaveni() async {
var preferences = await SharedPreferences.getInstance(); var preferences = await SharedPreferences.getInstance();
_zapamatovany = await LoginManager.zapamatovat();
setState(() { setState(() {
_ukladatOffline = preferences.getBool("offline") ?? false; _ukladatOffline = preferences.getBool("offline") ?? false;
_preskakovatVikend = preferences.getBool("skip") ?? false; _preskakovatVikend = preferences.getBool("skip") ?? false;
_kontrolovatTyden = preferences.getBool("tyden") ?? false; _kontrolovatTyden = preferences.getBool("tyden") ?? false;
_oznameniObed = preferences.getBool("oznamit") ?? false;
var _casStr = preferences.getString("oznameni_cas");
if (_casStr == null) {
var now = DateTime.now();
_oznameniCas = TimeOfDay.fromDateTime(DateTime.now());
preferences.setString("oznameni_cas", now.toString());
} else {
_oznameniCas = TimeOfDay.fromDateTime(DateTime.parse(_casStr));
}
}); });
} }
@ -91,7 +112,73 @@ class _NastaveniState extends State<Nastaveni> {
}); });
}) })
], ],
) ),
Row(
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Flexible(child: Text(Languages.of(context)!.notifyLunch)),
Switch(
value: _oznameniObed,
thumbColor: (!_zapamatovany
? MaterialStateProperty.all(Colors.grey)
: null),
onChanged: (value) {
if (!_zapamatovany) {
showDialog(
context: context,
builder: (bc) => AlertDialog(
title: Text(Languages.of(context)!.error),
content:
Text(Languages.of(context)!.needRemember),
actions: [
TextButton(
child: Text(Languages.of(context)!.ok),
onPressed: () {
Navigator.of(context).pop();
},
)
],
));
} else {
setState(() {
_oznameniObed = value;
if (_oznameniObed) {
vytvoritOznameni(casNaDate(_oznameniCas));
}
zmenitNastaveni("oznamit", value);
});
}
})
],
),
Text(Languages.of(context)!.notifyAt),
TextButton(
onPressed: () async {
if (_oznameniObed) {
var cas = await showTimePicker(
context: context, initialTime: _oznameniCas);
if (cas != null) {
var prefs = await SharedPreferences.getInstance();
prefs.setString("oznameni_cas",
casNaDate(cas).toString()); // aktualizovat vybraný čas
var den = casNaDate(cas);
debugPrint(den.isAfter(DateTime.now()).toString());
if (den.isAfter(DateTime.now())) {
// znovu vytvořit oznámení POUZE když je čas v budoucnosti
vytvoritOznameni(den);
}
}
setState(() {
_oznameniCas = cas ?? _oznameniCas;
});
}
},
child: Text(
"${(_oznameniCas.hour < 10 ? "0" : "") + _oznameniCas.hour.toString()}:${(_oznameniCas.minute < 10 ? "0" : "") + _oznameniCas.minute.toString()}",
style: TextStyle(
color: (!_oznameniObed) ? Colors.grey : Colors.purple),
),
),
], ],
), ),
)), )),
@ -109,4 +196,42 @@ class _NastaveniState extends State<Nastaveni> {
} }
} }
} }
void vytvoritOznameni(DateTime den) async {
await widget.n.cancelAll();
var d = await LoginManager.getDetails(); // získat údaje
if (d != null) {
// Nové oznámení
var c = Canteen(d["url"]!);
if (await c.login(d["user"]!, d["pass"]!)) {
var jidla = await c.jidelnicekDen();
try {
var jidlo = jidla.jidla.singleWhere((element) => element.objednano);
const AndroidNotificationDetails androidSpec =
AndroidNotificationDetails('opencanteen', 'predobjedem',
channelDescription: 'Oznámení o dnešním jídle',
importance: Importance.max,
priority: Priority.high,
ticker: 'today meal');
const IOSNotificationDetails iOSpec =
IOSNotificationDetails(presentAlert: true, presentBadge: true);
var l =
tz.getLocation(await FlutterNativeTimezone.getLocalTimezone());
await widget.n.zonedSchedule(
// Vytvoří nové oznámení pro daný čas a datum
0,
Languages.of(context)!.lunchNotif,
"${jidlo.nazev} - ${jidlo.varianta}",
tz.TZDateTime.from(den, l),
const NotificationDetails(android: androidSpec, iOS: iOSpec),
androidAllowWhileIdle: true,
uiLocalNotificationDateInterpretation:
UILocalNotificationDateInterpretation.absoluteTime);
} on StateError catch (_) {
// nenalezeno
}
}
}
}
} }

View file

@ -1,6 +1,5 @@
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:opencanteen/okna/nastaveni.dart';
import 'package:opencanteen/util.dart'; import 'package:opencanteen/util.dart';
import 'package:url_launcher/url_launcher.dart'; import 'package:url_launcher/url_launcher.dart';
@ -87,9 +86,6 @@ class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
} else if (value == Languages.of(context)!.about) { } else if (value == Languages.of(context)!.about) {
Navigator.push( Navigator.push(
context, MaterialPageRoute(builder: (c) => const AboutPage())); context, MaterialPageRoute(builder: (c) => const AboutPage()));
} else if (value == Languages.of(context)!.settings) {
Navigator.push(
context, MaterialPageRoute(builder: (c) => const Nastaveni()));
} }
} }
@ -111,7 +107,6 @@ class _OfflineJidelnicekState extends State<OfflineJidelnicek> {
itemBuilder: (BuildContext context) { itemBuilder: (BuildContext context) {
return { return {
Languages.of(context)!.reportBugs, Languages.of(context)!.reportBugs,
Languages.of(context)!.settings,
Languages.of(context)!.about, Languages.of(context)!.about,
Languages.of(context)!.signOut Languages.of(context)!.signOut
}.map((String choice) { }.map((String choice) {

View file

@ -1,14 +1,17 @@
import 'package:canteenlib/canteenlib.dart'; import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:flutter_secure_storage/flutter_secure_storage.dart'; import 'package:flutter_secure_storage/flutter_secure_storage.dart';
import 'package:introduction_screen/introduction_screen.dart'; import 'package:introduction_screen/introduction_screen.dart';
import 'package:opencanteen/lang/lang.dart'; import 'package:opencanteen/lang/lang.dart';
import 'package:opencanteen/okna/jidelnicek.dart'; import 'package:opencanteen/okna/jidelnicek.dart';
class WelcomeScreen extends StatefulWidget { class WelcomeScreen extends StatefulWidget {
const WelcomeScreen({Key? key, required this.canteen}) : super(key: key); const WelcomeScreen({Key? key, required this.canteen, required this.n})
: super(key: key);
final Canteen canteen; final Canteen canteen;
final FlutterLocalNotificationsPlugin n;
@override @override
State<WelcomeScreen> createState() => _WelcomeScreenState(); State<WelcomeScreen> createState() => _WelcomeScreenState();
@ -67,7 +70,8 @@ class _WelcomeScreenState extends State<WelcomeScreen> {
const storage = FlutterSecureStorage(); const storage = FlutterSecureStorage();
await storage.write(key: "oc_souhlas", value: "ano"); await storage.write(key: "oc_souhlas", value: "ano");
Navigator.of(context).pushReplacement(MaterialPageRoute( Navigator.of(context).pushReplacement(MaterialPageRoute(
builder: (c) => JidelnicekPage(canteen: widget.canteen))); builder: (c) =>
JidelnicekPage(canteen: widget.canteen, n: widget.n)));
}, },
), ),
); );

View file

@ -1,11 +1,13 @@
import 'package:canteenlib/canteenlib.dart'; import 'package:canteenlib/canteenlib.dart';
import 'package:flutter/material.dart'; import 'package:flutter/material.dart';
import 'package:flutter_local_notifications/flutter_local_notifications.dart';
import 'package:opencanteen/okna/burza.dart'; import 'package:opencanteen/okna/burza.dart';
import 'lang/lang.dart'; import 'lang/lang.dart';
import 'okna/jidelnicek.dart'; import 'okna/jidelnicek.dart';
Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) { Drawer drawerGenerator(BuildContext context, Canteen canteen, int p,
FlutterLocalNotificationsPlugin n) {
Drawer drawer = const Drawer(); Drawer drawer = const Drawer();
switch (p) { switch (p) {
case 1: case 1:
@ -28,7 +30,7 @@ Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
onTap: () => Navigator.push( onTap: () => Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (context) => BurzaPage(canteen: canteen), builder: (context) => BurzaPage(canteen: canteen, n: n),
), ),
), ),
), ),
@ -50,7 +52,7 @@ Drawer drawerGenerator(BuildContext context, Canteen canteen, int p) {
onTap: () => Navigator.push( onTap: () => Navigator.push(
context, context,
MaterialPageRoute( MaterialPageRoute(
builder: (c) => JidelnicekPage(canteen: canteen))), builder: (c) => JidelnicekPage(canteen: canteen, n: n))),
), ),
ListTile( ListTile(
leading: const Icon(Icons.store), leading: const Icon(Icons.store),
@ -81,3 +83,10 @@ class OfflineJidlo {
required this.naBurze, required this.naBurze,
required this.den}); required this.den});
} }
/// Vytvoří [DateTime] z [TimeOfDay]
DateTime casNaDate(TimeOfDay c) {
var now = DateTime.now();
return DateTime.parse(
"${now.year}-${(now.month < 10 ? "0" : "") + now.month.toString()}-${(now.day < 10 ? "0" : "") + now.day.toString()} ${(c.hour < 10 ? "0" : "") + c.hour.toString()}:${(c.minute < 10 ? "0" : "") + c.minute.toString()}:00");
}

View file

@ -57,48 +57,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.16.0" 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: crypto:
dependency: transitive dependency: transitive
description: description:
@ -153,11 +111,39 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.0.4" 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: flutter_localizations:
dependency: "direct main" dependency: "direct main"
description: flutter description: flutter
source: sdk source: sdk
version: "0.0.0" version: "0.0.0"
flutter_native_timezone:
dependency: "direct main"
description:
name: flutter_native_timezone
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.0"
flutter_secure_storage: flutter_secure_storage:
dependency: "direct main" dependency: "direct main"
description: description:
@ -268,13 +254,6 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.7.0" version: "1.7.0"
nm:
dependency: transitive
description:
name: nm
url: "https://pub.dartlang.org"
source: hosted
version: "0.5.0"
path: path:
dependency: transitive dependency: transitive
description: description:
@ -441,6 +420,13 @@ packages:
url: "https://pub.dartlang.org" url: "https://pub.dartlang.org"
source: hosted source: hosted
version: "1.2.0" version: "1.2.0"
timezone:
dependency: "direct main"
description:
name: timezone
url: "https://pub.dartlang.org"
source: hosted
version: "0.8.0"
typed_data: typed_data:
dependency: transitive dependency: transitive
description: description:

View file

@ -6,7 +6,7 @@ publish_to: 'none'
# The following defines the version and build number for your application. # The following defines the version and build number for your application.
# A version number is three numbers separated by dots, like 1.2.43 # A version number is three numbers separated by dots, like 1.2.43
# followed by an optional build number separated by a +. # followed by an optional build number separated by a +.
version: 1.2.0+14 version: 1.3.0+15
environment: environment:
sdk: ">=2.16.1 <3.0.0" sdk: ">=2.16.1 <3.0.0"
@ -17,12 +17,14 @@ dependencies:
flutter_localizations: flutter_localizations:
sdk: flutter sdk: flutter
canteenlib: ^0.1.0-alpha.16 canteenlib: ^0.1.0-alpha.16
connectivity_plus: ^2.2.1
flutter_secure_storage: ^5.0.2 flutter_secure_storage: ^5.0.2
url_launcher: ^6.0.20 url_launcher: ^6.0.20
path_provider: ^2.0.9 path_provider: ^2.0.9
shared_preferences: ^2.0.13 shared_preferences: ^2.0.13
introduction_screen: ^3.0.1 introduction_screen: ^3.0.1
flutter_local_notifications: ^9.5.3+1
timezone: ^0.8.0
flutter_native_timezone: ^2.0.0
dev_dependencies: dev_dependencies:
flutter_lints: ^1.0.0 flutter_lints: ^1.0.0