diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml index 326245c..20c1cd6 100644 --- a/android/app/src/main/AndroidManifest.xml +++ b/android/app/src/main/AndroidManifest.xml @@ -31,6 +31,13 @@ + + + + + + + diff --git a/lib/api.dart b/lib/api.dart index 24b77db..7218377 100644 --- a/lib/api.dart +++ b/lib/api.dart @@ -262,7 +262,12 @@ class User { try { Response resp = await data.dio.get( "${data.baseUrl}/validate-token", - options: Options(headers: {"Authorization": "Bearer $token"}), + options: Options( + headers: {"Authorization": "Bearer $token"}, + validateStatus: (status) { + return status == 200 || status == 401; + }, + ), ); if (resp.data['success'] != true) { @@ -401,6 +406,10 @@ class TimeTable { return timetables[dateOnly]!; } + if (data.user.token == "") { + return TimeTableData(date, [], []); + } + Response response = await data.dio.get( "${data.baseUrl}/api/timetable?to=${DateFormat('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'en_US').format(DateTime(date.year, date.month, date.day))}&from=${DateFormat('yyyy-MM-dd\'T\'HH:mm:ss\'Z\'', 'en_US').format(DateTime(date.year, date.month, date.day))}", options: Options( diff --git a/lib/create_message.dart b/lib/create_message.dart index c1ce623..29b3a73 100644 --- a/lib/create_message.dart +++ b/lib/create_message.dart @@ -253,11 +253,11 @@ class SendMessageScreenState extends BaseState { leading: const Icon(Icons.drag_handle_rounded), trailing: ElevatedButton( style: ButtonStyle( - backgroundColor: MaterialStateProperty.all< + backgroundColor: WidgetStateProperty.all< Color>( const Color.fromARGB(255, 152, 1, 29)), foregroundColor: - MaterialStateProperty.all( + WidgetStateProperty.all( Colors.white), ), child: const Icon(Icons.delete_rounded), diff --git a/lib/grades.dart b/lib/grades.dart index 6938c55..1d14155 100644 --- a/lib/grades.dart +++ b/lib/grades.dart @@ -47,7 +47,7 @@ class GradesPageState extends BaseState { children: [messages], ) : Text(AppLocalizations.of(context)!.loading), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } diff --git a/lib/home.dart b/lib/home.dart index 140d10a..fae73fe 100644 --- a/lib/home.dart +++ b/lib/home.dart @@ -341,9 +341,9 @@ class HomePageState extends BaseState { height: 50, margin: const EdgeInsets.only(left: 20, right: 20, top: 10), decoration: BoxDecoration( - color: theme.colorScheme.surfaceVariant, + color: theme.colorScheme.surfaceContainerHighest, border: Border.all( - color: theme.colorScheme.background, + color: theme.colorScheme.surface, ), borderRadius: BorderRadiusDirectional.circular(25), ), @@ -617,7 +617,7 @@ class HomePageState extends BaseState { ), ], ), - backgroundColor: theme.colorScheme.background, + backgroundColor: theme.colorScheme.surface, drawer: Drawer( child: ListView( children: [ diff --git a/lib/homework.dart b/lib/homework.dart index 500697d..cd782b4 100644 --- a/lib/homework.dart +++ b/lib/homework.dart @@ -52,7 +52,7 @@ class HomeworkPageState extends BaseState { children: [messages], ) : Text(AppLocalizations.of(context)!.loading), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } diff --git a/lib/l10n/app_cs.arb b/lib/l10n/app_cs.arb index a97b098..3a60312 100644 --- a/lib/l10n/app_cs.arb +++ b/lib/l10n/app_cs.arb @@ -139,5 +139,7 @@ "createMessageNewPollOptionPlaceholder": "Nová možnost", "createMessageErrorSelectRecipient": "Vyberte prosím příjemce", "createMessageErrorNoMessage": "Napište prosím zprávu", - "createMessageSend": "Odeslat" + "createMessageSend": "Odeslat", + "qrLoginPleaseLogin": "EduPage2 QR Přihlášení", + "qrLoginUseExistingCredentials": "Chystáte se přihlásit pomocí QR kódu" } diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 46ad383..3e91a08 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -71,5 +71,7 @@ "createMessageNewPollOptionPlaceholder": "New option", "createMessageErrorSelectRecipient": "Please select a recipient", "createMessageErrorNoMessage": "Please write a message", - "createMessageSend": "Send" + "createMessageSend": "Send", + "qrLoginPleaseLogin": "EduPage2 QR Login", + "qrLoginUseExistingCredentials": "You are about to login to EduPage2 using a QR code" } diff --git a/lib/login.dart b/lib/login.dart index 28df784..6cc8953 100644 --- a/lib/login.dart +++ b/lib/login.dart @@ -147,7 +147,7 @@ class LoinPageState extends BaseState { Navigator.pop(context), }, style: ButtonStyle( - elevation: MaterialStateProperty.all(3), + elevation: WidgetStateProperty.all(3), ), child: Text(local!.loginLogin), ), diff --git a/lib/main.dart b/lib/main.dart index e4185fc..f44eeea 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,9 +1,13 @@ +import 'dart:async'; + +import 'package:app_links/app_links.dart'; import 'package:dynamic_color/dynamic_color.dart'; import 'package:eduapge2/api.dart'; import 'package:eduapge2/homework.dart'; import 'package:eduapge2/icanteen.dart'; import 'package:eduapge2/load.dart'; import 'package:eduapge2/messages.dart'; +import 'package:eduapge2/qrlogin.dart'; import 'package:eduapge2/timetable.dart'; import 'package:firebase_analytics/firebase_analytics.dart'; import 'package:firebase_remote_config/firebase_remote_config.dart'; @@ -53,6 +57,8 @@ abstract class BaseState extends State { } } +final GlobalKey navigatorKey = GlobalKey(); + class MyApp extends StatelessWidget { const MyApp({super.key}); @@ -78,6 +84,7 @@ class MyApp extends StatelessWidget { return DynamicColorBuilder(builder: (lightColorScheme, darkColorScheme) { return MaterialApp( title: 'EduPage2', + navigatorKey: navigatorKey, localizationsDelegates: AppLocalizations.localizationsDelegates, supportedLocales: AppLocalizations.supportedLocales, navigatorObservers: [SentryNavigatorObserver(), observer], @@ -107,6 +114,9 @@ class PageBaseState extends BaseState { int _selectedIndex = 0; String baseUrl = FirebaseRemoteConfig.instance.getString("testUrl"); + late AppLinks _appLinks; + StreamSubscription? _linkSubscription; + bool loaded = false; bool error = false; //for error status @@ -125,6 +135,28 @@ class PageBaseState extends BaseState { setOptimalDisplayMode(); if (!_isCheckingForUpdate) _checkForUpdate(); // ik that it's not necessary super.initState(); + initDeepLinks(); + } + + @override + void dispose() { + _linkSubscription?.cancel(); + super.dispose(); + } + + Future initDeepLinks() async { + _appLinks = AppLinks(); + + _linkSubscription = _appLinks.uriLinkStream.listen((uri) { + if (uri.path.startsWith('/l/')) { + final code = uri.pathSegments[1]; + navigatorKey.currentState?.push(MaterialPageRoute( + builder: (context) => QRLoginPage( + code: code, + ), + )); + } + }); } Future setOptimalDisplayMode() async { diff --git a/lib/message.dart b/lib/message.dart index f9b72b5..9c36edd 100644 --- a/lib/message.dart +++ b/lib/message.dart @@ -413,7 +413,7 @@ class MessagePageState extends BaseState { children: [messages], ) : Text(AppLocalizations.of(context)!.loading), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } } diff --git a/lib/messages.dart b/lib/messages.dart index 6905440..278f1ee 100644 --- a/lib/messages.dart +++ b/lib/messages.dart @@ -89,7 +89,7 @@ class TimeTablePageState extends BaseState { children: [messages], ) : Text(AppLocalizations.of(context)!.loading), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, floatingActionButton: FloatingActionButton( onPressed: () { Navigator.push( diff --git a/lib/qrlogin.dart b/lib/qrlogin.dart new file mode 100644 index 0000000..f265f21 --- /dev/null +++ b/lib/qrlogin.dart @@ -0,0 +1,178 @@ +import 'package:dio/dio.dart'; +import 'package:eduapge2/main.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter/services.dart'; +import 'package:shared_preferences/shared_preferences.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart'; + +class QRLoginPage extends StatefulWidget { + final String code; + const QRLoginPage({super.key, required this.code}); + + @override + BaseState createState() => QRLoinPageState(); +} + +class QRLoinPageState extends BaseState { + AppLocalizations? local; + late SharedPreferences sharedPreferences; + bool _useCustomEndpoint = false; + bool showPassword = false; + + TextEditingController emailController = TextEditingController(); + TextEditingController passwordController = TextEditingController(); + TextEditingController serverController = TextEditingController(); + TextEditingController customEndpointController = TextEditingController(); + + @override + void initState() { + getPrefs(); + super.initState(); + } + + @override + void setState(VoidCallback fn) { + if (!mounted) return; + super.setState(fn); + } + + Future getPrefs() async { + sharedPreferences = await SharedPreferences.getInstance(); + String? sEmail = sharedPreferences.getString("email"); + String? sPassword = sharedPreferences.getString("password"); + String? sServer = sharedPreferences.getString("server"); + String? sEndpoint = sharedPreferences.getString("customEndpoint"); + + if (sEmail != null) { + emailController.text = sEmail; + } + if (sPassword != null) { + passwordController.text = sPassword; + } + if (sServer != null) { + serverController.text = sServer; + } + if (sEndpoint != null && sEndpoint != "") { + customEndpointController.text = sEndpoint; + _useCustomEndpoint = true; + } + setState(() {}); + } + + @override + Widget build(BuildContext context) { + local ??= AppLocalizations.of(context); + return Center( + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Card( + child: Padding( + padding: const EdgeInsets.all(15), + child: Column( + mainAxisSize: MainAxisSize.min, + children: [ + Text( + local!.qrLoginPleaseLogin, + style: const TextStyle( + fontSize: 18, + ), + ), + Text(local!.qrLoginUseExistingCredentials), + TextField( + controller: emailController, + decoration: InputDecoration( + icon: const Icon(Icons.email), + hintText: local!.loginUsername, + ), + keyboardType: TextInputType.emailAddress, + ), + TextField( + controller: passwordController, + decoration: InputDecoration( + icon: const Icon(Icons.key), + hintText: local!.loginPassword, + suffixIcon: IconButton( + icon: Icon(showPassword + ? Icons.visibility + : Icons.visibility_off), + onPressed: () { + setState(() { + showPassword = !showPassword; + }); + }, + ), + ), + obscureText: !showPassword, + keyboardType: TextInputType.visiblePassword, + ), + Row( + children: [ + Checkbox( + value: _useCustomEndpoint, + onChanged: (value) { + setState(() { + _useCustomEndpoint = value!; + }); + }, + ), + Text(local!.loginCustomEndpointCheckbox), + ], + ), + if (_useCustomEndpoint) + TextField( + controller: customEndpointController, + decoration: InputDecoration( + icon: const Icon(Icons.language), + hintText: local!.loginCustomEndpoint, + ), + ), + TextField( + controller: serverController, + decoration: InputDecoration( + icon: const Icon(Icons.cloud_queue), + hintText: local!.loginServer, + ), + keyboardType: TextInputType.url, + ), + const SizedBox(height: 10), + ElevatedButton( + onPressed: () async { + Dio dio = Dio(); + + var response = await dio.post( + 'https://ep2.vypal.me/qrlogin/${widget.code}', + options: Options( + headers: { + Headers.contentTypeHeader: + Headers.formUrlEncodedContentType, + }, + ), + data: { + 'username': emailController.text, + 'password': passwordController.text, + 'server': serverController.text, + 'endpoint': customEndpointController.text, + }, + ); + + if (response.statusCode == 200) { + SystemNavigator.pop(); + } else { + throw Exception('Failed to load data'); + } + }, + style: ButtonStyle( + elevation: WidgetStateProperty.all(3), + ), + child: Text(local!.loginLogin), + ), + ], + ), + ), + ), + ], + ), + ); + } +} diff --git a/lib/timetable.dart b/lib/timetable.dart index d834ff7..1fadd9a 100644 --- a/lib/timetable.dart +++ b/lib/timetable.dart @@ -112,7 +112,7 @@ class TimeTablePageState extends BaseState { context); }, ), - backgroundColor: Theme.of(context).colorScheme.background, + backgroundColor: Theme.of(context).colorScheme.surface, ); } } diff --git a/linux/flutter/generated_plugin_registrant.cc b/linux/flutter/generated_plugin_registrant.cc index 2528735..766ab16 100644 --- a/linux/flutter/generated_plugin_registrant.cc +++ b/linux/flutter/generated_plugin_registrant.cc @@ -7,17 +7,21 @@ #include "generated_plugin_registrant.h" #include +#include #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) sentry_flutter_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); - sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); - g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = - fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); - url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); + 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) gtk_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "GtkPlugin"); + gtk_plugin_register_with_registrar(gtk_registrar); + g_autoptr(FlPluginRegistrar) sentry_flutter_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "SentryFlutterPlugin"); + sentry_flutter_plugin_register_with_registrar(sentry_flutter_registrar); + g_autoptr(FlPluginRegistrar) url_launcher_linux_registrar = + fl_plugin_registry_get_registrar_for_plugin(registry, "UrlLauncherPlugin"); + url_launcher_plugin_register_with_registrar(url_launcher_linux_registrar); } diff --git a/linux/flutter/generated_plugins.cmake b/linux/flutter/generated_plugins.cmake index b942fff..a98a528 100644 --- a/linux/flutter/generated_plugins.cmake +++ b/linux/flutter/generated_plugins.cmake @@ -4,6 +4,7 @@ list(APPEND FLUTTER_PLUGIN_LIST dynamic_color + gtk sentry_flutter url_launcher_linux ) diff --git a/macos/Flutter/GeneratedPluginRegistrant.swift b/macos/Flutter/GeneratedPluginRegistrant.swift index 4216f5d..459ff28 100644 --- a/macos/Flutter/GeneratedPluginRegistrant.swift +++ b/macos/Flutter/GeneratedPluginRegistrant.swift @@ -5,6 +5,7 @@ import FlutterMacOS import Foundation +import app_links import connectivity_plus import dynamic_color import firebase_analytics @@ -18,6 +19,7 @@ import sqflite import url_launcher_macos func RegisterGeneratedPlugins(registry: FlutterPluginRegistry) { + AppLinksMacosPlugin.register(with: registry.registrar(forPlugin: "AppLinksMacosPlugin")) ConnectivityPlusPlugin.register(with: registry.registrar(forPlugin: "ConnectivityPlusPlugin")) DynamicColorPlugin.register(with: registry.registrar(forPlugin: "DynamicColorPlugin")) FLTFirebaseAnalyticsPlugin.register(with: registry.registrar(forPlugin: "FLTFirebaseAnalyticsPlugin")) diff --git a/pubspec.lock b/pubspec.lock index 741ac9e..e56c8ae 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -9,6 +9,14 @@ packages: url: "https://pub.dev" source: hosted version: "1.3.30" + app_links: + dependency: "direct main" + description: + name: app_links + sha256: "96e677810b83707ff5e10fac11e4839daa0ea4e0123c35864c092699165eb3db" + url: "https://pub.dev" + source: hosted + version: "6.1.1" args: dependency: transitive description: @@ -319,6 +327,14 @@ packages: description: flutter source: sdk version: "0.0.0" + gtk: + dependency: transitive + description: + name: gtk + sha256: e8ce9ca4b1df106e4d72dad201d345ea1a036cc12c360f1a7d5a758f78ffa42c + url: "https://pub.dev" + source: hosted + version: "2.1.0" html_unescape: dependency: "direct main" description: @@ -368,34 +384,34 @@ packages: dependency: "direct main" description: name: intl - sha256: "3bc132a9dbce73a7e4a21a17d06e1878839ffbf975568bc875c60537824b0c4d" + sha256: d6f56758b7d3014a48af9701c085700aac781a92a87a62b1333b46d8879661cf url: "https://pub.dev" source: hosted - version: "0.18.1" + version: "0.19.0" leak_tracker: dependency: transitive description: name: leak_tracker - sha256: "78eb209deea09858f5269f5a5b02be4049535f568c07b275096836f01ea323fa" + sha256: "7f0df31977cb2c0b88585095d168e689669a2cc9b97c309665e3386f3e9d341a" url: "https://pub.dev" source: hosted - version: "10.0.0" + version: "10.0.4" leak_tracker_flutter_testing: dependency: transitive description: name: leak_tracker_flutter_testing - sha256: b46c5e37c19120a8a01918cfaf293547f47269f7cb4b0058f21531c2465d6ef0 + sha256: "06e98f569d004c1315b991ded39924b21af84cf14cc94791b8aea337d25b57f8" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.3" leak_tracker_testing: dependency: transitive description: name: leak_tracker_testing - sha256: a597f72a664dbd293f3bfc51f9ba69816f84dcd403cdac7066cb3f6003f3ab47 + sha256: "6ba465d5d76e67ddf503e1161d1f4a6bc42306f9d66ca1e8f079a47290fb06d3" url: "https://pub.dev" source: hosted - version: "2.0.1" + version: "3.0.1" linkify: dependency: transitive description: @@ -432,10 +448,10 @@ packages: dependency: transitive description: name: meta - sha256: d584fa6707a52763a52446f02cc621b077888fb63b93bbcb1143a7be5a0c0c04 + sha256: "7687075e408b093f36e6bbf6c91878cc0d4cd10f409506f7bc996f68220b9136" url: "https://pub.dev" source: hosted - version: "1.11.0" + version: "1.12.0" nm: dependency: transitive description: @@ -741,10 +757,10 @@ packages: dependency: transitive description: name: test_api - sha256: "5c2f730018264d276c20e4f1503fd1308dfbbae39ec8ee63c5236311ac06954b" + sha256: "9955ae474176f7ac8ee4e989dadfb411a58c30415bcfb648fa04b2b8a03afa7f" url: "https://pub.dev" source: hosted - version: "0.6.1" + version: "0.7.0" typed_data: dependency: transitive description: @@ -837,10 +853,10 @@ packages: dependency: transitive description: name: vm_service - sha256: b3d56ff4341b8f182b96aceb2fa20e3dcb336b9f867bc0eafc0de10f1048e957 + sha256: "3923c89304b715fb1eb6423f017651664a03bf5f4b29983627c4da791f74a4ec" url: "https://pub.dev" source: hosted - version: "13.0.0" + version: "14.2.1" web: dependency: transitive description: diff --git a/pubspec.yaml b/pubspec.yaml index 5ed8aa1..6a28649 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -31,6 +31,7 @@ dependencies: flutter_displaymode: ^0.6.0 install_referrer: ^1.2.1 in_app_update: ^4.2.2 + app_links: ^6.1.1 dev_dependencies: integration_test: sdk: flutter diff --git a/windows/flutter/generated_plugin_registrant.cc b/windows/flutter/generated_plugin_registrant.cc index 1a60256..02a3349 100644 --- a/windows/flutter/generated_plugin_registrant.cc +++ b/windows/flutter/generated_plugin_registrant.cc @@ -6,6 +6,7 @@ #include "generated_plugin_registrant.h" +#include #include #include #include @@ -13,14 +14,16 @@ #include void RegisterPlugins(flutter::PluginRegistry* registry) { - ConnectivityPlusWindowsPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); - DynamicColorPluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); - FirebaseCorePluginCApiRegisterWithRegistrar( - registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); - SentryFlutterPluginRegisterWithRegistrar( - registry->GetRegistrarForPlugin("SentryFlutterPlugin")); - UrlLauncherWindowsRegisterWithRegistrar( - registry->GetRegistrarForPlugin("UrlLauncherWindows")); + AppLinksPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("AppLinksPluginCApi")); + ConnectivityPlusWindowsPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("ConnectivityPlusWindowsPlugin")); + DynamicColorPluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("DynamicColorPluginCApi")); + FirebaseCorePluginCApiRegisterWithRegistrar( + registry->GetRegistrarForPlugin("FirebaseCorePluginCApi")); + SentryFlutterPluginRegisterWithRegistrar( + registry->GetRegistrarForPlugin("SentryFlutterPlugin")); + UrlLauncherWindowsRegisterWithRegistrar( + registry->GetRegistrarForPlugin("UrlLauncherWindows")); } diff --git a/windows/flutter/generated_plugins.cmake b/windows/flutter/generated_plugins.cmake index 928840d..5995934 100644 --- a/windows/flutter/generated_plugins.cmake +++ b/windows/flutter/generated_plugins.cmake @@ -3,6 +3,7 @@ # list(APPEND FLUTTER_PLUGIN_LIST + app_links connectivity_plus dynamic_color firebase_core