Clean TV navigation traces and update app icons
|
Before Width: | Height: | Size: 544 B After Width: | Height: | Size: 4.6 KiB |
|
Before Width: | Height: | Size: 442 B After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 721 B After Width: | Height: | Size: 6.5 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 11 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 295 B After Width: | Height: | Size: 1.0 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 450 B After Width: | Height: | Size: 3.7 KiB |
|
Before Width: | Height: | Size: 282 B After Width: | Height: | Size: 1.6 KiB |
|
Before Width: | Height: | Size: 462 B After Width: | Height: | Size: 3.6 KiB |
|
Before Width: | Height: | Size: 704 B After Width: | Height: | Size: 5.7 KiB |
|
Before Width: | Height: | Size: 406 B After Width: | Height: | Size: 2.3 KiB |
|
Before Width: | Height: | Size: 586 B After Width: | Height: | Size: 5.2 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 862 B After Width: | Height: | Size: 8.5 KiB |
|
Before Width: | Height: | Size: 1.6 KiB After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 762 B After Width: | Height: | Size: 5.0 KiB |
|
Before Width: | Height: | Size: 1.2 KiB After Width: | Height: | Size: 11 KiB |
|
Before Width: | Height: | Size: 1.4 KiB After Width: | Height: | Size: 13 KiB |
@@ -10,6 +10,7 @@ import '../../core/sacred_tokens.dart';
|
|||||||
import '../../providers.dart';
|
import '../../providers.dart';
|
||||||
import '../../data/services/sync_service.dart';
|
import '../../data/services/sync_service.dart';
|
||||||
import '../../data/services/myquran_service.dart';
|
import '../../data/services/myquran_service.dart';
|
||||||
|
import '../../data/services/sound_service.dart';
|
||||||
import 'package:file_picker/file_picker.dart';
|
import 'package:file_picker/file_picker.dart';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
@@ -120,7 +121,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
_simulasiFocusNodes = [
|
_simulasiFocusNodes = [
|
||||||
_simulasiEntryFocusNode,
|
_simulasiEntryFocusNode,
|
||||||
...List.generate(
|
...List.generate(
|
||||||
6,
|
8,
|
||||||
(index) => FocusNode(debugLabel: 'simulasi_row_${index + 1}'),
|
(index) => FocusNode(debugLabel: 'simulasi_row_${index + 1}'),
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
@@ -1005,16 +1006,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
setState(() => _selectedTab = index);
|
setState(() => _selectedTab = index);
|
||||||
}
|
}
|
||||||
|
|
||||||
void _traceNav(String message) {
|
|
||||||
assert(() {
|
|
||||||
debugPrint('[TV NAV] $message');
|
|
||||||
return true;
|
|
||||||
}());
|
|
||||||
}
|
|
||||||
|
|
||||||
void _focusNavTab(int index) {
|
void _focusNavTab(int index) {
|
||||||
if (index < 0 || index >= _navFocusNodes.length) return;
|
if (index < 0 || index >= _navFocusNodes.length) return;
|
||||||
_traceNav('focus nav[$index]');
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_navFocusNodes[index].requestFocus();
|
_navFocusNodes[index].requestFocus();
|
||||||
@@ -1025,7 +1018,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
void _focusIdentityRow(int index) {
|
void _focusIdentityRow(int index) {
|
||||||
if (_selectedTab != 0) return;
|
if (_selectedTab != 0) return;
|
||||||
if (index < 0 || index >= _identityFocusNodes.length) return;
|
if (index < 0 || index >= _identityFocusNodes.length) return;
|
||||||
_traceNav('focus identitas[$index]');
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_identityFocusNodes[index].requestFocus();
|
_identityFocusNodes[index].requestFocus();
|
||||||
@@ -1036,7 +1028,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
void _focusJumatRow(int index) {
|
void _focusJumatRow(int index) {
|
||||||
if (_selectedTab != 3) return;
|
if (_selectedTab != 3) return;
|
||||||
if (index < 0 || index >= _jumatFocusNodes.length) return;
|
if (index < 0 || index >= _jumatFocusNodes.length) return;
|
||||||
_traceNav('focus jumat[$index]');
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_jumatFocusNodes[index].requestFocus();
|
_jumatFocusNodes[index].requestFocus();
|
||||||
@@ -1047,7 +1038,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
void _focusSimulasiRow(int index) {
|
void _focusSimulasiRow(int index) {
|
||||||
if (_selectedTab != 4) return;
|
if (_selectedTab != 4) return;
|
||||||
if (index < 0 || index >= _simulasiFocusNodes.length) return;
|
if (index < 0 || index >= _simulasiFocusNodes.length) return;
|
||||||
_traceNav('focus simulasi[$index]');
|
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
if (mounted) {
|
if (mounted) {
|
||||||
_simulasiFocusNodes[index].requestFocus();
|
_simulasiFocusNodes[index].requestFocus();
|
||||||
@@ -1210,7 +1200,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
final key = event.logicalKey;
|
final key = event.logicalKey;
|
||||||
_traceNav('identitas[$index] key=$key');
|
|
||||||
if (key == LogicalKeyboardKey.arrowUp) {
|
if (key == LogicalKeyboardKey.arrowUp) {
|
||||||
_focusIdentityRow(index - 1);
|
_focusIdentityRow(index - 1);
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
@@ -1274,7 +1263,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
final key = event.logicalKey;
|
final key = event.logicalKey;
|
||||||
_traceNav('simulasi[$index] key=$key');
|
|
||||||
if (key == LogicalKeyboardKey.arrowUp) {
|
if (key == LogicalKeyboardKey.arrowUp) {
|
||||||
_focusSimulasiRow(index - 1);
|
_focusSimulasiRow(index - 1);
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
@@ -3393,6 +3381,18 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
rowIndex: 0,
|
rowIndex: 0,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16 * s),
|
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(
|
_simulasiCard(
|
||||||
s: s,
|
s: s,
|
||||||
title: 'Menuju Adzan',
|
title: 'Menuju Adzan',
|
||||||
@@ -3401,8 +3401,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('pre_adzan'),
|
() => _simulateEvent('pre_adzan'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[1],
|
focusNode: _simulasiFocusNodes[2],
|
||||||
rowIndex: 1,
|
rowIndex: 2,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16 * s),
|
SizedBox(height: 16 * s),
|
||||||
_simulasiCard(
|
_simulasiCard(
|
||||||
@@ -3413,8 +3413,20 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('adzan'),
|
() => _simulateEvent('adzan'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[2],
|
focusNode: _simulasiFocusNodes[3],
|
||||||
rowIndex: 2,
|
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),
|
SizedBox(height: 16 * s),
|
||||||
_simulasiCard(
|
_simulasiCard(
|
||||||
@@ -3425,8 +3437,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('iqomah'),
|
() => _simulateEvent('iqomah'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[3],
|
focusNode: _simulasiFocusNodes[5],
|
||||||
rowIndex: 3,
|
rowIndex: 5,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16 * s),
|
SizedBox(height: 16 * s),
|
||||||
_simulasiCard(
|
_simulasiCard(
|
||||||
@@ -3437,8 +3449,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('jumat_incoming'),
|
() => _simulateEvent('jumat_incoming'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[4],
|
focusNode: _simulasiFocusNodes[6],
|
||||||
rowIndex: 4,
|
rowIndex: 6,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16 * s),
|
SizedBox(height: 16 * s),
|
||||||
_simulasiCard(
|
_simulasiCard(
|
||||||
@@ -3449,8 +3461,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('jumat_khutbah'),
|
() => _simulateEvent('jumat_khutbah'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[5],
|
focusNode: _simulasiFocusNodes[7],
|
||||||
rowIndex: 5,
|
rowIndex: 7,
|
||||||
),
|
),
|
||||||
SizedBox(height: 16 * s),
|
SizedBox(height: 16 * s),
|
||||||
_simulasiCard(
|
_simulasiCard(
|
||||||
@@ -3461,8 +3473,8 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
onTap: () => _activateSimulation(
|
onTap: () => _activateSimulation(
|
||||||
() => _simulateEvent('shalat'),
|
() => _simulateEvent('shalat'),
|
||||||
),
|
),
|
||||||
focusNode: _simulasiFocusNodes[6],
|
focusNode: _simulasiFocusNodes[8],
|
||||||
rowIndex: 6,
|
rowIndex: 8,
|
||||||
),
|
),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
@@ -3598,12 +3610,21 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
DateTime targetTime;
|
DateTime targetTime;
|
||||||
|
|
||||||
switch (eventType) {
|
switch (eventType) {
|
||||||
|
case 'pre_adzan_15':
|
||||||
|
targetTime = dzuhurTime.subtract(const Duration(seconds: 15));
|
||||||
|
break;
|
||||||
case 'pre_adzan':
|
case 'pre_adzan':
|
||||||
targetTime = dzuhurTime.subtract(const Duration(minutes: 2));
|
targetTime = dzuhurTime.subtract(const Duration(minutes: 2));
|
||||||
break;
|
break;
|
||||||
case 'adzan':
|
case 'adzan':
|
||||||
targetTime = dzuhurTime;
|
targetTime = dzuhurTime;
|
||||||
break;
|
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':
|
case 'iqomah':
|
||||||
targetTime = dzuhurTime.add(const Duration(seconds: 45)); // During iqomah
|
targetTime = dzuhurTime.add(const Duration(seconds: 45)); // During iqomah
|
||||||
break;
|
break;
|
||||||
@@ -3630,6 +3651,17 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
|
|
||||||
final offset = targetTime.difference(realNow);
|
final offset = targetTime.difference(realNow);
|
||||||
_simulateTimeOffset(offset);
|
_simulateTimeOffset(offset);
|
||||||
|
|
||||||
|
switch (eventType) {
|
||||||
|
case 'adzan':
|
||||||
|
unawaited(SoundService.instance.playAdzanBeep());
|
||||||
|
break;
|
||||||
|
case 'iqomah':
|
||||||
|
unawaited(SoundService.instance.playIqomahCountdown());
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
break;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void _activateSimulation(VoidCallback action) {
|
void _activateSimulation(VoidCallback action) {
|
||||||
|
|||||||
|
Before Width: | Height: | Size: 101 KiB After Width: | Height: | Size: 144 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 9.0 KiB |
|
Before Width: | Height: | Size: 520 B After Width: | Height: | Size: 878 B |
|
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 21 KiB |
|
Before Width: | Height: | Size: 1.0 KiB After Width: | Height: | Size: 1.8 KiB |
|
Before Width: | Height: | Size: 36 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 2.2 KiB After Width: | Height: | Size: 4.0 KiB |
BIN
web/favicon.png
|
Before Width: | Height: | Size: 917 B After Width: | Height: | Size: 2.9 KiB |
|
Before Width: | Height: | Size: 5.2 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 8.1 KiB After Width: | Height: | Size: 20 KiB |
|
Before Width: | Height: | Size: 5.5 KiB After Width: | Height: | Size: 15 KiB |
|
Before Width: | Height: | Size: 20 KiB After Width: | Height: | Size: 20 KiB |