Files
jamshalat-masjid-screen/lib/data/local/models.dart

544 lines
16 KiB
Dart

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';
static const String hijriCache = 'hijri_cache';
}
/// 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<String> runningTexts;
// Center text-slide items (separate from running ticker at bottom).
@HiveField(35)
List<String> textSlides;
// 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;
// Center hero block duration on main screen (seconds).
@HiveField(33)
int mainCenterSlideDurationSec;
// Per-text announcement slide duration on main screen (seconds).
@HiveField(34)
int announcementSlideDurationSec;
// Slideshow image paths (local)
@HiveField(20)
List<String> 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<int> 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;
// Group: Top header (identity + date on upper area)
@HiveField(36)
double scaleTopHeader;
// Group: Center text slides (pengumuman in main area)
@HiveField(37)
double scaleTextSlideCenter;
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.textSlides = 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.mainCenterSlideDurationSec = 10,
this.announcementSlideDurationSec = 7,
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,
this.scaleTopHeader = 1.0,
this.scaleTextSlideCenter = 1.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<String>? runningTexts,
List<String>? textSlides,
String? khatibName,
String? imamName,
int? mainScreenDurationSec,
int? slideDurationSec,
String? lastSyncDate,
String? lastAutoSyncAttemptDate,
int? mainCenterSlideDurationSec,
int? announcementSlideDurationSec,
List<String>? slideshowImages,
int? textScaleIndex,
bool? useUnsplashBackground,
String? unsplashKeyword,
int? unsplashRotationHours,
String? brandedBgImage,
List<int>? runningTextDurations,
String? marqueeAnimType,
double? scaleCardLabel,
double? scaleCardBody,
double? scaleRunningText,
int? hijriOffsetDays,
double? scaleTopHeader,
double? scaleTextSlideCenter,
}) {
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,
textSlides: textSlides ?? this.textSlides,
khatibName: khatibName ?? this.khatibName,
imamName: imamName ?? this.imamName,
mainScreenDurationSec:
mainScreenDurationSec ?? this.mainScreenDurationSec,
slideDurationSec: slideDurationSec ?? this.slideDurationSec,
lastSyncDate: lastSyncDate ?? this.lastSyncDate,
lastAutoSyncAttemptDate:
lastAutoSyncAttemptDate ?? this.lastAutoSyncAttemptDate,
mainCenterSlideDurationSec:
mainCenterSlideDurationSec ?? this.mainCenterSlideDurationSec,
announcementSlideDurationSec:
announcementSlideDurationSec ?? this.announcementSlideDurationSec,
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,
scaleTopHeader: scaleTopHeader ?? this.scaleTopHeader,
scaleTextSlideCenter:
scaleTextSlideCenter ?? this.scaleTextSlideCenter,
);
}
}
/// Adapter for AppSettings — hand-written to avoid code generation.
class AppSettingsAdapter extends TypeAdapter<AppSettings> {
@override
final int typeId = 0;
@override
AppSettings read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{};
for (int i = 0; i < numOfFields; i++) {
fields[reader.readByte()] = reader.read();
}
final runningTexts = (fields[14] as List?)?.cast<String>() ?? const [];
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: runningTexts,
textSlides:
(fields[35] as List?)?.cast<String>() ?? List<String>.from(runningTexts),
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?,
mainCenterSlideDurationSec: fields[33] as int? ?? 10,
announcementSlideDurationSec: fields[34] as int? ?? 7,
slideshowImages: (fields[20] as List?)?.cast<String>() ?? 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<int>() ?? 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,
scaleTopHeader: (fields[36] as num?)?.toDouble() ?? 1.0,
scaleTextSlideCenter: (fields[37] as num?)?.toDouble() ?? 1.0,
);
}
@override
void write(BinaryWriter writer, AppSettings obj) {
writer
..writeByte(38)
..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(35)
..write(obj.textSlides)
..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(33)
..write(obj.mainCenterSlideDurationSec)
..writeByte(34)
..write(obj.announcementSlideDurationSec)
..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)
..writeByte(36)
..write(obj.scaleTopHeader)
..writeByte(37)
..write(obj.scaleTextSlideCenter);
}
}
/// 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<String, DateTime> 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<DailyPrayerSchedule> {
@override
final int typeId = 1;
@override
DailyPrayerSchedule read(BinaryReader reader) {
final numOfFields = reader.readByte();
final fields = <int, dynamic>{};
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);
}
}