Skip to content

Commit

Permalink
Merge pull request #168 from DislikesSchool/wearos
Browse files Browse the repository at this point in the history
QR Login support for WearOS companion app
  • Loading branch information
vyPal committed Jul 6, 2024
2 parents 2f7cb1c + e4b3c93 commit 1df6477
Show file tree
Hide file tree
Showing 21 changed files with 305 additions and 47 deletions.
7 changes: 7 additions & 0 deletions android/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,13 @@
<action android:name="android.intent.action.MAIN"/>
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
<intent-filter android:autoVerify="true">
<action android:name="android.intent.action.VIEW" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.BROWSABLE" />
<data android:scheme="http" android:host="ep2.vypal.me" />
<data android:scheme="https" />
</intent-filter>
</activity>
<!-- Don't delete the meta-data below.
This is used by the Flutter tool to generate GeneratedPluginRegistrant.java -->
Expand Down
11 changes: 10 additions & 1 deletion lib/api.dart
Original file line number Diff line number Diff line change
Expand Up @@ -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) {
Expand Down Expand Up @@ -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(
Expand Down
4 changes: 2 additions & 2 deletions lib/create_message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -253,11 +253,11 @@ class SendMessageScreenState extends BaseState<SendMessageScreen> {
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<Color>(
WidgetStateProperty.all<Color>(
Colors.white),
),
child: const Icon(Icons.delete_rounded),
Expand Down
2 changes: 1 addition & 1 deletion lib/grades.dart
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class GradesPageState extends BaseState<GradesPage> {
children: <Widget>[messages],
)
: Text(AppLocalizations.of(context)!.loading),
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
);
}

Expand Down
6 changes: 3 additions & 3 deletions lib/home.dart
Original file line number Diff line number Diff line change
Expand Up @@ -341,9 +341,9 @@ class HomePageState extends BaseState<HomePage> {
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),
),
Expand Down Expand Up @@ -617,7 +617,7 @@ class HomePageState extends BaseState<HomePage> {
),
],
),
backgroundColor: theme.colorScheme.background,
backgroundColor: theme.colorScheme.surface,
drawer: Drawer(
child: ListView(
children: [
Expand Down
2 changes: 1 addition & 1 deletion lib/homework.dart
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ class HomeworkPageState extends BaseState<HomeworkPage> {
children: <Widget>[messages],
)
: Text(AppLocalizations.of(context)!.loading),
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
);
}

Expand Down
4 changes: 3 additions & 1 deletion lib/l10n/app_cs.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
4 changes: 3 additions & 1 deletion lib/l10n/app_en.arb
Original file line number Diff line number Diff line change
Expand Up @@ -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"
}
2 changes: 1 addition & 1 deletion lib/login.dart
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ class LoinPageState extends BaseState<LoginPage> {
Navigator.pop(context),
},
style: ButtonStyle(
elevation: MaterialStateProperty.all(3),
elevation: WidgetStateProperty.all(3),
),
child: Text(local!.loginLogin),
),
Expand Down
32 changes: 32 additions & 0 deletions lib/main.dart
Original file line number Diff line number Diff line change
@@ -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';
Expand Down Expand Up @@ -53,6 +57,8 @@ abstract class BaseState<T extends StatefulWidget> extends State<T> {
}
}

final GlobalKey<NavigatorState> navigatorKey = GlobalKey<NavigatorState>();

class MyApp extends StatelessWidget {
const MyApp({super.key});

Expand All @@ -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],
Expand Down Expand Up @@ -107,6 +114,9 @@ class PageBaseState extends BaseState<PageBase> {
int _selectedIndex = 0;
String baseUrl = FirebaseRemoteConfig.instance.getString("testUrl");

late AppLinks _appLinks;
StreamSubscription<Uri>? _linkSubscription;

bool loaded = false;

bool error = false; //for error status
Expand All @@ -125,6 +135,28 @@ class PageBaseState extends BaseState<PageBase> {
setOptimalDisplayMode();
if (!_isCheckingForUpdate) _checkForUpdate(); // ik that it's not necessary
super.initState();
initDeepLinks();
}

@override
void dispose() {
_linkSubscription?.cancel();
super.dispose();
}

Future<void> 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<void> setOptimalDisplayMode() async {
Expand Down
2 changes: 1 addition & 1 deletion lib/message.dart
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,7 @@ class MessagePageState extends BaseState<MessagePage> {
children: <Widget>[messages],
)
: Text(AppLocalizations.of(context)!.loading),
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
);
}
}
Expand Down
2 changes: 1 addition & 1 deletion lib/messages.dart
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ class TimeTablePageState extends BaseState<MessagesPage> {
children: <Widget>[messages],
)
: Text(AppLocalizations.of(context)!.loading),
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
floatingActionButton: FloatingActionButton(
onPressed: () {
Navigator.push(
Expand Down
178 changes: 178 additions & 0 deletions lib/qrlogin.dart
Original file line number Diff line number Diff line change
@@ -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<StatefulWidget> createState() => QRLoinPageState();
}

class QRLoinPageState extends BaseState<QRLoginPage> {
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<void> 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),
),
],
),
),
),
],
),
);
}
}
2 changes: 1 addition & 1 deletion lib/timetable.dart
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ class TimeTablePageState extends BaseState<TimeTablePage> {
context);
},
),
backgroundColor: Theme.of(context).colorScheme.background,
backgroundColor: Theme.of(context).colorScheme.surface,
);
}
}
Expand Down
Loading

0 comments on commit 1df6477

Please sign in to comment.