diff --git a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png index db77bb4..006d847 100644 Binary files a/android/app/src/main/res/mipmap-hdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png index 17987b7..2c8599f 100644 Binary files a/android/app/src/main/res/mipmap-mdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png index 09d4391..52bd050 100644 Binary files a/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png index d5f1c8d..13c139b 100644 Binary files a/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png index 4d6372e..a2f6eed 100644 Binary files a/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png and b/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png index dc9ada4..6ea8cdf 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-1024x1024@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png index 7353c41..174f283 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png index 797d452..306bfbc 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png index 6ed2d93..1c88d92 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png index 4cd7b00..ea02a29 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png index fe73094..2c75372 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png index 321773c..2e03a19 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png index 797d452..306bfbc 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png index 502f463..5bbfaf8 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png index 0ec3034..bd306af 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png index 0ec3034..bd306af 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png index e9f5fea..acb5c03 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png index 84ac32a..dbb7833 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png index 8953cba..86d938c 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png differ diff --git a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png index 0467bf1..0d9b7c8 100644 Binary files a/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png and b/ios/Runner/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png differ diff --git a/lib/features/admin/admin_screen.dart b/lib/features/admin/admin_screen.dart index c037bf3..f7747f6 100644 --- a/lib/features/admin/admin_screen.dart +++ b/lib/features/admin/admin_screen.dart @@ -10,6 +10,7 @@ import '../../core/sacred_tokens.dart'; import '../../providers.dart'; import '../../data/services/sync_service.dart'; import '../../data/services/myquran_service.dart'; +import '../../data/services/sound_service.dart'; import 'package:file_picker/file_picker.dart'; import 'dart:io'; @@ -120,7 +121,7 @@ class _AdminScreenState extends ConsumerState { _simulasiFocusNodes = [ _simulasiEntryFocusNode, ...List.generate( - 6, + 8, (index) => FocusNode(debugLabel: 'simulasi_row_${index + 1}'), ), ]; @@ -1005,16 +1006,8 @@ class _AdminScreenState extends ConsumerState { setState(() => _selectedTab = index); } - void _traceNav(String message) { - assert(() { - debugPrint('[TV NAV] $message'); - return true; - }()); - } - void _focusNavTab(int index) { if (index < 0 || index >= _navFocusNodes.length) return; - _traceNav('focus nav[$index]'); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _navFocusNodes[index].requestFocus(); @@ -1025,7 +1018,6 @@ class _AdminScreenState extends ConsumerState { void _focusIdentityRow(int index) { if (_selectedTab != 0) return; if (index < 0 || index >= _identityFocusNodes.length) return; - _traceNav('focus identitas[$index]'); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _identityFocusNodes[index].requestFocus(); @@ -1036,7 +1028,6 @@ class _AdminScreenState extends ConsumerState { void _focusJumatRow(int index) { if (_selectedTab != 3) return; if (index < 0 || index >= _jumatFocusNodes.length) return; - _traceNav('focus jumat[$index]'); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _jumatFocusNodes[index].requestFocus(); @@ -1047,7 +1038,6 @@ class _AdminScreenState extends ConsumerState { void _focusSimulasiRow(int index) { if (_selectedTab != 4) return; if (index < 0 || index >= _simulasiFocusNodes.length) return; - _traceNav('focus simulasi[$index]'); WidgetsBinding.instance.addPostFrameCallback((_) { if (mounted) { _simulasiFocusNodes[index].requestFocus(); @@ -1210,7 +1200,6 @@ class _AdminScreenState extends ConsumerState { return KeyEventResult.ignored; } final key = event.logicalKey; - _traceNav('identitas[$index] key=$key'); if (key == LogicalKeyboardKey.arrowUp) { _focusIdentityRow(index - 1); return KeyEventResult.handled; @@ -1274,7 +1263,6 @@ class _AdminScreenState extends ConsumerState { return KeyEventResult.ignored; } final key = event.logicalKey; - _traceNav('simulasi[$index] key=$key'); if (key == LogicalKeyboardKey.arrowUp) { _focusSimulasiRow(index - 1); return KeyEventResult.handled; @@ -3393,6 +3381,18 @@ class _AdminScreenState extends ConsumerState { rowIndex: 0, ), SizedBox(height: 16 * s), + _simulasiCard( + s: s, + title: '15 Detik Sebelum Adzan', + icon: HugeIcons.strokeRoundedNotification03, + desc: 'Melompat ke 15 detik sebelum Adzan Dzuhur untuk memeriksa transisi terakhir menuju Adzan.', + onTap: () => _activateSimulation( + () => _simulateEvent('pre_adzan_15'), + ), + focusNode: _simulasiFocusNodes[1], + rowIndex: 1, + ), + SizedBox(height: 16 * s), _simulasiCard( s: s, title: 'Menuju Adzan', @@ -3401,8 +3401,8 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('pre_adzan'), ), - focusNode: _simulasiFocusNodes[1], - rowIndex: 1, + focusNode: _simulasiFocusNodes[2], + rowIndex: 2, ), SizedBox(height: 16 * s), _simulasiCard( @@ -3413,8 +3413,20 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('adzan'), ), - focusNode: _simulasiFocusNodes[2], - rowIndex: 2, + focusNode: _simulasiFocusNodes[3], + rowIndex: 3, + ), + SizedBox(height: 16 * s), + _simulasiCard( + s: s, + title: '15 Detik Sebelum Iqamah', + icon: HugeIcons.strokeRoundedTimer02, + desc: 'Melompat ke 15 detik sebelum Iqamah Dzuhur untuk memeriksa hitungan mundur terakhir.', + onTap: () => _activateSimulation( + () => _simulateEvent('pre_iqomah_15'), + ), + focusNode: _simulasiFocusNodes[4], + rowIndex: 4, ), SizedBox(height: 16 * s), _simulasiCard( @@ -3425,8 +3437,8 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('iqomah'), ), - focusNode: _simulasiFocusNodes[3], - rowIndex: 3, + focusNode: _simulasiFocusNodes[5], + rowIndex: 5, ), SizedBox(height: 16 * s), _simulasiCard( @@ -3437,8 +3449,8 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('jumat_incoming'), ), - focusNode: _simulasiFocusNodes[4], - rowIndex: 4, + focusNode: _simulasiFocusNodes[6], + rowIndex: 6, ), SizedBox(height: 16 * s), _simulasiCard( @@ -3449,8 +3461,8 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('jumat_khutbah'), ), - focusNode: _simulasiFocusNodes[5], - rowIndex: 5, + focusNode: _simulasiFocusNodes[7], + rowIndex: 7, ), SizedBox(height: 16 * s), _simulasiCard( @@ -3461,8 +3473,8 @@ class _AdminScreenState extends ConsumerState { onTap: () => _activateSimulation( () => _simulateEvent('shalat'), ), - focusNode: _simulasiFocusNodes[6], - rowIndex: 6, + focusNode: _simulasiFocusNodes[8], + rowIndex: 8, ), ], ), @@ -3598,12 +3610,21 @@ class _AdminScreenState extends ConsumerState { DateTime targetTime; switch (eventType) { + case 'pre_adzan_15': + targetTime = dzuhurTime.subtract(const Duration(seconds: 15)); + break; case 'pre_adzan': targetTime = dzuhurTime.subtract(const Duration(minutes: 2)); break; case 'adzan': targetTime = dzuhurTime; break; + case 'pre_iqomah_15': + final settings = ref.read(settingsProvider); + targetTime = dzuhurTime + .add(Duration(minutes: settings.iqomahDzuhur)) + .subtract(const Duration(seconds: 15)); + break; case 'iqomah': targetTime = dzuhurTime.add(const Duration(seconds: 45)); // During iqomah break; @@ -3630,6 +3651,17 @@ class _AdminScreenState extends ConsumerState { final offset = targetTime.difference(realNow); _simulateTimeOffset(offset); + + switch (eventType) { + case 'adzan': + unawaited(SoundService.instance.playAdzanBeep()); + break; + case 'iqomah': + unawaited(SoundService.instance.playIqomahCountdown()); + break; + default: + break; + } } void _activateSimulation(VoidCallback action) { diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png index 82b6f9d..a90bb7c 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_1024.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png index 13b35eb..91da6ed 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_128.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png index 0a3f5fa..6e71c1d 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_16.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png index bdb5722..06c9159 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_256.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png index f083318..4b5971f 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_32.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png index 326c0e7..6ea8cdf 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_512.png differ diff --git a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png index 2f1632c..a8f2d98 100644 Binary files a/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png and b/macos/Runner/Assets.xcassets/AppIcon.appiconset/app_icon_64.png differ diff --git a/web/favicon.png b/web/favicon.png index 8aaa46a..2c8599f 100644 Binary files a/web/favicon.png and b/web/favicon.png differ diff --git a/web/icons/Icon-192.png b/web/icons/Icon-192.png index b749bfe..a2f6eed 100644 Binary files a/web/icons/Icon-192.png and b/web/icons/Icon-192.png differ diff --git a/web/icons/Icon-512.png b/web/icons/Icon-512.png index 88cfd48..6ea8cdf 100644 Binary files a/web/icons/Icon-512.png and b/web/icons/Icon-512.png differ diff --git a/web/icons/Icon-maskable-192.png b/web/icons/Icon-maskable-192.png index eb9b4d7..a2f6eed 100644 Binary files a/web/icons/Icon-maskable-192.png and b/web/icons/Icon-maskable-192.png differ diff --git a/web/icons/Icon-maskable-512.png b/web/icons/Icon-maskable-512.png index d69c566..6ea8cdf 100644 Binary files a/web/icons/Icon-maskable-512.png and b/web/icons/Icon-maskable-512.png differ