diff --git a/lib/main.dart b/lib/main.dart index 08c16e0e2..a791a2d69 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -28,6 +28,7 @@ import 'package:mawaqit/src/services/audio_manager.dart'; import 'package:mawaqit/src/services/FeatureManager.dart'; import 'package:mawaqit/src/services/mosque_manager.dart'; import 'package:mawaqit/src/services/theme_manager.dart'; +import 'package:mawaqit/src/services/toggle_screen_feature_manager.dart'; import 'package:mawaqit/src/services/user_preferences_manager.dart'; import 'package:media_kit/media_kit.dart'; import 'package:path_provider/path_provider.dart'; @@ -62,6 +63,8 @@ Future main() async { ], ), ); + await Future.delayed(const Duration(seconds: 5)); + await ToggleScreenFeature.restoreScheduledTimers(); }, ); } diff --git a/lib/src/data/data_source/random_hadith_remote_data_source.dart b/lib/src/data/data_source/random_hadith_remote_data_source.dart index 700db062f..981926e11 100644 --- a/lib/src/data/data_source/random_hadith_remote_data_source.dart +++ b/lib/src/data/data_source/random_hadith_remote_data_source.dart @@ -54,7 +54,7 @@ class RandomHadithRemoteDataSource { final document = XmlDocument.from(response.data)!; final hadithXmlList = document.getElements('hadith'); - log('random_hadith: RandomHadithRemoteDataSource: xml list ${hadithXmlList![0]}', time: DateTime.now()); + log('random_hadith: RandomHadithRemoteDataSource: xml list ${hadithXmlList![3]}', time: DateTime.now()); return hadithXmlList; }, debugName: 'random_hadith: getRandomHadithXML', diff --git a/lib/src/pages/SettingScreen.dart b/lib/src/pages/SettingScreen.dart index a3a5af004..77654bd4e 100644 --- a/lib/src/pages/SettingScreen.dart +++ b/lib/src/pages/SettingScreen.dart @@ -416,17 +416,18 @@ class _SettingScreenState extends ConsumerState { style: theme.textTheme.headlineSmall, textAlign: TextAlign.center, ), - _SettingItem( - title: S.of(context).screenLock, - subtitle: S.of(context).screenLockDesc, - icon: Icon(Icons.power_settings_new, size: 35), - onTap: () => showDialog( - context: context, - builder: (context) => ScreenLockModal( - timeShiftManager: timeShiftManager, + if (timeShiftManager.isLauncherInstalled) + _SettingItem( + title: S.of(context).screenLock, + subtitle: S.of(context).screenLockDesc, + icon: Icon(Icons.power_settings_new, size: 35), + onTap: () => showDialog( + context: context, + builder: (context) => ScreenLockModal( + timeShiftManager: timeShiftManager, + ), ), ), - ), _SettingItem( title: S.of(context).appTimezone, subtitle: S.of(context).descTimezone, diff --git a/lib/src/pages/home/widgets/FlashWidget.dart b/lib/src/pages/home/widgets/FlashWidget.dart index 5db8cdad4..4f31f6ad1 100644 --- a/lib/src/pages/home/widgets/FlashWidget.dart +++ b/lib/src/pages/home/widgets/FlashWidget.dart @@ -1,4 +1,5 @@ import 'package:flutter/material.dart'; +import 'package:google_fonts/google_fonts.dart'; import 'package:marquee/marquee.dart'; import 'package:mawaqit/i18n/AppLanguage.dart'; import 'package:mawaqit/src/helpers/RelativeSizes.dart'; @@ -42,6 +43,8 @@ class _FlashWidgetState extends State { velocity: 90, blankSpace: 400, style: TextStyle( + fontFamily: + flash.orientation == 'rtl' ? GoogleFonts.notoKufiArabic().fontFamily : GoogleFonts.roboto().fontFamily, height: 1, fontSize: 3.4.vwr, fontWeight: FontWeight.bold, diff --git a/lib/src/services/mosque_manager.dart b/lib/src/services/mosque_manager.dart index ceaa0df51..0e6eff6fe 100644 --- a/lib/src/services/mosque_manager.dart +++ b/lib/src/services/mosque_manager.dart @@ -11,6 +11,7 @@ import 'package:mawaqit/src/const/constants.dart'; import 'package:mawaqit/src/helpers/Api.dart'; import 'package:mawaqit/src/helpers/PerformanceHelper.dart'; import 'package:mawaqit/src/helpers/SharedPref.dart'; +import 'package:mawaqit/src/helpers/TimeShiftManager.dart'; import 'package:mawaqit/src/mawaqit_image/mawaqit_cache.dart'; import 'package:mawaqit/src/models/mosque.dart'; import 'package:mawaqit/src/models/mosqueConfig.dart'; @@ -216,29 +217,15 @@ class MosqueManager extends ChangeNotifier with WeatherMixin, AudioMixin, Mosque (e) { times = e; final today = useTomorrowTimes ? AppDateTime.tomorrow() : AppDateTime.now(); - if (isDeviceRooted) { - if (isToggleScreenActivated) { - ToggleScreenFeature.getLastEventDate().then((lastEventDate) async { - if (lastEventDate != null && lastEventDate.day != today.day) { - isEventsSet = false; // Reset the flag if it's a new day - await ToggleScreenFeature.cancelAllScheduledTimers(); - ToggleScreenFeature.toggleFeatureState(false); - ToggleScreenFeature.checkEventsScheduled().then((_) { - if (!isEventsSet) { - ToggleScreenFeature.scheduleToggleScreen( - isIshaFajrOnly, - e.dayTimesStrings(today, salahOnly: false), - minuteBefore, - minuteAfter, - ); - ToggleScreenFeature.toggleFeatureState(true); - ToggleScreenFeature.setLastEventDate(today); - isEventsSet = true; - } - }); - } - }); - } + final timeShiftManager = TimeShiftManager(); + + if (isDeviceRooted && isToggleScreenActivated && timeShiftManager.isLauncherInstalled) { + ToggleScreenFeature.handleDailyRescheduling( + isIshaFajrOnly: isIshaFajrOnly, + timeStrings: e.dayTimesStrings(today, salahOnly: false), + minuteBefore: minuteBefore, + minuteAfter: minuteAfter, + ); } notifyListeners(); diff --git a/lib/src/services/toggle_screen_feature_manager.dart b/lib/src/services/toggle_screen_feature_manager.dart index 7a0b1b7e8..673ad4678 100644 --- a/lib/src/services/toggle_screen_feature_manager.dart +++ b/lib/src/services/toggle_screen_feature_manager.dart @@ -8,6 +8,34 @@ import 'package:mawaqit/src/helpers/AppDate.dart'; import 'package:mawaqit/src/helpers/TimeShiftManager.dart'; import 'package:shared_preferences/shared_preferences.dart'; +class TimerScheduleInfo { + final DateTime scheduledTime; + final String actionType; // 'screenOn' or 'screenOff' + final bool isFajrIsha; + + TimerScheduleInfo({ + required this.scheduledTime, + required this.actionType, + required this.isFajrIsha, + }); + + Map toJson() { + return { + 'scheduledTime': scheduledTime.toIso8601String(), + 'actionType': actionType, + 'isFajrIsha': isFajrIsha, + }; + } + + factory TimerScheduleInfo.fromJson(Map json) { + return TimerScheduleInfo( + scheduledTime: DateTime.parse(json['scheduledTime']), + actionType: json['actionType'], + isFajrIsha: json['isFajrIsha'], + ); + } +} + class ToggleScreenFeature { static final ToggleScreenFeature _instance = ToggleScreenFeature._internal(); @@ -17,88 +45,208 @@ class ToggleScreenFeature { static const String _scheduledTimersKey = TurnOnOffTvConstant.kScheduledTimersKey; static final Map> _scheduledTimers = {}; + static const String _scheduledInfoKey = 'scheduled_info_key'; + static List _scheduleInfoList = []; static Future scheduleToggleScreen( bool isfajrIshaonly, List timeStrings, int beforeDelayMinutes, int afterDelayMinutes) async { + await cancelAllScheduledTimers(); + _scheduleInfoList.clear(); + + final now = AppDateTime.now(); final timeShiftManager = TimeShiftManager(); if (isfajrIshaonly) { - String fajrTime = timeStrings[0]; - List parts = fajrTime.split(':'); - int hour = int.parse(parts[0]); - int minute = int.parse(parts[1]); - - final now = AppDateTime.now(); - DateTime scheduledDateTime = DateTime(now.year, now.month, now.day, hour, minute); + _scheduleForPrayer( + timeStrings[0], + now, + beforeDelayMinutes, + afterDelayMinutes, + true, + timeShiftManager, + ); - if (scheduledDateTime.isBefore(now)) { - scheduledDateTime = scheduledDateTime.add(Duration(days: 1)); + _scheduleForPrayer( + timeStrings[5], + now, + beforeDelayMinutes, + afterDelayMinutes, + true, + timeShiftManager, + ); + } else { + for (String timeString in timeStrings) { + _scheduleForPrayer( + timeString, + now, + beforeDelayMinutes, + afterDelayMinutes, + false, + timeShiftManager, + ); } + } - final beforeDelay = scheduledDateTime.difference(now) - Duration(minutes: beforeDelayMinutes); + await Future.wait([saveScheduledEventsToLocale(), toggleFeatureState(true), setLastEventDate(now)]); + } - if (!beforeDelay.isNegative) { - final beforeTimer = Timer(beforeDelay, () { - timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOn() : _toggleTabletScreenOn(); - }); - _scheduledTimers[fajrTime] = [beforeTimer]; - } + static Future _saveScheduleInfo() async { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final scheduleData = _scheduleInfoList.map((info) => info.toJson()).toList(); + await prefs.setString(_scheduledInfoKey, json.encode(scheduleData)); + } + + static Future shouldReschedule() async { + final lastEventDate = await getLastEventDate(); + final today = AppDateTime.now(); + final isFeatureActive = await getToggleFeatureState(); + final areEventsScheduled = await checkEventsScheduled(); + + final shouldReschedule = + lastEventDate != null && lastEventDate.day != today.day && isFeatureActive && !areEventsScheduled; + return shouldReschedule; + } + + static Future handleDailyRescheduling({ + required bool isIshaFajrOnly, + required List timeStrings, + required int minuteBefore, + required int minuteAfter, + }) async { + final shouldSchedule = await shouldReschedule(); + + if (shouldSchedule) { + await cancelAllScheduledTimers(); + await toggleFeatureState(false); + + await scheduleToggleScreen( + isIshaFajrOnly, + timeStrings, + minuteBefore, + minuteAfter, + ); - String ishaTime = timeStrings[5]; - parts = ishaTime.split(':'); - hour = int.parse(parts[0]); - minute = int.parse(parts[1]); + await saveScheduledEventsToLocale(); + } + } - scheduledDateTime = DateTime(now.year, now.month, now.day, hour, minute); + static Future restoreScheduledTimers() async { + try { + final SharedPreferences prefs = await SharedPreferences.getInstance(); + final scheduleDataString = prefs.getString(_scheduledInfoKey); + final isFeatureActive = await getToggleFeatureState(); - if (scheduledDateTime.isBefore(now)) { - scheduledDateTime = scheduledDateTime.add(Duration(days: 1)); + if (scheduleDataString == null || !isFeatureActive) { + return; } - final afterDelay = scheduledDateTime.difference(now) + Duration(minutes: afterDelayMinutes); + final scheduleData = json.decode(scheduleDataString) as List; + _scheduleInfoList = scheduleData + .map((data) => TimerScheduleInfo.fromJson(data)) + .where((info) => info != null) // Filter out any null entries + .toList(); - final afterTimer = Timer(afterDelay, () { - timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOff() : _toggleTabletScreenOff(); - }); - _scheduledTimers[ishaTime] = [afterTimer]; - } else { - // Original logic for all prayer times - for (String timeString in timeStrings) { - final parts = timeString.split(':'); - final hour = int.parse(parts[0]); - final minute = int.parse(parts[1]); + final now = AppDateTime.now(); + final timeShiftManager = TimeShiftManager(); - final now = AppDateTime.now(); - DateTime scheduledDateTime = DateTime(now.year, now.month, now.day, hour, minute); + _scheduleInfoList.removeWhere((info) { + final isPast = info.scheduledTime.isBefore(now); + return isPast; + }); - if (scheduledDateTime.isBefore(now)) { - scheduledDateTime = scheduledDateTime.add(Duration(days: 1)); + for (var info in _scheduleInfoList) { + try { + final delay = info.scheduledTime.difference(now); + if (delay.isNegative) { + continue; + } + + final timer = Timer(delay, () { + try { + if (info.actionType == 'screenOn') { + timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOn() : _toggleTabletScreenOn(); + } else { + timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOff() : _toggleTabletScreenOff(); + } + } catch (e) { + print('Error executing timer action: $e'); + } + }); + + final timeString = '${info.scheduledTime.hour}:${info.scheduledTime.minute}'; + _scheduledTimers[timeString] ??= []; + _scheduledTimers[timeString]!.add(timer); + } catch (e) { + print('Error scheduling timer for ${info.scheduledTime}: $e'); } + } - final beforeDelay = scheduledDateTime.difference(now) - Duration(minutes: beforeDelayMinutes); - if (beforeDelay.isNegative) { - continue; - } - final beforeTimer = Timer(beforeDelay, () { - timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOn() : _toggleTabletScreenOn(); - }); + await saveScheduledEventsToLocale(); + } catch (e) { + print('Error restoring scheduled timers: $e'); + } + } - final afterDelay = scheduledDateTime.difference(now) + Duration(minutes: afterDelayMinutes); - final afterTimer = Timer(afterDelay, () { - timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOff() : _toggleTabletScreenOff(); - }); + static void _scheduleForPrayer( + String timeString, + DateTime now, + int beforeDelayMinutes, + int afterDelayMinutes, + bool isFajrIsha, + TimeShiftManager timeShiftManager, + ) { + final parts = timeString.split(':'); + final hour = int.parse(parts[0]); + final minute = int.parse(parts[1]); + + DateTime scheduledDateTime = DateTime(now.year, now.month, now.day, hour, minute); + if (scheduledDateTime.isBefore(now)) { + scheduledDateTime = scheduledDateTime.add(Duration(days: 1)); + } - _scheduledTimers[timeString] = [beforeTimer, afterTimer]; - } + // Schedule screen on + final beforeScheduleTime = scheduledDateTime.subtract(Duration(minutes: beforeDelayMinutes)); + if (beforeScheduleTime.isAfter(now)) { + _scheduleInfoList.add(TimerScheduleInfo( + scheduledTime: beforeScheduleTime, + actionType: 'screenOn', + isFajrIsha: isFajrIsha, + )); + + final beforeTimer = Timer(beforeScheduleTime.difference(now), () { + timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOn() : _toggleTabletScreenOn(); + }); + _scheduledTimers[timeString] = [beforeTimer]; + } else { + print('Screen ON time already passed: $beforeScheduleTime'); + } + + // Schedule screen off + final afterScheduleTime = scheduledDateTime.add(Duration(minutes: afterDelayMinutes)); + _scheduleInfoList.add(TimerScheduleInfo( + scheduledTime: afterScheduleTime, + actionType: 'screenOff', + isFajrIsha: isFajrIsha, + )); + + final afterTimer = Timer(afterScheduleTime.difference(now), () { + timeShiftManager.isLauncherInstalled ? _toggleBoxScreenOff() : _toggleTabletScreenOff(); + }); + + if (_scheduledTimers[timeString] != null) { + _scheduledTimers[timeString]!.add(afterTimer); + } else { + _scheduledTimers[timeString] = [afterTimer]; } - await _saveScheduledTimersToPrefs(); } static Future loadScheduledTimersFromPrefs() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); final timersJson = prefs.getString(_scheduledTimersKey); + if (timersJson != null) { final timersMap = json.decode(timersJson) as Map; + timersMap.forEach((timeString, timerDataList) { _scheduledTimers[timeString] = []; for (final timerData in timerDataList) { @@ -110,20 +258,6 @@ class ToggleScreenFeature { } } - static Future _saveScheduledTimersToPrefs() async { - final SharedPreferences prefs = await SharedPreferences.getInstance(); - - final timersMap = _scheduledTimers.map((timeString, timers) { - return MapEntry( - timeString, - timers.map((timer) { - return {'tick': timer.tick}; - }).toList(), - ); - }); - await prefs.setString(_scheduledTimersKey, json.encode(timersMap)); - } - static Future toggleFeatureState(bool isActive) async { final SharedPreferences prefs = await SharedPreferences.getInstance(); await prefs.setBool(TurnOnOffTvConstant.kActivateToggleFeature, isActive); @@ -131,24 +265,32 @@ class ToggleScreenFeature { static Future getToggleFeatureState() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getBool(TurnOnOffTvConstant.kActivateToggleFeature) ?? false; + final state = prefs.getBool(TurnOnOffTvConstant.kActivateToggleFeature) ?? false; + return state; } static Future getToggleFeatureishaFajrState() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - return prefs.getBool(TurnOnOffTvConstant.kisFajrIshaOnly) ?? false; + final state = prefs.getBool(TurnOnOffTvConstant.kisFajrIshaOnly) ?? false; + return state; } static Future cancelAllScheduledTimers() async { + int canceledTimers = 0; _scheduledTimers.forEach((timeString, timers) { for (final timer in timers) { timer.cancel(); + canceledTimers++; } }); + _scheduledTimers.clear(); + _scheduleInfoList.clear(); final SharedPreferences prefs = await SharedPreferences.getInstance(); - await prefs.remove(_scheduledTimersKey); + await prefs.remove(_scheduledInfoKey); + await prefs.remove(TurnOnOffTvConstant.kScheduledTimersKey); + await prefs.setBool(TurnOnOffTvConstant.kIsEventsSet, false); } static Future _toggleBoxScreenOn() async { @@ -189,13 +331,17 @@ class ToggleScreenFeature { static Future checkEventsScheduled() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); - logger.d("value${prefs.getBool("isEventsSet")}"); - return prefs.getBool(TurnOnOffTvConstant.kIsEventsSet) ?? false; + final isEventsSet = prefs.getBool(TurnOnOffTvConstant.kIsEventsSet) ?? false; + logger.d("value$isEventsSet"); + return isEventsSet; } static Future saveScheduledEventsToLocale() async { final SharedPreferences prefs = await SharedPreferences.getInstance(); + final scheduleData = _scheduleInfoList.map((info) => info.toJson()).toList(); + await prefs.setString(_scheduledInfoKey, json.encode(scheduleData)); + final Map timersMap = {}; _scheduledTimers.forEach((key, value) { timersMap[key] = value.map((timer) { @@ -207,8 +353,9 @@ class ToggleScreenFeature { }); await prefs.setString(TurnOnOffTvConstant.kScheduledTimersKey, json.encode(timersMap)); - logger.d("Saving into local"); await prefs.setBool(TurnOnOffTvConstant.kIsEventsSet, true); + + logger.d("Saving into local"); } static Future setLastEventDate(DateTime date) async { diff --git a/lib/src/state_management/quran/reading/quran_reading_notifer.dart b/lib/src/state_management/quran/reading/quran_reading_notifer.dart index 628b979b8..3786a92a0 100644 --- a/lib/src/state_management/quran/reading/quran_reading_notifer.dart +++ b/lib/src/state_management/quran/reading/quran_reading_notifer.dart @@ -77,11 +77,11 @@ class QuranReadingNotifier extends AutoDisposeAsyncNotifier { final currentState = state.value!; if (page >= 0 && page < currentState.totalPages) { await _saveLastReadPage(page); - final targetPage = isPortairt ? page : (page / 2).floor(); if (currentState.pageController.hasClients) { - // Calculate target page based on view mode - currentState.pageController.jumpToPage(targetPage); + !isPortairt + ? currentState.pageController.jumpToPage((page / 2).floor()) + : currentState.pageController.jumpToPage(page); } final newSurahName = _getCurrentSurahName(page, currentState.suwar); diff --git a/lib/src/state_management/screen_lock/screen_lock_notifier.dart b/lib/src/state_management/screen_lock/screen_lock_notifier.dart index 61a0fed17..e5925b8cc 100644 --- a/lib/src/state_management/screen_lock/screen_lock_notifier.dart +++ b/lib/src/state_management/screen_lock/screen_lock_notifier.dart @@ -23,7 +23,7 @@ class ScreenLockNotifier extends AsyncNotifier { DateTime.now().add(Duration(hours: timeShiftManager.shift, minutes: timeShiftManager.shiftInMinutes)), isfajrIshaonly: prefs.getBool(TurnOnOffTvConstant.kisFajrIshaOnly) ?? false, isActive: isActive, - selectedMinuteBefore: prefs.getInt(TurnOnOffTvConstant.kMinuteBeforeKey) ?? 10, + selectedMinuteBefore: prefs.getInt(TurnOnOffTvConstant.kMinuteBeforeKey) ?? 30, selectedMinuteAfter: prefs.getInt(TurnOnOffTvConstant.kMinuteAfterKey) ?? 30, ); } diff --git a/pubspec.yaml b/pubspec.yaml index 28e886bc5..8f7069b11 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -16,7 +16,8 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 1.18.2+1 + +version: 1.18.3+1 environment: sdk: ">=3.0.0 <4.0.0"