import 'package:hive_flutter/hive_flutter.dart'; /// Hive type adapter IDs and box names. class HiveBoxes { HiveBoxes._(); static const String settings = 'app_settings'; static const String prayerSchedule = 'prayer_schedule'; } /// AppSettings stored in Hive. @HiveType(typeId: 0) class AppSettings extends HiveObject { @HiveField(0) String masjidName; @HiveField(1) String masjidAddress; @HiveField(2) String cityIdApi; // myQuran city hash ID @HiveField(3) String cityDisplayName; @HiveField(4) bool showImsak; @HiveField(5) bool showTerbit; // Iqomah durations in minutes @HiveField(6) int iqomahSubuh; @HiveField(7) int iqomahDzuhur; @HiveField(8) int iqomahAshar; @HiveField(9) int iqomahMaghrib; @HiveField(10) int iqomahIsya; // Pre-Adzan lead time (minutes before adzan to lock main screen) @HiveField(11) int preAdzanLead; // Blank screen durations @HiveField(12) int blankScreenNormal; // minutes @HiveField(13) int blankScreenJumat; // minutes // Running text items @HiveField(14) List runningTexts; // Friday officers @HiveField(15) String khatibName; @HiveField(16) String imamName; // Rotation settings @HiveField(17) int mainScreenDurationSec; @HiveField(18) int slideDurationSec; // Last sync timestamp @HiveField(19) String? lastSyncDate; // Last automatic sync attempt timestamp (ISO8601). @HiveField(32) String? lastAutoSyncAttemptDate; // Slideshow image paths (local) @HiveField(20) List slideshowImages; // Text scaling (0=Small, 1=Medium, 2=Large) @HiveField(21) int textScaleIndex; // Unsplash Background configs @HiveField(22) bool useUnsplashBackground; @HiveField(23) String unsplashKeyword; @HiveField(24) int unsplashRotationHours; // Branded background image (local file path set by admin) @HiveField(25) String? brandedBgImage; // Per-item duration for running texts (seconds each) @HiveField(26) List runningTextDurations; // Running text animation type: 'marquee' or 'fade' @HiveField(27) String marqueeAnimType; // Granular text group scales (independent of textScaleIndex) // Group: Prayer card label (e.g. "SUBUH", "DZUHUR") @HiveField(28) double scaleCardLabel; // Group: Prayer card body (time + iqomah text) @HiveField(29) double scaleCardBody; // Group: Running text ticker at bottom @HiveField(30) double scaleRunningText; // Manual day adjustment applied to displayed Hijri date. @HiveField(31) int hijriOffsetDays; AppSettings({ this.masjidName = 'Masjid Al-Ikhlas', this.masjidAddress = 'Jl. Kebaikan No. 1', this.cityIdApi = '1218', // Default: Yogyakarta this.cityDisplayName = 'Kota Yogyakarta', this.showImsak = true, this.showTerbit = true, this.iqomahSubuh = 15, this.iqomahDzuhur = 10, this.iqomahAshar = 10, this.iqomahMaghrib = 7, this.iqomahIsya = 10, this.preAdzanLead = 10, this.blankScreenNormal = 15, this.blankScreenJumat = 45, this.runningTexts = const [ 'Mohon luruskan dan rapatkan shaf', 'Kajian rutin setiap Ahad pagi', ], this.khatibName = 'Ust. Fulan, S.Ag', this.imamName = 'Ust. Alan, Lc', this.mainScreenDurationSec = 15, this.slideDurationSec = 10, this.lastSyncDate, this.lastAutoSyncAttemptDate, this.slideshowImages = const [], this.textScaleIndex = 1, this.useUnsplashBackground = false, this.unsplashKeyword = 'mosque', this.unsplashRotationHours = 6, this.brandedBgImage, this.runningTextDurations = const [], this.marqueeAnimType = 'marquee', this.scaleCardLabel = 1.0, this.scaleCardBody = 1.0, this.scaleRunningText = 1.0, this.hijriOffsetDays = 0, }); AppSettings copyWith({ String? masjidName, String? masjidAddress, String? cityIdApi, String? cityDisplayName, bool? showImsak, bool? showTerbit, int? iqomahSubuh, int? iqomahDzuhur, int? iqomahAshar, int? iqomahMaghrib, int? iqomahIsya, int? preAdzanLead, int? blankScreenNormal, int? blankScreenJumat, List? runningTexts, String? khatibName, String? imamName, int? mainScreenDurationSec, int? slideDurationSec, String? lastSyncDate, String? lastAutoSyncAttemptDate, List? slideshowImages, int? textScaleIndex, bool? useUnsplashBackground, String? unsplashKeyword, int? unsplashRotationHours, String? brandedBgImage, List? runningTextDurations, String? marqueeAnimType, double? scaleCardLabel, double? scaleCardBody, double? scaleRunningText, int? hijriOffsetDays, }) { return AppSettings( masjidName: masjidName ?? this.masjidName, masjidAddress: masjidAddress ?? this.masjidAddress, cityIdApi: cityIdApi ?? this.cityIdApi, cityDisplayName: cityDisplayName ?? this.cityDisplayName, showImsak: showImsak ?? this.showImsak, showTerbit: showTerbit ?? this.showTerbit, iqomahSubuh: iqomahSubuh ?? this.iqomahSubuh, iqomahDzuhur: iqomahDzuhur ?? this.iqomahDzuhur, iqomahAshar: iqomahAshar ?? this.iqomahAshar, iqomahMaghrib: iqomahMaghrib ?? this.iqomahMaghrib, iqomahIsya: iqomahIsya ?? this.iqomahIsya, preAdzanLead: preAdzanLead ?? this.preAdzanLead, blankScreenNormal: blankScreenNormal ?? this.blankScreenNormal, blankScreenJumat: blankScreenJumat ?? this.blankScreenJumat, runningTexts: runningTexts ?? this.runningTexts, khatibName: khatibName ?? this.khatibName, imamName: imamName ?? this.imamName, mainScreenDurationSec: mainScreenDurationSec ?? this.mainScreenDurationSec, slideDurationSec: slideDurationSec ?? this.slideDurationSec, lastSyncDate: lastSyncDate ?? this.lastSyncDate, lastAutoSyncAttemptDate: lastAutoSyncAttemptDate ?? this.lastAutoSyncAttemptDate, slideshowImages: slideshowImages ?? this.slideshowImages, textScaleIndex: textScaleIndex ?? this.textScaleIndex, useUnsplashBackground: useUnsplashBackground ?? this.useUnsplashBackground, unsplashKeyword: unsplashKeyword ?? this.unsplashKeyword, unsplashRotationHours: unsplashRotationHours ?? this.unsplashRotationHours, brandedBgImage: brandedBgImage ?? this.brandedBgImage, runningTextDurations: runningTextDurations ?? this.runningTextDurations, marqueeAnimType: marqueeAnimType ?? this.marqueeAnimType, scaleCardLabel: scaleCardLabel ?? this.scaleCardLabel, scaleCardBody: scaleCardBody ?? this.scaleCardBody, scaleRunningText: scaleRunningText ?? this.scaleRunningText, hijriOffsetDays: hijriOffsetDays ?? this.hijriOffsetDays, ); } } /// Adapter for AppSettings — hand-written to avoid code generation. class AppSettingsAdapter extends TypeAdapter { @override final int typeId = 0; @override AppSettings read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = {}; for (int i = 0; i < numOfFields; i++) { fields[reader.readByte()] = reader.read(); } return AppSettings( masjidName: fields[0] as String? ?? 'Masjid Al-Ikhlas', masjidAddress: fields[1] as String? ?? 'Jl. Kebaikan No. 1', cityIdApi: fields[2] as String? ?? '1218', cityDisplayName: fields[3] as String? ?? 'Kota Yogyakarta', showImsak: fields[4] as bool? ?? true, showTerbit: fields[5] as bool? ?? true, iqomahSubuh: fields[6] as int? ?? 15, iqomahDzuhur: fields[7] as int? ?? 10, iqomahAshar: fields[8] as int? ?? 10, iqomahMaghrib: fields[9] as int? ?? 7, iqomahIsya: fields[10] as int? ?? 10, preAdzanLead: fields[11] as int? ?? 10, blankScreenNormal: fields[12] as int? ?? 15, blankScreenJumat: fields[13] as int? ?? 45, runningTexts: (fields[14] as List?)?.cast() ?? const [], khatibName: fields[15] as String? ?? '', imamName: fields[16] as String? ?? '', mainScreenDurationSec: fields[17] as int? ?? 15, slideDurationSec: fields[18] as int? ?? 10, lastSyncDate: fields[19] as String?, lastAutoSyncAttemptDate: fields[32] as String?, slideshowImages: (fields[20] as List?)?.cast() ?? const [], textScaleIndex: fields[21] as int? ?? 1, useUnsplashBackground: fields[22] as bool? ?? false, unsplashKeyword: fields[23] as String? ?? 'mosque', unsplashRotationHours: fields[24] as int? ?? 6, brandedBgImage: fields[25] as String?, runningTextDurations: (fields[26] as List?)?.cast() ?? const [], marqueeAnimType: fields[27] as String? ?? 'marquee', scaleCardLabel: (fields[28] as num?)?.toDouble() ?? 1.0, scaleCardBody: (fields[29] as num?)?.toDouble() ?? 1.0, scaleRunningText: (fields[30] as num?)?.toDouble() ?? 1.0, hijriOffsetDays: fields[31] as int? ?? 0, ); } @override void write(BinaryWriter writer, AppSettings obj) { writer ..writeByte(33) ..writeByte(0)..write(obj.masjidName) ..writeByte(1)..write(obj.masjidAddress) ..writeByte(2)..write(obj.cityIdApi) ..writeByte(3)..write(obj.cityDisplayName) ..writeByte(4)..write(obj.showImsak) ..writeByte(5)..write(obj.showTerbit) ..writeByte(6)..write(obj.iqomahSubuh) ..writeByte(7)..write(obj.iqomahDzuhur) ..writeByte(8)..write(obj.iqomahAshar) ..writeByte(9)..write(obj.iqomahMaghrib) ..writeByte(10)..write(obj.iqomahIsya) ..writeByte(11)..write(obj.preAdzanLead) ..writeByte(12)..write(obj.blankScreenNormal) ..writeByte(13)..write(obj.blankScreenJumat) ..writeByte(14)..write(obj.runningTexts) ..writeByte(15)..write(obj.khatibName) ..writeByte(16)..write(obj.imamName) ..writeByte(17)..write(obj.mainScreenDurationSec) ..writeByte(18)..write(obj.slideDurationSec) ..writeByte(19)..write(obj.lastSyncDate) ..writeByte(32)..write(obj.lastAutoSyncAttemptDate) ..writeByte(20)..write(obj.slideshowImages) ..writeByte(21)..write(obj.textScaleIndex) ..writeByte(22)..write(obj.useUnsplashBackground) ..writeByte(23)..write(obj.unsplashKeyword) ..writeByte(24)..write(obj.unsplashRotationHours) ..writeByte(25)..write(obj.brandedBgImage) ..writeByte(26)..write(obj.runningTextDurations) ..writeByte(27)..write(obj.marqueeAnimType) ..writeByte(28)..write(obj.scaleCardLabel) ..writeByte(29)..write(obj.scaleCardBody) ..writeByte(30)..write(obj.scaleRunningText) ..writeByte(31)..write(obj.hijriOffsetDays); } } /// Daily prayer schedule row cached from the MyQuran API. @HiveType(typeId: 1) class DailyPrayerSchedule extends HiveObject { @HiveField(0) String date; // yyyy-MM-dd @HiveField(1) String imsak; @HiveField(2) String subuh; @HiveField(3) String terbit; @HiveField(4) String dhuha; @HiveField(5) String dzuhur; @HiveField(6) String ashar; @HiveField(7) String maghrib; @HiveField(8) String isya; DailyPrayerSchedule({ required this.date, required this.imsak, required this.subuh, required this.terbit, required this.dhuha, required this.dzuhur, required this.ashar, required this.maghrib, required this.isya, }); /// Parse time string "HH:mm" to a DateTime on the given date. DateTime timeToDateTime(String time, DateTime refDate) { final parts = time.split(':'); return DateTime( refDate.year, refDate.month, refDate.day, int.parse(parts[0]), int.parse(parts[1]), ); } /// Get all prayer times as DateTime map for a given reference date. Map toDateTimeMap(DateTime refDate) => { 'imsak': timeToDateTime(imsak, refDate), 'subuh': timeToDateTime(subuh, refDate), 'terbit': timeToDateTime(terbit, refDate), 'dhuha': timeToDateTime(dhuha, refDate), 'dzuhur': timeToDateTime(dzuhur, refDate), 'ashar': timeToDateTime(ashar, refDate), 'maghrib': timeToDateTime(maghrib, refDate), 'isya': timeToDateTime(isya, refDate), }; } /// Adapter for DailyPrayerSchedule. class DailyPrayerScheduleAdapter extends TypeAdapter { @override final int typeId = 1; @override DailyPrayerSchedule read(BinaryReader reader) { final numOfFields = reader.readByte(); final fields = {}; for (int i = 0; i < numOfFields; i++) { fields[reader.readByte()] = reader.read(); } return DailyPrayerSchedule( date: fields[0] as String? ?? '', imsak: fields[1] as String? ?? '00:00', subuh: fields[2] as String? ?? '00:00', terbit: fields[3] as String? ?? '00:00', dhuha: fields[4] as String? ?? '00:00', dzuhur: fields[5] as String? ?? '00:00', ashar: fields[6] as String? ?? '00:00', maghrib: fields[7] as String? ?? '00:00', isya: fields[8] as String? ?? '00:00', ); } @override void write(BinaryWriter writer, DailyPrayerSchedule obj) { writer ..writeByte(9) ..writeByte(0)..write(obj.date) ..writeByte(1)..write(obj.imsak) ..writeByte(2)..write(obj.subuh) ..writeByte(3)..write(obj.terbit) ..writeByte(4)..write(obj.dhuha) ..writeByte(5)..write(obj.dzuhur) ..writeByte(6)..write(obj.ashar) ..writeByte(7)..write(obj.maghrib) ..writeByte(8)..write(obj.isya); } }