Files
Client/lib/widget_data/widget_sync.dart
T
2026-08-06 20:22:32 +02:00

127 lines
4.5 KiB
Dart

import 'dart:async';
import 'dart:convert';
import 'dart:developer';
import 'package:home_widget/home_widget.dart';
import 'widget_data.dart';
/// Bridge to the native widget host. All keys/names live here so the Kotlin
/// and Swift sides stay in sync.
class WidgetSync {
static const String iosAppGroupId =
'group.eu.mhsl.marianum.mobile.client.widget';
// Must match the WidgetKit `kind` strings declared in
// TimetableWidgetExtension.swift — a mismatch makes reloadTimelines a no-op.
static const String iosDayWidgetKind = 'TimetableDayWidget';
static const String iosWeekWidgetKind = 'TimetableWeekWidget';
static const String androidDayProvider = 'TimetableDayWidget';
static const String androidWeekProvider = 'TimetableWeekWidget';
// Version suffix lets a schema change invalidate stale snapshots by
// bumping the key instead of risking a parse crash.
static const String dayDataKey = 'widget_data_day_v1';
// v2: 14-day window + per-day `days` holiday info.
static const String weekDataKey = 'widget_data_week_v2';
static const String fetchedAtKey = 'widget_data_fetched_at_v1';
static const String loggedInKey = 'widget_data_logged_in_v1';
// Mirrored into widget storage so the background isolate can read it
// without reopening HydratedBloc storage.
static const String connectDoubleLessonsKey =
'widget_setting_connect_double_lessons_v1';
static const String themeModeKey = 'widget_setting_theme_mode_v1';
// Mirrored so the background isolate hits the same Marianum-Connect base
// URL the in-app settings cubit currently has selected.
static const String marianumConnectBaseUrlKey = 'widget_setting_mc_base_url_v1';
static bool _initialised = false;
static Future<void> ensureInitialized() async {
if (_initialised) return;
await HomeWidget.setAppGroupId(iosAppGroupId);
_initialised = true;
}
static Future<void> writeDayData(WidgetTimetableData data) =>
_writeData(dayDataKey, data);
static Future<void> writeWeekData(WidgetTimetableData data) =>
_writeData(weekDataKey, data);
static Future<void> _writeData(String key, WidgetTimetableData data) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<String>(key, jsonEncode(data.toJson()));
await HomeWidget.saveWidgetData<String>(
fetchedAtKey,
data.fetchedAt.toIso8601String(),
);
}
static Future<void> setLoggedIn(bool loggedIn) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<bool>(loggedInKey, loggedIn);
}
static Future<void> setConnectDoubleLessons(bool value) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<bool>(connectDoubleLessonsKey, value);
}
/// Default `true` matches `default_settings.dart` — fresh install behaves
/// like the in-app calendar.
static Future<bool> getConnectDoubleLessons() async {
await ensureInitialized();
final value = await HomeWidget.getWidgetData<bool>(
connectDoubleLessonsKey,
defaultValue: true,
);
return value ?? true;
}
static Future<void> setThemeMode(String mode) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<String>(themeModeKey, mode);
}
static Future<void> setMarianumConnectBaseUrl(String url) async {
await ensureInitialized();
await HomeWidget.saveWidgetData<String>(marianumConnectBaseUrlKey, url);
}
static Future<String?> getMarianumConnectBaseUrl() async {
await ensureInitialized();
return HomeWidget.getWidgetData<String>(marianumConnectBaseUrlKey);
}
static Future<DateTime?> getFetchedAt() async {
await ensureInitialized();
final raw = await HomeWidget.getWidgetData<String>(fetchedAtKey);
return raw == null ? null : DateTime.tryParse(raw);
}
static Future<void> clear() async {
await ensureInitialized();
await HomeWidget.saveWidgetData<String>(dayDataKey, null);
await HomeWidget.saveWidgetData<String>(weekDataKey, null);
await HomeWidget.saveWidgetData<String>(fetchedAtKey, null);
await HomeWidget.saveWidgetData<bool>(loggedInKey, false);
}
static Future<void> triggerUpdate() async {
await ensureInitialized();
try {
await HomeWidget.updateWidget(
androidName: androidDayProvider,
iOSName: iosDayWidgetKind,
);
await HomeWidget.updateWidget(
androidName: androidWeekProvider,
iOSName: iosWeekWidgetKind,
);
} on Exception catch (e) {
log('WidgetSync.triggerUpdate failed: $e');
}
}
}