diff --git a/filcnaplo/ios/Runner/Runner.entitlements b/filcnaplo/ios/Runner/Runner.entitlements index 2eb7e33..123fc6c 100644 --- a/filcnaplo/ios/Runner/Runner.entitlements +++ b/filcnaplo/ios/Runner/Runner.entitlements @@ -3,6 +3,8 @@ com.apple.security.application-groups - + + group.refilcnaplo.livecard + diff --git a/filcnaplo/ios/livecard/livecard.entitlements b/filcnaplo/ios/livecard/livecard.entitlements index 2eb7e33..123fc6c 100644 --- a/filcnaplo/ios/livecard/livecard.entitlements +++ b/filcnaplo/ios/livecard/livecard.entitlements @@ -3,6 +3,8 @@ com.apple.security.application-groups - + + group.refilcnaplo.livecard + diff --git a/filcnaplo/lib/api/providers/live_card_provider.dart b/filcnaplo/lib/api/providers/live_card_provider.dart index 56fc76a..96f0a85 100644 --- a/filcnaplo/lib/api/providers/live_card_provider.dart +++ b/filcnaplo/lib/api/providers/live_card_provider.dart @@ -13,7 +13,14 @@ import 'package:flutter/foundation.dart'; import 'package:live_activities/live_activities.dart'; import 'package:filcnaplo_mobile_ui/pages/home/live_card/live_card.i18n.dart'; -enum LiveCardState { empty, duringLesson, duringBreak, morning, afternoon, night } +enum LiveCardState { + empty, + duringLesson, + duringBreak, + morning, + afternoon, + night +} class LiveCardProvider extends ChangeNotifier { Lesson? currentLesson; @@ -42,14 +49,17 @@ class LiveCardProvider extends ChangeNotifier { _latestActivityId = value.isNotEmpty ? value.first : null; }); _timer = Timer.periodic(const Duration(seconds: 1), (timer) => update()); - _delay = settings.bellDelayEnabled ? Duration(seconds: settings.bellDelay) : Duration.zero; + _delay = settings.bellDelayEnabled + ? Duration(seconds: settings.bellDelay) + : Duration.zero; update(); } @override void dispose() { _timer.cancel(); - if (_latestActivityId != null && Platform.isIOS) _liveActivitiesPlugin.endActivity(_latestActivityId!); + if (_latestActivityId != null && Platform.isIOS) + _liveActivitiesPlugin.endActivity(_latestActivityId!); super.dispose(); } @@ -78,14 +88,25 @@ class LiveCardProvider extends ChangeNotifier { switch (currentState) { case LiveCardState.duringLesson: return { - "icon": currentLesson != null ? SubjectIcon.resolveName(subject: currentLesson?.subject) : "book", - "index": currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "", - "title": currentLesson != null ? ShortSubject.resolve(subject: currentLesson?.subject).capital() : "", + "icon": currentLesson != null + ? SubjectIcon.resolveName(subject: currentLesson?.subject) + : "book", + "index": + currentLesson != null ? '${currentLesson!.lessonIndex}. ' : "", + "title": currentLesson != null + ? ShortSubject.resolve(subject: currentLesson?.subject).capital() + : "", "subtitle": currentLesson?.room.replaceAll("_", " ") ?? "", "description": currentLesson?.description ?? "", - "startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(), - "endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(), - "nextSubject": nextLesson != null ? ShortSubject.resolve(subject: nextLesson?.subject).capital() : "", + "startDate": ((currentLesson?.start.millisecondsSinceEpoch ?? 0) - + _delay.inMilliseconds) + .toString(), + "endDate": ((currentLesson?.end.millisecondsSinceEpoch ?? 0) - + _delay.inMilliseconds) + .toString(), + "nextSubject": nextLesson != null + ? ShortSubject.resolve(subject: nextLesson?.subject).capital() + : "", "nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "", }; case LiveCardState.duringBreak: @@ -101,10 +122,19 @@ class LiveCardProvider extends ChangeNotifier { return { "icon": iconFloorMap[diff] ?? "cup.and.saucer", "title": "Szünet", - "description": "go $diff".i18n.fill([diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room]), - "startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(), - "endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) - _delay.inMilliseconds).toString(), - "nextSubject": (nextLesson != null ? ShortSubject.resolve(subject: nextLesson?.subject) : "").capital(), + "description": "go $diff".i18n.fill([ + diff != "to room" ? (nextLesson!.getFloor() ?? 0) : nextLesson!.room + ]), + "startDate": ((prevLesson?.end.millisecondsSinceEpoch ?? 0) - + _delay.inMilliseconds) + .toString(), + "endDate": ((nextLesson?.start.millisecondsSinceEpoch ?? 0) - + _delay.inMilliseconds) + .toString(), + "nextSubject": (nextLesson != null + ? ShortSubject.resolve(subject: nextLesson?.subject) + : "") + .capital(), "nextRoom": nextLesson?.room.replaceAll("_", " ") ?? "", "index": "", "subtitle": "", @@ -119,15 +149,25 @@ class LiveCardProvider extends ChangeNotifier { final cmap = toMap(); if (!mapEquals(cmap, _lastActivity)) { _lastActivity = cmap; - - if (_lastActivity.isNotEmpty) { - if (_latestActivityId == null) { - _liveActivitiesPlugin.createActivity(_lastActivity).then((value) => _latestActivityId = value); + try { + if (_lastActivity.isNotEmpty) { + if (_latestActivityId == null) { + _liveActivitiesPlugin + .createActivity(_lastActivity) + .then((value) => _latestActivityId = value); + } else { + _liveActivitiesPlugin.updateActivity( + _latestActivityId!, _lastActivity); + } } else { - _liveActivitiesPlugin.updateActivity(_latestActivityId!, _lastActivity); + if (_latestActivityId != null) { + _liveActivitiesPlugin.endActivity(_latestActivityId!); + } + } + } catch (e) { + if (kDebugMode) { + print('ERROR: Unable to create or update iOS LiveCard!'); } - } else { - if (_latestActivityId != null) _liveActivitiesPlugin.endActivity(_latestActivityId!); } } } @@ -139,19 +179,28 @@ class LiveCardProvider extends ChangeNotifier { today = _today(_timetable); } - _delay = _settings.bellDelayEnabled ? Duration(seconds: _settings.bellDelay) : Duration.zero; + _delay = _settings.bellDelayEnabled + ? Duration(seconds: _settings.bellDelay) + : Duration.zero; final now = _now().add(_delay); // Filter cancelled lessons #20 // Filter label lessons #128 - today = today.where((lesson) => lesson.status?.name != "Elmaradt" && lesson.subject.id != '' && !lesson.isEmpty).toList(); + today = today + .where((lesson) => + lesson.status?.name != "Elmaradt" && + lesson.subject.id != '' && + !lesson.isEmpty) + .toList(); if (today.isNotEmpty) { // sort today.sort((a, b) => a.start.compareTo(b.start)); - final _lesson = today.firstWhere((l) => l.start.isBefore(now) && l.end.isAfter(now), orElse: () => Lesson.fromJson({})); + final _lesson = today.firstWhere( + (l) => l.start.isBefore(now) && l.end.isAfter(now), + orElse: () => Lesson.fromJson({})); if (_lesson.start.year != 0) { currentLesson = _lesson; @@ -159,7 +208,8 @@ class LiveCardProvider extends ChangeNotifier { currentLesson = null; } - final _next = today.firstWhere((l) => l.start.isAfter(now), orElse: () => Lesson.fromJson({})); + final _next = today.firstWhere((l) => l.start.isAfter(now), + orElse: () => Lesson.fromJson({})); nextLessons = today.where((l) => l.start.isAfter(now)).toList(); if (_next.start.year != 0) { @@ -168,7 +218,8 @@ class LiveCardProvider extends ChangeNotifier { nextLesson = null; } - final _prev = today.lastWhere((l) => l.end.isBefore(now), orElse: () => Lesson.fromJson({})); + final _prev = today.lastWhere((l) => l.end.isBefore(now), + orElse: () => Lesson.fromJson({})); if (_prev.start.year != 0) { prevLesson = _prev; @@ -198,7 +249,10 @@ class LiveCardProvider extends ChangeNotifier { Duration get delay => _delay; - bool _sameDate(DateTime a, DateTime b) => (a.year == b.year && a.month == b.month && a.day == b.day); + bool _sameDate(DateTime a, DateTime b) => + (a.year == b.year && a.month == b.month && a.day == b.day); - List _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? []).where((l) => _sameDate(l.date, _now())).toList(); + List _today(TimetableProvider p) => (p.getWeek(Week.current()) ?? []) + .where((l) => _sameDate(l.date, _now())) + .toList(); } diff --git a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart b/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart index 611a462..1e3e429 100644 --- a/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart +++ b/filcnaplo_premium/lib/ui/mobile/timetable/fs_timetable.dart @@ -11,7 +11,8 @@ import 'package:intl/intl.dart'; import 'package:i18n_extension/i18n_widget.dart'; class PremiumFSTimetable extends StatefulWidget { - const PremiumFSTimetable({Key? key, required this.controller}) : super(key: key); + const PremiumFSTimetable({Key? key, required this.controller}) + : super(key: key); final TimetableController controller; @@ -45,16 +46,21 @@ class _PremiumFSTimetableState extends State { final everyLesson = days.expand((x) => x).toList(); everyLesson.sort((a, b) => a.start.compareTo(b.start)); - final int maxLessonCount = days.fold(0, (a, b) => math.max(a, b.where((l) => l.subject.id != "" || l.isEmpty).length)); + final int maxLessonCount = days.fold( + 0, + (a, b) => math.max( + a, b.where((l) => l.subject.id != "" || l.isEmpty).length)); final int minIndex = int.tryParse(everyLesson.first.lessonIndex) ?? 0; - final int maxIndex = int.tryParse(everyLesson.last.lessonIndex) ?? maxLessonCount; + final int maxIndex = + int.tryParse(everyLesson.last.lessonIndex) ?? maxLessonCount; const prefixw = 40; const padding = prefixw + 6 * 2; final colw = (MediaQuery.of(context).size.width - padding) / days.length; return Scaffold( + appBar: buildAppBar(), body: ListView.builder( physics: const BouncingScrollPhysics(), padding: const EdgeInsets.symmetric(horizontal: 6.0, vertical: 24.0), @@ -63,7 +69,10 @@ class _PremiumFSTimetableState extends State { List columns = []; for (int dayIndex = -1; dayIndex < days.length; dayIndex++) { - final dayOffset = dayIndex == -1 ? 0 : (int.tryParse(days[dayIndex].first.lessonIndex) ?? 0) - minIndex; + final dayOffset = dayIndex == -1 + ? 0 + : (int.tryParse(days[dayIndex].first.lessonIndex) ?? 0) - + minIndex; final lessonIndex = index - 1; if (dayIndex == -1) { @@ -75,7 +84,9 @@ class _PremiumFSTimetableState extends State { padding: const EdgeInsets.symmetric(horizontal: 8.0), child: Text( "${minIndex + lessonIndex}.", - style: TextStyle(fontWeight: FontWeight.bold, color: Theme.of(context).colorScheme.secondary), + style: TextStyle( + fontWeight: FontWeight.bold, + color: Theme.of(context).colorScheme.secondary), ), ), )); @@ -85,12 +96,15 @@ class _PremiumFSTimetableState extends State { continue; } - final lessons = days[dayIndex].where((l) => l.subject.id != "" || l.isEmpty).toList(); + final lessons = days[dayIndex] + .where((l) => l.subject.id != "" || l.isEmpty) + .toList(); if (lessons.isEmpty) continue; if (lessonIndex >= lessons.length) continue; - if (dayIndex >= days.length || (lessonIndex + dayOffset) >= lessons.length) { + if (dayIndex >= days.length || + (lessonIndex + dayOffset) >= lessons.length) { columns.add(SizedBox(width: colw)); continue; } @@ -100,8 +114,11 @@ class _PremiumFSTimetableState extends State { width: colw, height: 40.0, child: Text( - DateFormat("EEEE", I18n.of(context).locale.languageCode).format(lessons.first.date).capital(), - style: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold), + DateFormat("EEEE", I18n.of(context).locale.languageCode) + .format(lessons.first.date) + .capital(), + style: const TextStyle( + fontSize: 24.0, fontWeight: FontWeight.bold), ), )); continue; @@ -113,11 +130,14 @@ class _PremiumFSTimetableState extends State { child: Row( crossAxisAlignment: CrossAxisAlignment.start, children: [ - Icon(FeatherIcons.slash, size: 18.0, color: AppColors.of(context).text.withOpacity(.3)), + Icon(FeatherIcons.slash, + size: 18.0, + color: AppColors.of(context).text.withOpacity(.3)), const SizedBox(width: 8.0), Text( "Lyukas óra", - style: TextStyle(color: AppColors.of(context).text.withOpacity(.3)), + style: TextStyle( + color: AppColors.of(context).text.withOpacity(.3)), ), ], ), @@ -139,16 +159,27 @@ class _PremiumFSTimetableState extends State { Row( children: [ Icon( - SubjectIcon.resolveVariant(context: context, subject: lessons[lessonIndex - dayOffset].subject), + SubjectIcon.resolveVariant( + context: context, + subject: lessons[lessonIndex - dayOffset].subject), size: 18.0, color: AppColors.of(context).text.withOpacity(.7), ), const SizedBox(width: 8.0), Expanded( child: Text( - lessons[lessonIndex - dayOffset].subject.renamedTo ?? lessons[lessonIndex - dayOffset].subject.name.capital(), + lessons[lessonIndex - dayOffset].subject.renamedTo ?? + lessons[lessonIndex - dayOffset] + .subject + .name + .capital(), maxLines: 1, - style: TextStyle(fontStyle: lessons[lessonIndex - dayOffset].subject.isRenamed ? FontStyle.italic : null), + style: TextStyle( + fontStyle: lessons[lessonIndex - dayOffset] + .subject + .isRenamed + ? FontStyle.italic + : null), overflow: TextOverflow.clip, softWrap: false, ), @@ -160,7 +191,9 @@ class _PremiumFSTimetableState extends State { padding: const EdgeInsets.only(left: 26.0), child: Text( lessons[lessonIndex - dayOffset].room, - style: TextStyle(color: AppColors.of(context).text.withOpacity(.5), overflow: TextOverflow.ellipsis), + style: TextStyle( + color: AppColors.of(context).text.withOpacity(.5), + overflow: TextOverflow.ellipsis), ), ), ], @@ -176,4 +209,14 @@ class _PremiumFSTimetableState extends State { ), ); } + + AppBar buildAppBar() { + return AppBar( + backgroundColor: Colors.transparent, + automaticallyImplyLeading: false, + actions: const [ + BackButton(), + ], + ); + } }