Files
jamshalat-masjid-screen/lib/data/local/models.dart
2026-03-31 14:37:14 +07:00

440 lines
13 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';
}
/// 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;
// 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<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;
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<String>? runningTexts,
String? khatibName,
String? imamName,
int? mainScreenDurationSec,
int? slideDurationSec,
String? lastSyncDate,
String? lastAutoSyncAttemptDate,
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,
}) {
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<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();
}
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<String>() ?? 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<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,
);
}
@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<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);
}
}