From adb8b65829f28b43176ca78910cb9ca50fbb3ce0 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 19 Aug 2023 15:14:05 +0100 Subject: [PATCH 01/18] Update target sdk version --- uni/android/app/build.gradle | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/uni/android/app/build.gradle b/uni/android/app/build.gradle index e1abc5e1c..8fe00a408 100644 --- a/uni/android/app/build.gradle +++ b/uni/android/app/build.gradle @@ -32,7 +32,7 @@ if (keystorePropertiesFile.exists()) { } android { - compileSdkVersion flutter.compileSdkVersion + compileSdkVersion 33 // default is flutter.compileSdkVersion ndkVersion flutter.ndkVersion compileOptions { @@ -50,10 +50,8 @@ android { defaultConfig { applicationId "pt.up.fe.ni.uni" - // You can update the following values to match your application needs. - // For more information, see: https://docs.flutter.dev/deployment/android#reviewing-the-build-configuration. - minSdkVersion flutter.minSdkVersion - targetSdkVersion flutter.targetSdkVersion + minSdkVersion 21 // default is flutter.minSdkVersion + targetSdkVersion 33 // default is flutter.targetSdkVersion versionCode flutterVersionCode.toInteger() versionName flutterVersionName } From d2ed63a706f8f265db2fe96f68c1cf06825b92d3 Mon Sep 17 00:00:00 2001 From: niaefeup-admin Date: Fri, 1 Sep 2023 11:34:56 +0000 Subject: [PATCH 02/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 862505918..7ffae3959 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.0+118 \ No newline at end of file +1.6.0+119 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index b4e98accf..fe92515d3 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -20,7 +20,7 @@ publish_to: 'none' # Remove this line if you wish to publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.0+118 +version: 1.6.0+119 environment: sdk: ">=2.17.1 <3.0.0" From aae1f6fefdfcb3cfc299a707edd3da1c654bbe9f Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Wed, 20 Sep 2023 19:29:50 +0000 Subject: [PATCH 03/18] Bump very_good_analysis from 4.0.0+1 to 5.1.0 in /uni Bumps [very_good_analysis](https://github.com/VeryGoodOpenSource/very_good_analysis) from 4.0.0+1 to 5.1.0. - [Release notes](https://github.com/VeryGoodOpenSource/very_good_analysis/releases) - [Changelog](https://github.com/VeryGoodOpenSource/very_good_analysis/blob/main/CHANGELOG.md) - [Commits](https://github.com/VeryGoodOpenSource/very_good_analysis/compare/v4.0.0...v5.1.0) --- updated-dependencies: - dependency-name: very_good_analysis dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- uni/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index d1c137447..76875e25c 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -68,7 +68,7 @@ dev_dependencies: url: https://github.com/dart-lang/mockito.git ref: "e54a006" test: any - very_good_analysis: ^4.0.0+1 + very_good_analysis: ^5.1.0 flutter: generate: true From 610751ef1893a5ba34401012c6b8212ac5545dae Mon Sep 17 00:00:00 2001 From: bdmendes Date: Fri, 22 Sep 2023 07:46:00 +0000 Subject: [PATCH 04/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 7ebc9097f..8f2c3c19e 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.64+182 \ No newline at end of file +1.5.65+183 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 76875e25c..51a32fd64 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.64+182 +version: 1.5.65+183 environment: sdk: '>=3.0.0 <4.0.0' From 3b4397ea44a54474d714bdfc2c70acffa9224084 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Sep 2023 11:28:30 +0100 Subject: [PATCH 05/18] Bump encrypt version --- uni/pubspec.yaml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 51a32fd64..944950dc9 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -22,7 +22,7 @@ dependencies: cupertino_icons: ^1.0.2 currency_text_input_formatter: ^2.1.5 email_validator: ^2.0.1 - encrypt: ^5.0.0-beta.1 + encrypt: ^5.0.3 expansion_tile_card: ^3.0.0 flutter: sdk: flutter From 09f3191ca00c01cc6d48ca573d7707c8a7de6b1f Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Sep 2023 12:40:53 +0100 Subject: [PATCH 06/18] Use same key for encryption purposes --- .../background_workers/notifications.dart | 4 ++ .../local_storage/app_shared_preferences.dart | 38 +++++++++---------- .../controller/networking/network_router.dart | 7 ++++ uni/lib/main.dart | 4 +- .../model/providers/lazy/exam_provider.dart | 12 +++--- .../providers/lazy/lecture_provider.dart | 8 ++-- .../providers/startup/profile_provider.dart | 8 ++-- .../providers/startup/session_provider.dart | 5 +++ .../view/common_widgets/page_transition.dart | 9 ----- uni/lib/view/login/login.dart | 2 + uni/lib/view/terms_and_condition_dialog.dart | 7 +--- uni/test/integration/src/exams_page_test.dart | 5 +-- .../integration/src/schedule_page_test.dart | 3 +- .../unit/providers/exams_provider_test.dart | 17 ++++----- .../unit/providers/lecture_provider_test.dart | 6 +-- 15 files changed, 65 insertions(+), 70 deletions(-) diff --git a/uni/lib/controller/background_workers/notifications.dart b/uni/lib/controller/background_workers/notifications.dart index f6c7f52f8..b09b0365a 100644 --- a/uni/lib/controller/background_workers/notifications.dart +++ b/uni/lib/controller/background_workers/notifications.dart @@ -71,6 +71,10 @@ class NotificationManager { final userInfo = await AppSharedPreferences.getPersistentUserInfo(); final faculties = await AppSharedPreferences.getUserFaculties(); + if (userInfo == null || faculties.isEmpty) { + return; + } + final session = await NetworkRouter.login( userInfo.item1, userInfo.item2, diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index d5da1a7c4..cf447d340 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -14,6 +14,10 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// This database stores the user's student number, password and favorite /// widgets. class AppSharedPreferences { + static final iv = encrypt.IV.fromLength(16); + static final key = + encrypt.Key.fromBase64('DT3/GTNYldhwOD3ZbpVLoAwA/mncsN7U7sJxfFn3y0A='); + static const lastUpdateTimeKeySuffix = '_last_update_time'; static const String userNumber = 'user_number'; static const String userPw = 'user_password'; @@ -24,10 +28,6 @@ class AppSharedPreferences { 'tuition_notification_toogle'; static const String themeMode = 'theme_mode'; static const String locale = 'app_locale'; - static const int keyLength = 32; - static const int ivLength = 16; - static final iv = encrypt.IV.fromLength(ivLength); - static const String favoriteCards = 'favorite_cards'; static final List defaultFavoriteCards = [ FavoriteWidgetType.schedule, @@ -149,9 +149,12 @@ class AppSharedPreferences { /// * the first element in the tuple is the user's student number. /// * the second element in the tuple is the user's password, in plain text /// format. - static Future> getPersistentUserInfo() async { + static Future?> getPersistentUserInfo() async { final userNum = await getUserNumber(); final userPass = await getUserPassword(); + if (userNum == null || userPass == null) { + return null; + } return Tuple2(userNum, userPass); } @@ -164,22 +167,16 @@ class AppSharedPreferences { } /// Returns the user's student number. - static Future getUserNumber() async { + static Future getUserNumber() async { final prefs = await SharedPreferences.getInstance(); - return prefs.getString(userNumber) ?? - ''; // empty string for the case it does not exist + return prefs.getString(userNumber); } /// Returns the user's password, in plain text format. - static Future getUserPassword() async { + static Future getUserPassword() async { final prefs = await SharedPreferences.getInstance(); - var pass = prefs.getString(userPw) ?? ''; - - if (pass != '') { - pass = decode(pass); - } - - return pass; + final password = prefs.getString(userPw); + return password != null ? decode(password) : null; } /// Replaces the user's favorite widgets with [newFavorites]. @@ -270,15 +267,18 @@ class AppSharedPreferences { } /// Decrypts [base64Text]. - static String decode(String base64Text) { + static String? decode(String base64Text) { final encrypter = _createEncrypter(); - return encrypter.decrypt64(base64Text, iv: iv); + try { + return encrypter.decrypt64(base64Text, iv: iv); + } catch (e) { + return null; + } } /// Creates an [encrypt.Encrypter] for encrypting and decrypting the user's /// password. static encrypt.Encrypter _createEncrypter() { - final key = encrypt.Key.fromLength(keyLength); return encrypt.Encrypter(encrypt.AES(key)); } diff --git a/uni/lib/controller/networking/network_router.dart b/uni/lib/controller/networking/network_router.dart index ce60f4a68..a72f59b98 100644 --- a/uni/lib/controller/networking/network_router.dart +++ b/uni/lib/controller/networking/network_router.dart @@ -71,6 +71,7 @@ class NetworkRouter { faculties, persistentSession: persistentSession, ); + if (session == null) { Logger().e('Login failed: user not authenticated'); return null; @@ -90,6 +91,12 @@ class NetworkRouter { static Future reLoginFromSession(Session session) async { final username = session.username; final password = await AppSharedPreferences.getUserPassword(); + + if (password == null) { + Logger().e('Re-login failed: password not found'); + return null; + } + final faculties = session.faculties; final persistentSession = session.persistentSession; diff --git a/uni/lib/main.dart b/uni/lib/main.dart index 6f2e29a30..407d18030 100644 --- a/uni/lib/main.dart +++ b/uni/lib/main.dart @@ -52,10 +52,8 @@ SentryEvent? beforeSend(SentryEvent event) { Future firstRoute() async { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); - final userName = userPersistentInfo.item1; - final password = userPersistentInfo.item2; - if (userName != '' && password != '') { + if (userPersistentInfo != null) { return '/${DrawerItem.navPersonalArea.title}'; } diff --git a/uni/lib/model/providers/lazy/exam_provider.dart b/uni/lib/model/providers/lazy/exam_provider.dart index 562424f80..4327df8b3 100644 --- a/uni/lib/model/providers/lazy/exam_provider.dart +++ b/uni/lib/model/providers/lazy/exam_provider.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:collection'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/exam_fetcher.dart'; import 'package:uni/controller/local_storage/app_exams_database.dart'; import 'package:uni/controller/local_storage/app_shared_preferences.dart'; @@ -42,27 +41,28 @@ class ExamProvider extends StateProviderNotifier { Future loadFromRemote(Session session, Profile profile) async { await fetchUserExams( ParserExams(), - await AppSharedPreferences.getPersistentUserInfo(), profile, session, profile.courseUnits, + persistentSession: + (await AppSharedPreferences.getPersistentUserInfo()) != null, ); } Future fetchUserExams( ParserExams parserExams, - Tuple2 userPersistentInfo, Profile profile, Session session, - List userUcs, - ) async { + List userUcs, { + required bool persistentSession, + }) async { try { final exams = await ExamFetcher(profile.courses, userUcs) .extractExams(session, parserExams); exams.sort((exam1, exam2) => exam1.begin.compareTo(exam2.begin)); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (persistentSession) { await AppExamsDatabase().saveNewExams(exams); } diff --git a/uni/lib/model/providers/lazy/lecture_provider.dart b/uni/lib/model/providers/lazy/lecture_provider.dart index a9f832a5f..fd5789ef7 100644 --- a/uni/lib/model/providers/lazy/lecture_provider.dart +++ b/uni/lib/model/providers/lazy/lecture_provider.dart @@ -1,7 +1,6 @@ import 'dart:async'; import 'dart:collection'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher_api.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher_html.dart'; @@ -30,23 +29,24 @@ class LectureProvider extends StateProviderNotifier { @override Future loadFromRemote(Session session, Profile profile) async { await fetchUserLectures( - await AppSharedPreferences.getPersistentUserInfo(), session, profile, + persistentSession: + (await AppSharedPreferences.getPersistentUserInfo()) != null, ); } Future fetchUserLectures( - Tuple2 userPersistentInfo, Session session, Profile profile, { + required bool persistentSession, ScheduleFetcher? fetcher, }) async { try { final lectures = await getLecturesFromFetcherOrElse(fetcher, session, profile); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (persistentSession) { final db = AppLecturesDatabase(); await db.saveNewLectures(lectures); } diff --git a/uni/lib/model/providers/startup/profile_provider.dart b/uni/lib/model/providers/startup/profile_provider.dart index 81d386fad..c6f05d31a 100644 --- a/uni/lib/model/providers/startup/profile_provider.dart +++ b/uni/lib/model/providers/startup/profile_provider.dart @@ -75,7 +75,7 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (userPersistentInfo != null) { final profileDb = AppUserDataDatabase(); await profileDb.saveUserFees(feesBalance, feesLimit); } @@ -95,7 +95,7 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (userPersistentInfo != null) { final profileDb = AppUserDataDatabase(); await profileDb.saveUserPrintBalance(printBalance); } @@ -119,7 +119,7 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (userPersistentInfo != null) { // Course units are saved later, so we don't it here final profileDb = AppUserDataDatabase(); await profileDb.insertUserData(_profile); @@ -144,7 +144,7 @@ class ProfileProvider extends StateProviderNotifier { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); - if (userPersistentInfo.item1 != '' && userPersistentInfo.item2 != '') { + if (userPersistentInfo != null) { final coursesDb = AppCoursesDatabase(); await coursesDb.saveNewCourses(courses); diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index e4b006ea6..6131432dd 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -35,6 +35,11 @@ class SessionProvider extends StateProviderNotifier { Future loadFromStorage() async { final userPersistentInfo = await AppSharedPreferences.getPersistentUserInfo(); + + if (userPersistentInfo == null) { + return; + } + final userName = userPersistentInfo.item1; final password = userPersistentInfo.item2; diff --git a/uni/lib/view/common_widgets/page_transition.dart b/uni/lib/view/common_widgets/page_transition.dart index 494f8097f..2f3581a0d 100644 --- a/uni/lib/view/common_widgets/page_transition.dart +++ b/uni/lib/view/common_widgets/page_transition.dart @@ -1,6 +1,4 @@ import 'package:flutter/material.dart'; - -import 'package:uni/controller/local_storage/app_shared_preferences.dart'; import 'package:uni/view/navigation_service.dart'; import 'package:uni/view/terms_and_condition_dialog.dart'; @@ -49,16 +47,9 @@ class PageTransition { static Future requestTermsAndConditionsAcceptanceIfNeeded( BuildContext context, ) async { - final userPersistentInfo = - await AppSharedPreferences.getPersistentUserInfo(); - final userName = userPersistentInfo.item1; - final password = userPersistentInfo.item2; - if (context.mounted) { final termsAcceptance = await TermsAndConditionDialog.buildIfTermsChanged( context, - userName, - password, ); switch (termsAcceptance) { diff --git a/uni/lib/view/login/login.dart b/uni/lib/view/login/login.dart index 7cea404ee..a7cd92df5 100644 --- a/uni/lib/view/login/login.dart +++ b/uni/lib/view/login/login.dart @@ -2,6 +2,7 @@ import 'dart:async'; import 'package:flutter/material.dart'; import 'package:flutter_svg/svg.dart'; +import 'package:logger/logger.dart'; import 'package:provider/provider.dart'; import 'package:uni/generated/l10n.dart'; import 'package:uni/model/entities/login_exceptions.dart'; @@ -67,6 +68,7 @@ class LoginPageViewState extends State { } else if (error is WrongCredentialsException) { unawaited(ToastMessage.error(context, error.message)); } else { + Logger().e(error); unawaited(ToastMessage.error(context, S.of(context).failed_login)); } } diff --git a/uni/lib/view/terms_and_condition_dialog.dart b/uni/lib/view/terms_and_condition_dialog.dart index a1b1e2a8e..245cd0747 100644 --- a/uni/lib/view/terms_and_condition_dialog.dart +++ b/uni/lib/view/terms_and_condition_dialog.dart @@ -13,8 +13,6 @@ class TermsAndConditionDialog { static Future buildIfTermsChanged( BuildContext context, - String userName, - String password, ) async { final termsAreAccepted = await updateTermsAndConditionsAcceptancePreference(); @@ -22,8 +20,7 @@ class TermsAndConditionDialog { if (!termsAreAccepted) { final routeCompleter = Completer(); SchedulerBinding.instance.addPostFrameCallback( - (timestamp) => - _buildShowDialog(context, routeCompleter, userName, password), + (timestamp) => _buildShowDialog(context, routeCompleter), ); return routeCompleter.future; } @@ -34,8 +31,6 @@ class TermsAndConditionDialog { static Future _buildShowDialog( BuildContext context, Completer userTermsDecision, - String userName, - String password, ) { return showDialog( context: context, diff --git a/uni/test/integration/src/exams_page_test.dart b/uni/test/integration/src/exams_page_test.dart index 44622790c..03779534f 100644 --- a/uni/test/integration/src/exams_page_test.dart +++ b/uni/test/integration/src/exams_page_test.dart @@ -6,7 +6,6 @@ import 'package:http/http.dart' as http; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_exams.dart'; import 'package:uni/model/entities/course.dart'; @@ -82,10 +81,10 @@ void main() { await examProvider.fetchUserExams( ParserExams(), - const Tuple2('', ''), profile, Session(username: '', cookies: '', faculties: ['feup']), [sopeCourseUnit, sdisCourseUnit], + persistentSession: false, ); await tester.pumpAndSettle(); @@ -118,10 +117,10 @@ void main() { await examProvider.fetchUserExams( ParserExams(), - const Tuple2('', ''), profile, Session(username: '', cookies: '', faculties: ['feup']), [sopeCourseUnit, sdisCourseUnit], + persistentSession: false, ); await tester.pumpAndSettle(); diff --git a/uni/test/integration/src/schedule_page_test.dart b/uni/test/integration/src/schedule_page_test.dart index e775c86b2..0deb663d2 100644 --- a/uni/test/integration/src/schedule_page_test.dart +++ b/uni/test/integration/src/schedule_page_test.dart @@ -7,7 +7,6 @@ import 'package:http/http.dart' as http; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; import 'package:provider/provider.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/course.dart'; import 'package:uni/model/entities/profile.dart'; @@ -71,9 +70,9 @@ void main() { expect(find.byKey(const Key(scheduleSlotTimeKey2)), findsNothing); await scheduleProvider.fetchUserLectures( - const Tuple2('', ''), Session(username: '', cookies: '', faculties: ['feup']), profile, + persistentSession: false, ); await tester.tap(find.byKey(const Key('schedule-page-tab-2'))); diff --git a/uni/test/unit/providers/exams_provider_test.dart b/uni/test/unit/providers/exams_provider_test.dart index 2f4c33ddd..5c696a338 100644 --- a/uni/test/unit/providers/exams_provider_test.dart +++ b/uni/test/unit/providers/exams_provider_test.dart @@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/controller/parsers/parser_exams.dart'; import 'package:uni/model/entities/course.dart'; @@ -61,8 +60,6 @@ void main() { 'feup', ); - const userPersistentInfo = Tuple2('', ''); - final profile = Profile()..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final userUcs = [sopeCourseUnit, sdisCourseUnit]; @@ -85,10 +82,10 @@ void main() { await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.exams.isNotEmpty, true); @@ -102,10 +99,10 @@ void main() { await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.successful); @@ -132,10 +129,10 @@ When given three exams but one is to be parsed out, await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.successful); @@ -148,10 +145,10 @@ When given three exams but one is to be parsed out, await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.failed); }); @@ -174,10 +171,10 @@ When given three exams but one is to be parsed out, await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.successful); @@ -202,10 +199,10 @@ When given three exams but one is to be parsed out, await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.successful); @@ -230,10 +227,10 @@ When given three exams but one is to be parsed out, await provider.fetchUserExams( parserExams, - userPersistentInfo, profile, session, userUcs, + persistentSession: false, ); expect(provider.status, RequestStatus.successful); diff --git a/uni/test/unit/providers/lecture_provider_test.dart b/uni/test/unit/providers/lecture_provider_test.dart index 8498f01c2..77edb5e27 100644 --- a/uni/test/unit/providers/lecture_provider_test.dart +++ b/uni/test/unit/providers/lecture_provider_test.dart @@ -2,7 +2,6 @@ import 'package:flutter_test/flutter_test.dart'; import 'package:http/http.dart'; import 'package:mockito/annotations.dart'; import 'package:mockito/mockito.dart'; -import 'package:tuple/tuple.dart'; import 'package:uni/controller/fetchers/schedule_fetcher/schedule_fetcher.dart'; import 'package:uni/controller/networking/network_router.dart'; import 'package:uni/model/entities/course.dart'; @@ -22,7 +21,6 @@ void main() { final fetcherMock = MockScheduleFetcher(); final mockClient = MockClient(); final mockResponse = MockResponse(); - const userPersistentInfo = Tuple2('', ''); final profile = Profile()..courses = [Course(id: 7474)]; final session = Session(username: '', cookies: '', faculties: ['feup']); final day = DateTime(2021, 06); @@ -66,10 +64,10 @@ void main() { .thenAnswer((_) async => [lecture1, lecture2]); await provider.fetchUserLectures( - userPersistentInfo, session, profile, fetcher: fetcherMock, + persistentSession: false, ); expect(provider.lectures, [lecture1, lecture2]); @@ -81,10 +79,10 @@ void main() { .thenAnswer((_) async => throw Exception('💥')); await provider.fetchUserLectures( - userPersistentInfo, session, profile, fetcher: fetcherMock, + persistentSession: false, ); expect(provider.status, RequestStatus.failed); From c1e3c9668649244205e23415dad464d1db811d8f Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Sep 2023 14:43:29 +0100 Subject: [PATCH 07/18] Use fixed iv for AES --- uni/lib/controller/local_storage/app_shared_preferences.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/controller/local_storage/app_shared_preferences.dart b/uni/lib/controller/local_storage/app_shared_preferences.dart index cf447d340..faca04476 100644 --- a/uni/lib/controller/local_storage/app_shared_preferences.dart +++ b/uni/lib/controller/local_storage/app_shared_preferences.dart @@ -14,7 +14,7 @@ import 'package:uni/utils/favorite_widget_type.dart'; /// This database stores the user's student number, password and favorite /// widgets. class AppSharedPreferences { - static final iv = encrypt.IV.fromLength(16); + static final iv = encrypt.IV.fromBase64('jF9jjdSEPgsKnf0jCl1GAQ=='); static final key = encrypt.Key.fromBase64('DT3/GTNYldhwOD3ZbpVLoAwA/mncsN7U7sJxfFn3y0A='); From c4a2238086ee2e426e3327838dac0ea294697bc8 Mon Sep 17 00:00:00 2001 From: limwa Date: Sat, 23 Sep 2023 15:46:20 +0000 Subject: [PATCH 08/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 8f2c3c19e..85999cd62 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.65+183 \ No newline at end of file +1.5.66+184 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 944950dc9..30d9542d8 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.65+183 +version: 1.5.66+184 environment: sdk: '>=3.0.0 <4.0.0' From 1f27e34c850c05c17a4c5c34e8dbb4c7776c13fa Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Sat, 23 Sep 2023 15:00:59 +0100 Subject: [PATCH 09/18] Remove useless login locks and checks --- .../controller/networking/network_router.dart | 60 ++++++++----------- .../providers/state_provider_notifier.dart | 10 ---- 2 files changed, 25 insertions(+), 45 deletions(-) diff --git a/uni/lib/controller/networking/network_router.dart b/uni/lib/controller/networking/network_router.dart index a72f59b98..f05d88d54 100644 --- a/uni/lib/controller/networking/network_router.dart +++ b/uni/lib/controller/networking/network_router.dart @@ -55,7 +55,6 @@ class NetworkRouter { final url = '${NetworkRouter.getBaseUrls(faculties)[0]}mob_val_geral.autentica'; - final response = await http.post( url.toUri(), body: {'pv_login': username, 'pv_password': password}, @@ -100,8 +99,6 @@ class NetworkRouter { final faculties = session.faculties; final persistentSession = session.persistentSession; - Logger().d('Re-login from session: $username, $faculties'); - return login( username, password, @@ -117,17 +114,13 @@ class NetworkRouter { String pass, List faculties, ) async { - return _loginLock.synchronized(() async { - final url = - '${NetworkRouter.getBaseUrls(faculties)[0]}vld_validacao.validacao'; - - final response = await http.post( - url.toUri(), - body: {'p_user': user, 'p_pass': pass}, - ).timeout(_requestTimeout); - - return response.body; - }); + final url = + '${NetworkRouter.getBaseUrls(faculties)[0]}vld_validacao.validacao'; + final response = await http.post( + url.toUri(), + body: {'p_user': user, 'p_pass': pass}, + ).timeout(_requestTimeout); + return response.body; } /// Extracts the cookies present in [headers]. @@ -192,6 +185,7 @@ class NetworkRouter { NavigationService.logoutAndPopHistory(null); return Future.error('Login failed'); } + session ..username = newSession.username ..cookies = newSession.cookies; @@ -218,20 +212,18 @@ class NetworkRouter { /// Check if the user is still logged in, /// performing a health check on the user's personal page. static Future userLoggedIn(Session session) async { - return _loginLock.synchronized(() async { - Logger().d('Checking if user is still logged in'); + Logger().d('Checking if user is still logged in'); - final url = '${getBaseUrl(session.faculties[0])}' - 'fest_geral.cursos_list?pv_num_unico=${session.username}'; - final headers = {}; - headers['cookie'] = session.cookies; + final url = '${getBaseUrl(session.faculties[0])}' + 'fest_geral.cursos_list?pv_num_unico=${session.username}'; + final headers = {}; + headers['cookie'] = session.cookies; - final response = await (httpClient != null - ? httpClient!.get(url.toUri(), headers: headers) - : http.get(url.toUri(), headers: headers)); + final response = await (httpClient != null + ? httpClient!.get(url.toUri(), headers: headers) + : http.get(url.toUri(), headers: headers)); - return response.statusCode == 200; - }); + return response.statusCode == 200; } /// Returns the base url of the user's faculties. @@ -253,17 +245,15 @@ class NetworkRouter { static Future killSigarraAuthentication( List faculties, ) async { - return _loginLock.synchronized(() async { - final url = '${NetworkRouter.getBaseUrl(faculties[0])}vld_validacao.sair'; - final response = await http.get(url.toUri()).timeout(_requestTimeout); + final url = '${NetworkRouter.getBaseUrl(faculties[0])}vld_validacao.sair'; + final response = await http.get(url.toUri()).timeout(_requestTimeout); - if (response.statusCode == 200) { - Logger().i('Logout Successful'); - } else { - Logger().i('Logout Failed'); - } + if (response.statusCode == 200) { + Logger().i('Logout Successful'); + } else { + Logger().i('Logout Failed'); + } - return response; - }); + return response; } } diff --git a/uni/lib/model/providers/state_provider_notifier.dart b/uni/lib/model/providers/state_provider_notifier.dart index beb4b7616..41e80ed28 100644 --- a/uni/lib/model/providers/state_provider_notifier.dart +++ b/uni/lib/model/providers/state_provider_notifier.dart @@ -108,16 +108,6 @@ abstract class StateProviderNotifier extends ChangeNotifier { Future forceRefresh(BuildContext context) async { await _lock.synchronized(() async { - if (_lastUpdateTime != null && - DateTime.now().difference(_lastUpdateTime!) < - const Duration(minutes: 1)) { - Logger().w( - 'Last update for $runtimeType was less than a minute ago; ' - 'skipping refresh', - ); - return; - } - final session = Provider.of(context, listen: false).session; final profile = From eda231e2b7d9897a32a65bece5999117ec72d46a Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 25 Sep 2023 10:16:43 +0000 Subject: [PATCH 10/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 85999cd62..c88b05e01 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.66+184 \ No newline at end of file +1.5.67+185 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 30d9542d8..f08c60ccf 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.66+184 +version: 1.5.67+185 environment: sdk: '>=3.0.0 <4.0.0' From 5d66caddfd76b064d3fa6471d7720d37e119f13d Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 20 Sep 2023 18:02:43 +0100 Subject: [PATCH 11/18] Bug fix --- uni/lib/model/entities/lecture.dart | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/uni/lib/model/entities/lecture.dart b/uni/lib/model/entities/lecture.dart index 8ff9da9db..1df6d1426 100644 --- a/uni/lib/model/entities/lecture.dart +++ b/uni/lib/model/entities/lecture.dart @@ -25,7 +25,7 @@ class Lecture { String classNumber, int occurrId, ) { - final endTime = startTime.add(Duration(seconds: 60 * 30 * blocks)); + final endTime = startTime.add(Duration(minutes: 30 * blocks)); final lecture = Lecture( subject, typeClass, @@ -62,8 +62,8 @@ class Lecture { day.add(Duration(hours: startTimeHours, minutes: startTimeMinutes)), day.add( Duration( - hours: startTimeMinutes + endTimeHours, - minutes: startTimeMinutes + endTimeMinutes, + hours: endTimeHours, + minutes: endTimeMinutes, ), ), blocks, From 70799b5442c94d3f2c042ec50a3d40e3b61d2b56 Mon Sep 17 00:00:00 2001 From: DGoiana Date: Wed, 20 Sep 2023 18:42:49 +0100 Subject: [PATCH 12/18] Code improvement --- uni/lib/model/entities/lecture.dart | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/uni/lib/model/entities/lecture.dart b/uni/lib/model/entities/lecture.dart index 1df6d1426..f91658b2e 100644 --- a/uni/lib/model/entities/lecture.dart +++ b/uni/lib/model/entities/lecture.dart @@ -44,28 +44,25 @@ class Lecture { String subject, String typeClass, DateTime day, - String startTime, + String startTimeString, int blocks, String room, String teacher, String classNumber, int occurrId, ) { - final startTimeHours = int.parse(startTime.substring(0, 2)); - final startTimeMinutes = int.parse(startTime.substring(3, 5)); - final endTimeHours = - (startTimeMinutes + (blocks * 30)) ~/ 60 + startTimeHours; - final endTimeMinutes = (startTimeMinutes + (blocks * 30)) % 60; + final startTime = day.add( + Duration( + hours: int.parse(startTimeString.substring(0, 2)), + minutes: int.parse(startTimeString.substring(3, 5)), + ), + ); + final endTime = startTime.add(Duration(minutes: 30 * blocks)); return Lecture( subject, typeClass, - day.add(Duration(hours: startTimeHours, minutes: startTimeMinutes)), - day.add( - Duration( - hours: endTimeHours, - minutes: endTimeMinutes, - ), - ), + startTime, + endTime, blocks, room, teacher, From a2b0625decc0c27fb2e792bf86a73129ec649bc9 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 25 Sep 2023 10:17:33 +0000 Subject: [PATCH 13/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index c88b05e01..72db98ade 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.67+185 \ No newline at end of file +1.5.68+186 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index f08c60ccf..09af5e880 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.67+185 +version: 1.5.68+186 environment: sdk: '>=3.0.0 <4.0.0' From fd71a9b7264c9244b4b79671cb2d0aa4e0cd4fc4 Mon Sep 17 00:00:00 2001 From: Bruno Mendes Date: Wed, 20 Sep 2023 16:54:19 +0100 Subject: [PATCH 14/18] Use home back button action on login page --- .../view/home/widgets/exit_app_dialog.dart | 58 ++++++++++--------- .../view/home/widgets/main_cards_list.dart | 2 +- uni/lib/view/login/login.dart | 23 +------- 3 files changed, 34 insertions(+), 49 deletions(-) diff --git a/uni/lib/view/home/widgets/exit_app_dialog.dart b/uni/lib/view/home/widgets/exit_app_dialog.dart index fe6ed0ab5..0abd03550 100644 --- a/uni/lib/view/home/widgets/exit_app_dialog.dart +++ b/uni/lib/view/home/widgets/exit_app_dialog.dart @@ -1,45 +1,49 @@ +import 'dart:async'; + import 'package:flutter/material.dart'; -import 'package:flutter/services.dart'; import 'package:uni/generated/l10n.dart'; /// Manages the app section displayed when the user presses the back button class BackButtonExitWrapper extends StatelessWidget { const BackButtonExitWrapper({ - required this.context, required this.child, super.key, }); - final BuildContext context; final Widget child; - Future backButton() { - return showDialog( - context: context, - builder: (context) => AlertDialog( - title: Text( - S.of(context).exit_confirm, - style: Theme.of(context).textTheme.headlineSmall, - ), - actions: [ - ElevatedButton( - onPressed: () => Navigator.of(context).pop(false), - child: Text(S.of(context).no), - ), - ElevatedButton( - onPressed: () => - SystemChannels.platform.invokeMethod('SystemNavigator.pop'), - child: Text(S.of(context).yes), - ) - ], - ), - ); - } - @override Widget build(BuildContext context) { return WillPopScope( - onWillPop: () => backButton() as Future, + onWillPop: () { + final userActionCompleter = Completer(); + showDialog( + context: context, + builder: (context) => AlertDialog( + title: Text( + S.of(context).exit_confirm, + style: Theme.of(context).textTheme.headlineSmall, + ), + actions: [ + ElevatedButton( + onPressed: () { + userActionCompleter.complete(false); + Navigator.of(context).pop(false); + }, + child: Text(S.of(context).no), + ), + ElevatedButton( + onPressed: () { + userActionCompleter.complete(true); + Navigator.of(context).pop(false); + }, + child: Text(S.of(context).yes), + ) + ], + ), + ); + return userActionCompleter.future; + }, child: child, ); } diff --git a/uni/lib/view/home/widgets/main_cards_list.dart b/uni/lib/view/home/widgets/main_cards_list.dart index b044dccf2..f28665a1c 100644 --- a/uni/lib/view/home/widgets/main_cards_list.dart +++ b/uni/lib/view/home/widgets/main_cards_list.dart @@ -24,6 +24,7 @@ typedef CardCreator = GenericCard Function( class MainCardsList extends StatelessWidget { const MainCardsList({super.key}); + static Map cardCreators = { FavoriteWidgetType.schedule: ScheduleCard.fromEditingInformation, FavoriteWidgetType.exams: ExamCard.fromEditingInformation, @@ -44,7 +45,6 @@ class MainCardsList extends StatelessWidget { return LazyConsumer( builder: (context, homePageProvider) => Scaffold( body: BackButtonExitWrapper( - context: context, child: SizedBox( height: MediaQuery.of(context).size.height, child: homePageProvider.isEditing diff --git a/uni/lib/view/login/login.dart b/uni/lib/view/login/login.dart index a7cd92df5..8a0131a3e 100644 --- a/uni/lib/view/login/login.dart +++ b/uni/lib/view/login/login.dart @@ -11,6 +11,7 @@ import 'package:uni/model/providers/state_providers.dart'; import 'package:uni/model/request_status.dart'; import 'package:uni/utils/drawer_items.dart'; import 'package:uni/view/common_widgets/toast_message.dart'; +import 'package:uni/view/home/widgets/exit_app_dialog.dart'; import 'package:uni/view/login/widgets/inputs.dart'; import 'package:uni/view/theme.dart'; import 'package:url_launcher/url_launcher.dart'; @@ -37,7 +38,6 @@ class LoginPageViewState extends State { TextEditingController(); final GlobalKey _formKey = GlobalKey(); - static bool _exitApp = false; bool _keepSignedIn = true; bool _obscurePasswordInput = true; @@ -113,7 +113,7 @@ class LoginPageViewState extends State { child: Builder( builder: (context) => Scaffold( backgroundColor: darkRed, - body: WillPopScope( + body: BackButtonExitWrapper( child: Padding( padding: EdgeInsets.only( left: queryData.size.width / 8, @@ -160,31 +160,12 @@ class LoginPageViewState extends State { ], ), ), - onWillPop: () => onWillPop(context), ), ), ), ); } - /// Delay time before the user leaves the app - Future exitAppWaiter() async { - _exitApp = true; - await Future.delayed(const Duration(seconds: 2)); - _exitApp = false; - } - - /// If the user tries to leave, displays a quick prompt for him to confirm. - /// If this is already the second time, the user leaves the app. - Future onWillPop(BuildContext context) { - if (_exitApp) { - return Future.value(true); - } - ToastMessage.info(context, S.of(context).press_again); - exitAppWaiter(); - return Future.value(false); - } - /// Creates the title for the login menu. Widget createTitle(MediaQueryData queryData, BuildContext context) { return ConstrainedBox( From 8a20d6b1d15a5f9ae55dbcffd13df85e2894761b Mon Sep 17 00:00:00 2001 From: bdmendes Date: Mon, 25 Sep 2023 10:18:09 +0000 Subject: [PATCH 15/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 72db98ade..79acc0972 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.5.68+186 \ No newline at end of file +1.5.69+187 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 09af5e880..97cb195fd 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.5.68+186 +version: 1.5.69+187 environment: sdk: '>=3.0.0 <4.0.0' From 60c7d5b35821bb81442bcc4daa5c0bc17fe313c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lu=C3=ADs=20Duarte?= Date: Mon, 25 Sep 2023 15:02:50 +0100 Subject: [PATCH 16/18] Fixed username format while persisting --- uni/lib/model/providers/startup/session_provider.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/uni/lib/model/providers/startup/session_provider.dart b/uni/lib/model/providers/startup/session_provider.dart index 6131432dd..cccdc536e 100644 --- a/uni/lib/model/providers/startup/session_provider.dart +++ b/uni/lib/model/providers/startup/session_provider.dart @@ -109,7 +109,7 @@ class SessionProvider extends StateProviderNotifier { if (persistentSession) { await AppSharedPreferences.savePersistentUserInfo( - username, + session.username, password, faculties, ); From 85d7277884badd65b9a8abdab87e5444af7a3436 Mon Sep 17 00:00:00 2001 From: thePeras Date: Mon, 25 Sep 2023 14:27:01 +0000 Subject: [PATCH 17/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index 4a90543d4..c1f4ccf54 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.6.0+188 \ No newline at end of file +1.6.1+189 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index 7695674c0..fdbccd0c1 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.6.0+188 +version: 1.6.1+189 environment: sdk: '>=3.0.0 <4.0.0' From 1bf751bb84099923cfa2f0d6150a17e5f69482c3 Mon Sep 17 00:00:00 2001 From: bdmendes Date: Tue, 26 Sep 2023 15:51:21 +0000 Subject: [PATCH 18/18] Bump app version [no ci] --- uni/app_version.txt | 2 +- uni/pubspec.yaml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/uni/app_version.txt b/uni/app_version.txt index c1f4ccf54..3a04413b4 100644 --- a/uni/app_version.txt +++ b/uni/app_version.txt @@ -1 +1 @@ -1.6.1+189 \ No newline at end of file +1.7.0+190 \ No newline at end of file diff --git a/uni/pubspec.yaml b/uni/pubspec.yaml index fdbccd0c1..78292fb41 100644 --- a/uni/pubspec.yaml +++ b/uni/pubspec.yaml @@ -7,7 +7,7 @@ publish_to: 'none' # We do not publish to pub.dev # To change it manually, override the value in app_version.txt. # The app version code is automatically also bumped by CI. # Do not change it manually. -version: 1.6.1+189 +version: 1.7.0+190 environment: sdk: '>=3.0.0 <4.0.0'