improved performance and battery usage on older devices
This commit is contained in:
@@ -1,7 +1,5 @@
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:rrule/rrule.dart';
|
||||
|
||||
import '../api/marianumconnect/queries/timetable_get_holidays/timetable_get_holidays_response.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_rooms/timetable_get_rooms_response.dart';
|
||||
import '../api/marianumconnect/queries/timetable_get_subjects/timetable_get_subjects_response.dart';
|
||||
@@ -10,6 +8,7 @@ import '../api/marianumconnect/queries/timetable_get_week/timetable_get_week_res
|
||||
import '../api/mhsl/custom_timetable_event/custom_timetable_event.dart';
|
||||
import '../api/mhsl/custom_timetable_event/get/get_custom_timetable_event_response.dart';
|
||||
import '../extensions/date_time.dart';
|
||||
import '../utils/recurrence_occurrences.dart';
|
||||
import '../view/pages/timetable/data/lesson_labels.dart';
|
||||
import '../view/pages/timetable/data/lesson_merger.dart';
|
||||
import '../view/pages/timetable/data/lesson_period_schedule.dart';
|
||||
@@ -443,11 +442,13 @@ class WidgetDataMapper {
|
||||
}
|
||||
|
||||
try {
|
||||
final parsed = RecurrenceRule.fromString(rule);
|
||||
final anchorUtc = event.startDate.toUtc();
|
||||
for (final occUtc in parsed.getInstances(start: anchorUtc)) {
|
||||
if (!occUtc.isBefore(rangeEndUtc)) break;
|
||||
if (occUtc.isBefore(rangeStartUtc)) continue;
|
||||
for (final occUtc in RecurrenceOccurrences.between(
|
||||
rule,
|
||||
anchorUtc,
|
||||
rangeStartUtc,
|
||||
rangeEndUtc,
|
||||
)) {
|
||||
final occLocal = occUtc.toLocal();
|
||||
final occStart = DateTime(
|
||||
occLocal.year,
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import 'dart:convert';
|
||||
import 'dart:developer';
|
||||
|
||||
import 'package:flutter/foundation.dart';
|
||||
@@ -20,22 +21,65 @@ class WidgetPublisher {
|
||||
static DateTime widgetNow() =>
|
||||
kDebugMode ? DateTime.now().add(debugTimeShift) : DateTime.now();
|
||||
|
||||
/// Identical snapshots are still re-written after this long, so the stored
|
||||
/// `fetchedAt` (freshness for the background refresh) doesn't go stale.
|
||||
static const Duration _republishAfter = Duration(minutes: 10);
|
||||
|
||||
static Future<void> _queue = Future.value();
|
||||
static (bool, String, bool)? _lastFlags;
|
||||
static String? _lastSignature;
|
||||
static DateTime? _lastPublishedAt;
|
||||
|
||||
/// Forgets what was written last, so the next publish writes again. Called
|
||||
/// whenever the stored widget data is cleared (sign-out).
|
||||
static void resetDedupe() {
|
||||
_lastSignature = null;
|
||||
_lastPublishedAt = null;
|
||||
}
|
||||
|
||||
/// Publishes run one at a time: they are fire-and-forget from the bloc
|
||||
/// stream, and interleaved writes could leave day and week data from
|
||||
/// different states behind.
|
||||
/// [epoch] is the session the [state] belongs to; defaults to the current
|
||||
/// one. Callers that delay the publish must capture it up front.
|
||||
static Future<void> publishFromBlocState(
|
||||
TimetableState state, {
|
||||
Settings? settings,
|
||||
bool isTeacher = false,
|
||||
int? epoch,
|
||||
}) {
|
||||
final sessionEpoch = epoch ?? AccountData().sessionEpoch;
|
||||
return _queue = _queue.then(
|
||||
(_) => _publish(
|
||||
state,
|
||||
settings: settings,
|
||||
isTeacher: isTeacher,
|
||||
epoch: sessionEpoch,
|
||||
),
|
||||
);
|
||||
}
|
||||
|
||||
static Future<void> _publish(
|
||||
TimetableState state, {
|
||||
required Settings? settings,
|
||||
required bool isTeacher,
|
||||
required int epoch,
|
||||
}) async {
|
||||
final epoch = AccountData().sessionEpoch;
|
||||
if (!AccountData().isCurrentSession(epoch)) return;
|
||||
try {
|
||||
final connectDouble =
|
||||
settings?.timetableSettings.connectDoubleLessons ?? true;
|
||||
// Mirror into widget storage so the background isolate sees the same
|
||||
// values the user just toggled — concurrently, they are independent.
|
||||
await Future.wait([
|
||||
WidgetSync.setConnectDoubleLessons(connectDouble),
|
||||
WidgetSync.setThemeMode(_themeName(settings?.appTheme)),
|
||||
WidgetSync.setIsTeacher(isTeacher),
|
||||
]);
|
||||
final flags = (connectDouble, _themeName(settings?.appTheme), isTeacher);
|
||||
if (flags != _lastFlags) {
|
||||
await Future.wait([
|
||||
WidgetSync.setConnectDoubleLessons(flags.$1),
|
||||
WidgetSync.setThemeMode(flags.$2),
|
||||
WidgetSync.setIsTeacher(flags.$3),
|
||||
]);
|
||||
_lastFlags = flags;
|
||||
}
|
||||
final lessons = state.getAllKnownLessons();
|
||||
final now = widgetNow();
|
||||
final dayData = WidgetDataMapper.buildDayData(
|
||||
@@ -63,6 +107,20 @@ class WidgetPublisher {
|
||||
// A publish still running at sign-out would put the previous account's
|
||||
// plan back onto the just cleared widget.
|
||||
if (!AccountData().isCurrentSession(epoch)) return;
|
||||
// Most bloc emits (week swipes, prefetches) don't touch the widget's
|
||||
// window; skip the SharedPreferences commits and widget re-render then.
|
||||
final signature = jsonEncode([
|
||||
_withoutFetchedAt(dayData.toJson()),
|
||||
_withoutFetchedAt(weekData.toJson()),
|
||||
]);
|
||||
final lastAt = _lastPublishedAt;
|
||||
if (signature == _lastSignature &&
|
||||
lastAt != null &&
|
||||
now.difference(lastAt) < _republishAfter) {
|
||||
return;
|
||||
}
|
||||
_lastSignature = signature;
|
||||
_lastPublishedAt = now;
|
||||
await WidgetSync.writeDayData(dayData);
|
||||
await WidgetSync.writeWeekData(weekData);
|
||||
await WidgetSync.setLoggedIn(true);
|
||||
@@ -74,6 +132,9 @@ class WidgetPublisher {
|
||||
}
|
||||
}
|
||||
|
||||
static Map<String, Object?> _withoutFetchedAt(Map<String, Object?> json) =>
|
||||
Map.of(json)..remove('fetchedAt');
|
||||
|
||||
static String _themeName(ThemeMode? mode) {
|
||||
switch (mode) {
|
||||
case ThemeMode.light:
|
||||
|
||||
@@ -5,6 +5,7 @@ import 'dart:developer';
|
||||
import 'package:home_widget/home_widget.dart';
|
||||
|
||||
import 'widget_data.dart';
|
||||
import 'widget_publisher.dart';
|
||||
|
||||
/// Bridge to the native widget host. All keys/names live here so the Kotlin
|
||||
/// and Swift sides stay in sync.
|
||||
@@ -109,6 +110,7 @@ class WidgetSync {
|
||||
}
|
||||
|
||||
static Future<void> clear() async {
|
||||
WidgetPublisher.resetDedupe();
|
||||
await ensureInitialized();
|
||||
await HomeWidget.saveWidgetData<String>(dayDataKey, null);
|
||||
await HomeWidget.saveWidgetData<String>(weekDataKey, null);
|
||||
|
||||
Reference in New Issue
Block a user