feat(tv-admin): fix action/focus flows, update app title, randomize unsplash, bump 1.0.9+10
This commit is contained in:
@@ -2,7 +2,7 @@
|
|||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES"/>
|
||||||
<application
|
<application
|
||||||
android:label="jamshalat_masjid_screen"
|
android:label="JamShalat - Masjid Screen"
|
||||||
android:name="${applicationName}"
|
android:name="${applicationName}"
|
||||||
android:icon="@mipmap/ic_launcher">
|
android:icon="@mipmap/ic_launcher">
|
||||||
<activity
|
<activity
|
||||||
|
|||||||
@@ -7,7 +7,7 @@
|
|||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Jamshalat Masjid Screen</string>
|
<string>JamShalat - Masjid Screen</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
@@ -15,7 +15,7 @@
|
|||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>jamshalat_masjid_screen</string>
|
<string>JamShalat - Masjid Screen</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -10,12 +10,22 @@ class UnsplashService {
|
|||||||
UnsplashService._();
|
UnsplashService._();
|
||||||
|
|
||||||
/// Fetches a list of highly compressed landscape URLs based on the given keyword.
|
/// Fetches a list of highly compressed landscape URLs based on the given keyword.
|
||||||
Future<List<String>> fetchLandscapeBackgrounds(String keyword) async {
|
Future<List<String>> fetchLandscapeBackgrounds(
|
||||||
|
String keyword, {
|
||||||
|
int page = 1,
|
||||||
|
}) async {
|
||||||
// Trim keyword and default to 'mosque' if empty
|
// Trim keyword and default to 'mosque' if empty
|
||||||
final query = keyword.trim().isEmpty ? 'mosque' : keyword.trim();
|
final query = keyword.trim().isEmpty ? 'mosque' : keyword.trim();
|
||||||
|
|
||||||
// Specifically requesting 'regular' size to fit 1080p elegantly while minimizing RAM overhead.
|
// Specifically requesting 'regular' size to fit 1080p elegantly while minimizing RAM overhead.
|
||||||
final url = Uri.parse('$_baseUrl/search/photos?query=$query&orientation=landscape&per_page=20');
|
final url = Uri.parse('$_baseUrl/search/photos').replace(
|
||||||
|
queryParameters: {
|
||||||
|
'query': query,
|
||||||
|
'orientation': 'landscape',
|
||||||
|
'per_page': '20',
|
||||||
|
'page': page.clamp(1, 1000).toString(),
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
final response = await http.get(
|
final response = await http.get(
|
||||||
|
|||||||
@@ -344,6 +344,40 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<void> _pickSlideshowImages() async {
|
||||||
|
try {
|
||||||
|
final res = await FilePicker.platform.pickFiles(
|
||||||
|
type: FileType.image,
|
||||||
|
allowMultiple: true,
|
||||||
|
);
|
||||||
|
if (res == null) return;
|
||||||
|
|
||||||
|
var hasNewImage = false;
|
||||||
|
setState(() {
|
||||||
|
for (final path in res.paths) {
|
||||||
|
if (path != null &&
|
||||||
|
File(path).existsSync() &&
|
||||||
|
!_slideshowImages.contains(path)) {
|
||||||
|
_slideshowImages.add(path);
|
||||||
|
hasNewImage = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
if (hasNewImage) {
|
||||||
|
_queueTampilanAutoSave(
|
||||||
|
message: 'Galeri slideshow otomatis tersimpan',
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!mounted) return;
|
||||||
|
_showStatusBadge(
|
||||||
|
'Gagal membuka pemilih file. Pastikan file manager tersedia di perangkat.',
|
||||||
|
isError: true,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Future<void> _savePengumuman({
|
Future<void> _savePengumuman({
|
||||||
String message = 'Pengaturan pengumuman otomatis tersimpan',
|
String message = 'Pengaturan pengumuman otomatis tersimpan',
|
||||||
}) async {
|
}) async {
|
||||||
@@ -1270,7 +1304,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
count += 1;
|
count += 1;
|
||||||
count += 1;
|
count += 1;
|
||||||
count += _slideshowImages.length;
|
count += _slideshowImages.length;
|
||||||
count += 1;
|
|
||||||
return count;
|
return count;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1375,9 +1408,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
_focusNavTab(index + 1);
|
_focusNavTab(index + 1);
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.arrowRight ||
|
if (key == LogicalKeyboardKey.arrowRight || _isActivateKey(key)) {
|
||||||
key == LogicalKeyboardKey.select ||
|
|
||||||
key == LogicalKeyboardKey.enter) {
|
|
||||||
_focusEntryForTab(index);
|
_focusEntryForTab(index);
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1410,7 +1441,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1453,7 +1484,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1485,7 +1516,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1517,7 +1548,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1548,7 +1579,7 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
@@ -1579,13 +1610,21 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
if (key == LogicalKeyboardKey.arrowRight) {
|
if (key == LogicalKeyboardKey.arrowRight) {
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
if (key == LogicalKeyboardKey.select || key == LogicalKeyboardKey.enter) {
|
if (_isActivateKey(key)) {
|
||||||
onActivate();
|
onActivate();
|
||||||
return KeyEventResult.handled;
|
return KeyEventResult.handled;
|
||||||
}
|
}
|
||||||
return KeyEventResult.ignored;
|
return KeyEventResult.ignored;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _isActivateKey(LogicalKeyboardKey key) {
|
||||||
|
return key == LogicalKeyboardKey.enter ||
|
||||||
|
key == LogicalKeyboardKey.select ||
|
||||||
|
key == LogicalKeyboardKey.numpadEnter ||
|
||||||
|
key == LogicalKeyboardKey.space ||
|
||||||
|
key == LogicalKeyboardKey.gameButtonA;
|
||||||
|
}
|
||||||
|
|
||||||
Widget _buildJumatTab(double s) {
|
Widget _buildJumatTab(double s) {
|
||||||
return FocusTraversalGroup(
|
return FocusTraversalGroup(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderTraversalPolicy(),
|
||||||
@@ -1743,7 +1782,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
_slideshowImages.length,
|
_slideshowImages.length,
|
||||||
(_) => row++,
|
(_) => row++,
|
||||||
);
|
);
|
||||||
final openPengumumanRow = row++;
|
|
||||||
|
|
||||||
return FocusTraversalGroup(
|
return FocusTraversalGroup(
|
||||||
policy: WidgetOrderTraversalPolicy(),
|
policy: WidgetOrderTraversalPolicy(),
|
||||||
@@ -2050,41 +2088,9 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
_buildTampilanActionButton(
|
_buildTampilanActionButton(
|
||||||
rowIndex: addSlideshowImageRow,
|
rowIndex: addSlideshowImageRow,
|
||||||
s: s,
|
s: s,
|
||||||
onActivate: () async {
|
onActivate: _pickSlideshowImages,
|
||||||
final res = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
|
|
||||||
if (res != null) {
|
|
||||||
setState(() {
|
|
||||||
for (var path in res.paths) {
|
|
||||||
if (path != null &&
|
|
||||||
File(path).existsSync() &&
|
|
||||||
!_slideshowImages.contains(path)) {
|
|
||||||
_slideshowImages.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_queueTampilanAutoSave(
|
|
||||||
message: 'Galeri slideshow otomatis tersimpan',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
child: ElevatedButton.icon(
|
child: ElevatedButton.icon(
|
||||||
onPressed: () async {
|
onPressed: _pickSlideshowImages,
|
||||||
final res = await FilePicker.platform.pickFiles(type: FileType.image, allowMultiple: true);
|
|
||||||
if (res != null) {
|
|
||||||
setState(() {
|
|
||||||
for (var path in res.paths) {
|
|
||||||
if (path != null &&
|
|
||||||
File(path).existsSync() &&
|
|
||||||
!_slideshowImages.contains(path)) {
|
|
||||||
_slideshowImages.add(path);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
_queueTampilanAutoSave(
|
|
||||||
message: 'Galeri slideshow otomatis tersimpan',
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
icon: HugeIcon(icon: HugeIcons.strokeRoundedPlusSign, color: SacredColors.onPrimary, size: 18 * s),
|
icon: HugeIcon(icon: HugeIcons.strokeRoundedPlusSign, color: SacredColors.onPrimary, size: 18 * s),
|
||||||
label: Text('TAMBAH FOTO', style: TextStyle(fontSize: 14 * s)),
|
label: Text('TAMBAH FOTO', style: TextStyle(fontSize: 14 * s)),
|
||||||
style: _tvElevatedActionStyle(
|
style: _tvElevatedActionStyle(
|
||||||
@@ -2180,58 +2186,6 @@ class _AdminScreenState extends ConsumerState<AdminScreen> {
|
|||||||
],
|
],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
SizedBox(height: 24 * s),
|
|
||||||
_adminCard(
|
|
||||||
s,
|
|
||||||
child: Column(
|
|
||||||
crossAxisAlignment: CrossAxisAlignment.start,
|
|
||||||
children: [
|
|
||||||
_sectionLabel('Pengumuman Dipisah ke Tab Sendiri', s),
|
|
||||||
SizedBox(height: 8 * s),
|
|
||||||
Text(
|
|
||||||
'Text slide tengah dan running text bawah sekarang dipindahkan ke tab Pengumuman agar halaman Tampilan & Media lebih ringkas.',
|
|
||||||
style: GoogleFonts.manrope(
|
|
||||||
fontSize: 14 * s,
|
|
||||||
color: SacredColors.onSurfaceVariant,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 16 * s),
|
|
||||||
_buildTampilanActionButton(
|
|
||||||
rowIndex: openPengumumanRow,
|
|
||||||
s: s,
|
|
||||||
onActivate: () {
|
|
||||||
_setSelectedTab(3);
|
|
||||||
_focusEntryForTab(3);
|
|
||||||
},
|
|
||||||
child: ElevatedButton.icon(
|
|
||||||
onPressed: () {
|
|
||||||
_setSelectedTab(3);
|
|
||||||
_focusEntryForTab(3);
|
|
||||||
},
|
|
||||||
icon: HugeIcon(
|
|
||||||
icon: HugeIcons.strokeRoundedNotification03,
|
|
||||||
color: SacredColors.onPrimary,
|
|
||||||
size: 18 * s,
|
|
||||||
),
|
|
||||||
label: Text(
|
|
||||||
'BUKA TAB PENGUMUMAN',
|
|
||||||
style: TextStyle(fontSize: 14 * s),
|
|
||||||
),
|
|
||||||
style: _tvElevatedActionStyle(
|
|
||||||
s: s,
|
|
||||||
normalBackground: SacredColors.secondary,
|
|
||||||
normalForeground: SacredColors.onPrimary,
|
|
||||||
padding: EdgeInsets.symmetric(
|
|
||||||
horizontal: 20 * s,
|
|
||||||
vertical: 14 * s,
|
|
||||||
),
|
|
||||||
fontSize: 14 * s,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
],
|
|
||||||
),
|
|
||||||
),
|
|
||||||
SizedBox(height: 40 * s),
|
SizedBox(height: 40 * s),
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
|
import 'dart:math';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||||
|
|
||||||
@@ -14,11 +15,15 @@ class UnsplashBackground extends ConsumerStatefulWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class _UnsplashBackgroundState extends ConsumerState<UnsplashBackground> {
|
class _UnsplashBackgroundState extends ConsumerState<UnsplashBackground> {
|
||||||
|
final Random _rng = Random();
|
||||||
List<String> _urls = [];
|
List<String> _urls = [];
|
||||||
int _currentIndex = 0;
|
int _currentIndex = 0;
|
||||||
Timer? _rotationTimer;
|
Timer? _rotationTimer;
|
||||||
|
Timer? _keywordDebounceTimer;
|
||||||
|
int _fetchNonce = 0;
|
||||||
String? _lastKeyword;
|
String? _lastKeyword;
|
||||||
int? _lastRotationHours;
|
int? _lastRotationHours;
|
||||||
|
bool? _lastUseUnsplash;
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void initState() {
|
void initState() {
|
||||||
@@ -30,18 +35,55 @@ class _UnsplashBackgroundState extends ConsumerState<UnsplashBackground> {
|
|||||||
final settings = ref.read(settingsProvider);
|
final settings = ref.read(settingsProvider);
|
||||||
_lastKeyword = settings.unsplashKeyword;
|
_lastKeyword = settings.unsplashKeyword;
|
||||||
_lastRotationHours = settings.unsplashRotationHours;
|
_lastRotationHours = settings.unsplashRotationHours;
|
||||||
|
_lastUseUnsplash = settings.useUnsplashBackground;
|
||||||
|
|
||||||
await _fetchImages(settings.unsplashKeyword);
|
if (settings.useUnsplashBackground) {
|
||||||
|
await _fetchImages(settings.unsplashKeyword, immediate: true);
|
||||||
|
}
|
||||||
_startTimer(settings.unsplashRotationHours);
|
_startTimer(settings.unsplashRotationHours);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> _fetchImages(String keyword) async {
|
Future<void> _fetchImages(String keyword, {bool immediate = false}) async {
|
||||||
final urls = await UnsplashService.instance.fetchLandscapeBackgrounds(keyword);
|
_keywordDebounceTimer?.cancel();
|
||||||
if (urls.isNotEmpty && mounted) {
|
Future<void> runFetch() async {
|
||||||
setState(() {
|
if (!ref.read(settingsProvider).useUnsplashBackground) return;
|
||||||
_urls = urls;
|
final requestId = ++_fetchNonce;
|
||||||
_currentIndex = 0;
|
final randomPage = 1 + _rng.nextInt(10);
|
||||||
});
|
final urls = await UnsplashService.instance.fetchLandscapeBackgrounds(
|
||||||
|
keyword,
|
||||||
|
page: randomPage,
|
||||||
|
);
|
||||||
|
if (!mounted || requestId != _fetchNonce) return;
|
||||||
|
if (urls.isNotEmpty) {
|
||||||
|
urls.shuffle(_rng);
|
||||||
|
setState(() {
|
||||||
|
_urls = urls;
|
||||||
|
_currentIndex = _rng.nextInt(_urls.length);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
await runFetch();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
_keywordDebounceTimer = Timer(
|
||||||
|
const Duration(milliseconds: 1200),
|
||||||
|
() {
|
||||||
|
unawaited(runFetch());
|
||||||
|
},
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
void _nextRandomImage() {
|
||||||
|
if (_urls.isNotEmpty && mounted) {
|
||||||
|
if (_urls.length <= 1) return;
|
||||||
|
var nextIndex = _currentIndex;
|
||||||
|
while (nextIndex == _currentIndex) {
|
||||||
|
nextIndex = _rng.nextInt(_urls.length);
|
||||||
|
}
|
||||||
|
setState(() => _currentIndex = nextIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -50,17 +92,14 @@ class _UnsplashBackgroundState extends ConsumerState<UnsplashBackground> {
|
|||||||
if (hours <= 0) return;
|
if (hours <= 0) return;
|
||||||
|
|
||||||
_rotationTimer = Timer.periodic(Duration(hours: hours), (_) {
|
_rotationTimer = Timer.periodic(Duration(hours: hours), (_) {
|
||||||
if (_urls.isNotEmpty && mounted) {
|
_nextRandomImage();
|
||||||
setState(() {
|
|
||||||
_currentIndex = (_currentIndex + 1) % _urls.length;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
_rotationTimer?.cancel();
|
_rotationTimer?.cancel();
|
||||||
|
_keywordDebounceTimer?.cancel();
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -69,10 +108,18 @@ class _UnsplashBackgroundState extends ConsumerState<UnsplashBackground> {
|
|||||||
final settings = ref.watch(settingsProvider);
|
final settings = ref.watch(settingsProvider);
|
||||||
|
|
||||||
// Watch for config changes
|
// Watch for config changes
|
||||||
|
if (settings.useUnsplashBackground != _lastUseUnsplash) {
|
||||||
|
_lastUseUnsplash = settings.useUnsplashBackground;
|
||||||
|
if (settings.useUnsplashBackground && _urls.isEmpty) {
|
||||||
|
_fetchImages(settings.unsplashKeyword, immediate: true);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (settings.unsplashKeyword != _lastKeyword) {
|
if (settings.unsplashKeyword != _lastKeyword) {
|
||||||
_lastKeyword = settings.unsplashKeyword;
|
_lastKeyword = settings.unsplashKeyword;
|
||||||
// Re-fetch images organically
|
if (settings.useUnsplashBackground) {
|
||||||
_fetchImages(settings.unsplashKeyword);
|
_fetchImages(settings.unsplashKeyword);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (settings.unsplashRotationHours != _lastRotationHours) {
|
if (settings.unsplashRotationHours != _lastRotationHours) {
|
||||||
|
|||||||
@@ -125,7 +125,7 @@ class JamShalatApp extends ConsumerWidget {
|
|||||||
// textScaleProvider will be used selectively in child components.
|
// textScaleProvider will be used selectively in child components.
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
title: 'Jam Shalat Digital',
|
title: 'JamShalat - Masjid Screen',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
theme: ThemeData(
|
theme: ThemeData(
|
||||||
useMaterial3: true,
|
useMaterial3: true,
|
||||||
|
|||||||
@@ -45,11 +45,11 @@ static void my_application_activate(GApplication* application) {
|
|||||||
if (use_header_bar) {
|
if (use_header_bar) {
|
||||||
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
GtkHeaderBar* header_bar = GTK_HEADER_BAR(gtk_header_bar_new());
|
||||||
gtk_widget_show(GTK_WIDGET(header_bar));
|
gtk_widget_show(GTK_WIDGET(header_bar));
|
||||||
gtk_header_bar_set_title(header_bar, "jamshalat_masjid_screen");
|
gtk_header_bar_set_title(header_bar, "JamShalat - Masjid Screen");
|
||||||
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
gtk_header_bar_set_show_close_button(header_bar, TRUE);
|
||||||
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
gtk_window_set_titlebar(window, GTK_WIDGET(header_bar));
|
||||||
} else {
|
} else {
|
||||||
gtk_window_set_title(window, "jamshalat_masjid_screen");
|
gtk_window_set_title(window, "JamShalat - Masjid Screen");
|
||||||
}
|
}
|
||||||
|
|
||||||
gtk_window_set_default_size(window, 1280, 720);
|
gtk_window_set_default_size(window, 1280, 720);
|
||||||
|
|||||||
@@ -12,8 +12,10 @@
|
|||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
|
<key>CFBundleDisplayName</key>
|
||||||
|
<string>JamShalat - Masjid Screen</string>
|
||||||
<key>CFBundleName</key>
|
<key>CFBundleName</key>
|
||||||
<string>$(PRODUCT_NAME)</string>
|
<string>JamShalat - Masjid Screen</string>
|
||||||
<key>CFBundlePackageType</key>
|
<key>CFBundlePackageType</key>
|
||||||
<string>APPL</string>
|
<string>APPL</string>
|
||||||
<key>CFBundleShortVersionString</key>
|
<key>CFBundleShortVersionString</key>
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
name: jamshalat_masjid_screen
|
name: jamshalat_masjid_screen
|
||||||
description: Smart Digital Prayer Clock for Android TV Box
|
description: Smart Digital Prayer Clock for Android TV Box
|
||||||
publish_to: 'none'
|
publish_to: 'none'
|
||||||
version: 1.0.8+9
|
version: 1.0.9+10
|
||||||
|
|
||||||
environment:
|
environment:
|
||||||
sdk: '>=3.0.0 <4.0.0'
|
sdk: '>=3.0.0 <4.0.0'
|
||||||
|
|||||||
@@ -23,13 +23,13 @@
|
|||||||
<!-- iOS meta tags & icons -->
|
<!-- iOS meta tags & icons -->
|
||||||
<meta name="mobile-web-app-capable" content="yes">
|
<meta name="mobile-web-app-capable" content="yes">
|
||||||
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
<meta name="apple-mobile-web-app-status-bar-style" content="black">
|
||||||
<meta name="apple-mobile-web-app-title" content="jamshalat_masjid_screen">
|
<meta name="apple-mobile-web-app-title" content="JamShalat - Masjid Screen">
|
||||||
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
<link rel="apple-touch-icon" href="icons/Icon-192.png">
|
||||||
|
|
||||||
<!-- Favicon -->
|
<!-- Favicon -->
|
||||||
<link rel="icon" type="image/png" href="favicon.png"/>
|
<link rel="icon" type="image/png" href="favicon.png"/>
|
||||||
|
|
||||||
<title>jamshalat_masjid_screen</title>
|
<title>JamShalat - Masjid Screen</title>
|
||||||
<link rel="manifest" href="manifest.json">
|
<link rel="manifest" href="manifest.json">
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "jamshalat_masjid_screen",
|
"name": "JamShalat - Masjid Screen",
|
||||||
"short_name": "jamshalat_masjid_screen",
|
"short_name": "JamShalat",
|
||||||
"start_url": ".",
|
"start_url": ".",
|
||||||
"display": "standalone",
|
"display": "standalone",
|
||||||
"background_color": "#0175C2",
|
"background_color": "#0175C2",
|
||||||
|
|||||||
@@ -90,12 +90,12 @@ BEGIN
|
|||||||
BLOCK "040904e4"
|
BLOCK "040904e4"
|
||||||
BEGIN
|
BEGIN
|
||||||
VALUE "CompanyName", "com.jamshalat" "\0"
|
VALUE "CompanyName", "com.jamshalat" "\0"
|
||||||
VALUE "FileDescription", "jamshalat_masjid_screen" "\0"
|
VALUE "FileDescription", "JamShalat - Masjid Screen" "\0"
|
||||||
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
VALUE "FileVersion", VERSION_AS_STRING "\0"
|
||||||
VALUE "InternalName", "jamshalat_masjid_screen" "\0"
|
VALUE "InternalName", "jamshalat_masjid_screen" "\0"
|
||||||
VALUE "LegalCopyright", "Copyright (C) 2026 com.jamshalat. All rights reserved." "\0"
|
VALUE "LegalCopyright", "Copyright (C) 2026 com.jamshalat. All rights reserved." "\0"
|
||||||
VALUE "OriginalFilename", "jamshalat_masjid_screen.exe" "\0"
|
VALUE "OriginalFilename", "jamshalat_masjid_screen.exe" "\0"
|
||||||
VALUE "ProductName", "jamshalat_masjid_screen" "\0"
|
VALUE "ProductName", "JamShalat - Masjid Screen" "\0"
|
||||||
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
VALUE "ProductVersion", VERSION_AS_STRING "\0"
|
||||||
END
|
END
|
||||||
END
|
END
|
||||||
|
|||||||
@@ -27,7 +27,7 @@ int APIENTRY wWinMain(_In_ HINSTANCE instance, _In_opt_ HINSTANCE prev,
|
|||||||
FlutterWindow window(project);
|
FlutterWindow window(project);
|
||||||
Win32Window::Point origin(10, 10);
|
Win32Window::Point origin(10, 10);
|
||||||
Win32Window::Size size(1280, 720);
|
Win32Window::Size size(1280, 720);
|
||||||
if (!window.Create(L"jamshalat_masjid_screen", origin, size)) {
|
if (!window.Create(L"JamShalat - Masjid Screen", origin, size)) {
|
||||||
return EXIT_FAILURE;
|
return EXIT_FAILURE;
|
||||||
}
|
}
|
||||||
window.SetQuitOnClose(true);
|
window.SetQuitOnClose(true);
|
||||||
|
|||||||
Reference in New Issue
Block a user