// GCG.MeinCantor - Die Schulplattform für Cantorianer. // Copyright (C) 2021-2022 Georg-Cantor-Gymnasium Halle (Saale) // This program is free software: you can redistribute it and/or modify // it under the terms of the GNU Affero General Public License as published // by the Free Software Foundation, either version 3 of the License, or // (at your option) any later version. // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // GNU Affero General Public License for more details. // You should have received a copy of the GNU Affero General Public License // along with this program. If not, see . import 'dart:convert'; import 'dart:io'; import 'package:meincantor/cache_manager.dart'; import 'package:flutter_cache_manager/flutter_cache_manager.dart'; import 'package:http/http.dart' as http; import 'package:flutter/material.dart'; import 'package:intl/intl.dart'; import 'package:shared_preferences/shared_preferences.dart'; import 'package:meincantor/const.dart'; import 'package:meincantor/timetable.dart'; import 'package:meincantor/login.dart'; import 'package:meincantor/main.dart'; Future getArticles() async { var uri = Uri.https(szUrl["url"]! as String, "/api/articles"); final response = await http.get(uri, headers: szUrl["headers"]! as Map); return (response); } Future getNews() async { var uri = Uri.https(szUrl["url"]! as String, "/api/aktuelles"); final response = await http.get(uri, headers: szUrl["headers"]! as Map); return (response); } Future getToken( String user, String password, String otp, String devId) async { var uri = Uri.https("mein.cantorgymnasium.de", "/login"); String body = '{"user":"$user", "password": "$password", "otp": "$otp", "devid": "$devId"}'; final response = await http.post(uri, body: body); return (response); } Future getUserInfo( String user, String password, String otp, String devId) async { var uri = Uri.https("mein.cantorgymnasium.de", "/api/userinfo"); String body = '{"user":"$user", "password": "$password", "otp": "$otp", "devid": "$devId"}'; final response = await http.post(uri, body: body); if (response.statusCode == 200) { return utf8.decode(response.bodyBytes); } else { throw Exception('Failed to log in'); } } Future fetchClassTimetable(String ext, String? classNum) async { try { return (http.Response(await getCachedTimetable(ext, classNum), 200)); } on HttpExceptionWithStatus catch (e) { return http.Response(e.message, e.statusCode); } on HttpException catch (e) { return http.Response(e.message, 500); } on SocketException catch (e) { return http.Response(e.message, 404); } } fetchLessonList() async { SharedPreferences prefs = await SharedPreferences.getInstance(); String classNum; if (prefs.getString('class_num') != null) { classNum = prefs.getString('class_num')!.replaceAll("/", "_"); } else { classNum = '05_1'; } var apiKey = prefs.getString('api_key'); var uri = Uri.https("mein.cantorgymnasium.de", "/api/lessons/$classNum"); var headers = {"x-api-key": "$apiKey"}; final response = await http.get(uri, headers: headers).onError((error, stackTrace) { return (http.Response("", 404)); }); if (response.statusCode == 200) { prefs.setString("lessons", utf8.decode(response.bodyBytes)); } else { prefs.setString("lessons", jsonEncode([])); } } Widget buildTimetable(Future future, String info) { return FutureBuilder( future: future, builder: (context, snapshot) { if (snapshot.hasData) { int statusCode = snapshot.data!.statusCode; if (statusCode == 200) { Widget timetableView = ClassTimetableBuilder.buildView( jsonDecode(snapshot.data!.body), context) .view .child; return timetableView; } else if (statusCode == 400) { Navigator.push( context, MaterialPageRoute(builder: (context) => Login())); } else if (statusCode == 500) { var chars = Runes('Es konnte kein $info gefunden werden. \u{1F937}'); List cardChildren = []; cardChildren.add(ListTile( title: Text(String.fromCharCodes(chars), style: const TextStyle(color: Palette.primary)), )); Card card = Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), color: Colors.white, child: Column( children: cardChildren, )); return Padding( padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), child: ListView( children: [card], )); } else if (statusCode == 404) { var chars = Runes( 'Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internet-Verbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support. \u{1F4e1}'); List cardChildren = []; cardChildren.add(ListTile( title: Text(String.fromCharCodes(chars), style: const TextStyle(color: Palette.primary)), )); Card card = Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), color: Colors.white, child: Column( children: cardChildren, )); return Padding( padding: const EdgeInsets.fromLTRB(20, 20, 20, 20), child: ListView( children: [card], )); } return Center(child: Text('Error $statusCode')); } else if (snapshot.hasError) { return Text('$snapshot.error'); } else { return const Center(child: CircularProgressIndicator()); } }, ); } Widget buildTodayClassTimetableLesson(int count) { return FutureBuilder( future: fetchClassTimetable( "/${DateFormat("yyyyMMdd").format(DateTime.now())}", null), builder: (context, snapshot) { if (snapshot.hasData) { int statusCode = snapshot.data!.statusCode; if (statusCode == 200) { List lessons = LessonsListBuilder.buildList( jsonDecode(snapshot.data!.body), count: count) .lessons; if (lessons.isNotEmpty) { return Column(children: lessons); } else { var chars = Runes( 'Keine Stunden mehr gefunden. Sieht so aus als hättest du Schluss für heute \u{1F389}'); List cardChildren = []; cardChildren.add(ListTile( title: Text(String.fromCharCodes(chars), style: const TextStyle(color: Palette.primary)))); Card card = Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), color: Colors.white, child: Column( children: cardChildren, )); return Column(children: [card]); } } else if (statusCode == 400) { Future.delayed(Duration.zero, () { Navigator.push( context, MaterialPageRoute(builder: (context) => Login()), ); }); } else if (statusCode == 500) { var chars = Runes( 'Es konnte kein Vertretungsplan für heute gefunden werden. \u{1F937}'); List cardChildren = []; cardChildren.add(ListTile( title: Text(String.fromCharCodes(chars), style: const TextStyle(color: Palette.primary)))); Card card = Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), color: Colors.white, child: Column( children: cardChildren, )); return card; } else if (statusCode == 404) { var chars = Runes( 'Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internet-Verbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support. \u{1F4e1}'); List cardChildren = []; cardChildren.add(ListTile( title: Text(String.fromCharCodes(chars), style: const TextStyle(color: Palette.primary)), )); Card card = Card( shape: RoundedRectangleBorder( borderRadius: BorderRadius.circular(15.0), ), color: Colors.white, child: Column( children: cardChildren, )); return Column( children: [card], ); } return Center(child: Text('Error $statusCode')); } else if (snapshot.hasError) { return Text('$snapshot.error'); } else { return const Center(child: CircularProgressIndicator()); } }, ); } Future fetchClassesList() async { SharedPreferences prefs = await SharedPreferences.getInstance(); var apiKey = prefs.getString('api_key'); var uri = Uri.https("mein.cantorgymnasium.de", "/api/classes"); var headers = {"x-api-key": "$apiKey"}; final response = http.get(uri, headers: headers).onError((error, stackTrace) { return (http.Response("", 404)); }); return response; } Widget buildClassesChooser() { return FutureBuilder( future: fetchClassesList(), builder: (context, snapshot) { if (snapshot.hasData) { int statusCode = snapshot.data!.statusCode; if (statusCode == 200) { List items = []; jsonDecode(utf8.decode(snapshot.data!.bodyBytes)).forEach((value) { items.add(value.toString()); }); return ClassesChooser(items: items); } else if (statusCode == 400) { Navigator.push( context, MaterialPageRoute(builder: (context) => Login())); } else if (statusCode == 500) { return const Padding( padding: EdgeInsets.fromLTRB(10, 10, 10, 10), child: Center( child: Text( "Serverfehler. Bitte wende dich an den MeinCantor-Support.")), ); } else if (statusCode == 404) { return const Padding( padding: EdgeInsets.fromLTRB(10, 10, 10, 10), child: Center( child: Text( "Keine Verbindung mit dem MeinCantor-Server möglich. Bitte prüfe deine Internetverbindung und deine DNS-Einstellungen oder wende dich an den MeinCantor-Support")), ); } return Text('$statusCode'); } else if (snapshot.hasError) { return Text('$snapshot.error'); } else { return const Center(child: CircularProgressIndicator()); } }, ); } class ClassesChooser extends StatefulWidget { final List items; const ClassesChooser({Key? key, required this.items}) : super(key: key); @override State createState() => _ClassesChooserState(items); } class _ClassesChooserState extends State { final List items; String? dropdownValue; _ClassesChooserState(this.items); @override void initState() { super.initState(); _read(); // read in initState } _read() async { SharedPreferences prefs = await SharedPreferences.getInstance(); setState(() { dropdownValue = prefs.getString("class_num"); // get the value }); } @override Widget build(BuildContext context) { return DropdownButtonFormField( value: dropdownValue, decoration: const InputDecoration( icon: Icon(Icons.people_outline), border: OutlineInputBorder(), labelText: 'Klasse (05/1, 07/3, 10/2...)', ), onChanged: (String? newValue) async { SharedPreferences prefs = await SharedPreferences.getInstance(); String classNum = newValue!; prefs.setString('class_num', classNum); prefs.remove("lessons"); prefs.remove("todayTImetable"); prefs.remove("tomorrowTimetable"); setState(() { dropdownValue = newValue; }); }, items: items.map>((String value) { return DropdownMenuItem( value: value, child: Text(value), ); }).toList(), ); } }