diff --git a/android/app/build.gradle b/android/app/build.gradle
index f055702..c31d5ad 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -7,7 +7,7 @@ plugins {
android {
namespace = "com.example.hmg_qline.hmg_qline"
- compileSdk = flutter.compileSdkVersion
+ compileSdk = 35
ndkVersion = "27.0.12077973"
compileOptions {
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index a25330d..f4b26f2 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -3,6 +3,7 @@
+
diff --git a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootForegroundService.kt b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootForegroundService.kt
index 6b8591e..bafb989 100644
--- a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootForegroundService.kt
+++ b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootForegroundService.kt
@@ -1,19 +1,24 @@
package com.example.hmg_qline.hmg_qline
+import android.app.AlarmManager
import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
+import android.app.PendingIntent
+import android.content.Context
import android.content.Intent
import android.os.Build
import android.util.Log
import androidx.core.app.NotificationCompat
import androidx.lifecycle.LifecycleService
+import android.app.ActivityManager
class BootForegroundService : LifecycleService() {
override fun onCreate() {
super.onCreate()
startForegroundService()
+ schedulePeriodicCheck()
}
private fun startForegroundService() {
@@ -30,18 +35,73 @@ class BootForegroundService : LifecycleService() {
val notification: Notification = NotificationCompat.Builder(this, channelId)
.setContentTitle("QLine App")
- .setContentText("Launching application...")
+ .setContentText("Monitoring QLine activity...")
.setSmallIcon(R.mipmap.ic_launcher)
.build()
startForeground(1, notification)
- // Launch MainActivity
- Log.d("BootForegroundService", "Starting MainActivity")
- val intent = Intent(this, MainActivity::class.java)
- intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- startActivity(intent)
+ val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ val runningProcesses = activityManager.runningAppProcesses
- stopSelf() // Stop the service after launching the app
+ var isAppInForeground = false
+ for (process in runningProcesses) {
+ if (process.processName == packageName &&
+ process.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+ ) {
+ isAppInForeground = true
+ break
+ }
+ }
+
+ if (!isAppInForeground) {
+ Log.d("BootForegroundService", "App is NOT in foreground — launching MainActivity.")
+ val intent = Intent(this, MainActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+ startActivity(intent)
+ } else {
+ Log.d("BootForegroundService", "App is already in foreground.")
+ }
+
+ stopSelf() // Stop the service after check
+ }
+
+
+ private fun schedulePeriodicCheck() {
+ val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val intent = Intent(this, BootForegroundService::class.java)
+ val pendingIntent = PendingIntent.getService(
+ this,
+ 0,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+ // Repeat every 1 minute
+ alarmManager.setRepeating(
+ AlarmManager.RTC_WAKEUP,
+ System.currentTimeMillis() + 60 * 1000,
+ 60 * 1000,
+ pendingIntent
+ )
+
+ }
+
+ private fun bringAppToFrontIfNotVisible() {
+ val activityManager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager
+ val runningProcesses = activityManager.runningAppProcesses
+
+ val isAppInForeground = runningProcesses.any {
+ it.processName == packageName && it.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND
+ }
+
+ if (!isAppInForeground) {
+ Log.d("BootForegroundService", "App is not in foreground, launching MainActivity")
+ val intent = Intent(this, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ startActivity(intent)
+ } else {
+ Log.d("BootForegroundService", "App is already in foreground")
+ }
}
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/MainActivity.kt b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/MainActivity.kt
index 79542b6..400406c 100644
--- a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/MainActivity.kt
+++ b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/MainActivity.kt
@@ -1,5 +1,27 @@
package com.example.hmg_qline.hmg_qline
+import android.content.Intent
+import android.os.Bundle
+import android.util.Log
import io.flutter.embedding.android.FlutterActivity
+import io.flutter.embedding.engine.FlutterEngine
+import io.flutter.plugin.common.MethodChannel
-class MainActivity : FlutterActivity() {}
\ No newline at end of file
+class MainActivity : FlutterActivity() {
+ private val CHANNEL = "com.example.hmg_qline/foreground"
+
+ override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
+ super.configureFlutterEngine(flutterEngine)
+
+ MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler {
+ call, _ ->
+ Log.d("MainActivity", "MethodChannel call received: ${call.method}")
+ if (call.method == "reopenApp") {
+ Log.d("MainActivity", "reopenApp called, launching MainActivity")
+ val intent = Intent(this, MainActivity::class.java)
+ intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_SINGLE_TOP)
+ startActivity(intent)
+ }
+ }
+ }
+}
diff --git a/lib/config/dependency_injection.dart b/lib/config/dependency_injection.dart
index 140dae5..0572e41 100644
--- a/lib/config/dependency_injection.dart
+++ b/lib/config/dependency_injection.dart
@@ -2,6 +2,7 @@
// import 'package:flutter/material.dart';
import 'package:connectivity_plus/connectivity_plus.dart';
+import 'package:flutter/services.dart';
import 'package:flutter_tts/flutter_tts.dart';
import 'package:get_it/get_it.dart';
import 'package:hmg_qline/api/api_client.dart';
@@ -12,6 +13,7 @@ import 'package:hmg_qline/services/cache_service.dart';
import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/services/text_to_speech_service.dart';
+import 'package:hmg_qline/utilities/native_method_handler.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/view_models/screen_config_view_model.dart';
import 'package:just_audio/just_audio.dart';
@@ -39,8 +41,10 @@ class AppDependencies {
getIt.registerSingleton(SignalrRepoImp(loggerService: getIt.get()));
getIt.registerSingleton(ScreenDetailsRepoImp(apiClientInstance: getIt.get(), loggerService: getIt.get()));
- //ThirdPartyServices
+ //repos
+ getIt.registerSingleton(NativeMethodChannelServiceImp(loggerService: getIt.get(), platform: const MethodChannel('com.example.hmg_qline/foreground')));
+ //ThirdPartyServices
getIt.registerSingleton(ConnectivityServiceImp(connectivityInstance: Connectivity()));
getIt.registerSingleton(CacheServiceImp(preferencesInstance: await SharedPreferences.getInstance()));
getIt.registerSingleton(AudioServiceImp(audioPlayerInstance: AudioPlayer()));
@@ -54,6 +58,7 @@ class AppDependencies {
cacheService: getIt.get(),
connectivityService: getIt.get(),
loggerService: getIt.get(),
+ nativeMethodChannelService: getIt.get(),
),
);
diff --git a/lib/constants/app_constants.dart b/lib/constants/app_constants.dart
index ccf3c2b..41a286c 100644
--- a/lib/constants/app_constants.dart
+++ b/lib/constants/app_constants.dart
@@ -23,6 +23,9 @@ class AppStrings {
static String dataLogsFileName = "data_logs.txt";
static String errorLogsFileName = "error_logs.txt";
+
+
+ static String openAppNativeFunctionName = "reopenApp";
}
class AppColors {
@@ -104,7 +107,7 @@ class AppConstants {
static String testIP = '12.4.5.1'; // projectID.QlineType.ScreenType.AnyNumber (1 to 10)
static int thresholdForListUI = 3;
- static int currentBuildVersion = 7;
+ static double currentBuildVersion = 8.1;
}
class ApiConstants {
diff --git a/lib/main.dart b/lib/main.dart
index 58178f6..0b99299 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -9,11 +9,58 @@ import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/views/view_helpers/size_config.dart';
import 'package:wakelock_plus/wakelock_plus.dart';
-
void main() async {
WidgetsFlutterBinding.ensureInitialized();
await AppDependencies.addDependencies();
- WakelockPlus.enable();
+ // Keep the screen on
+ await WakelockPlus.enable();
+
+ // The following foreground task logic is now handled natively in Android. Commented out for clarity.
+ /*
+ void _initializeForegroundTask() {
+ FlutterForegroundTask.init(
+ androidNotificationOptions: AndroidNotificationOptions(
+ channelId: 'foreground_service',
+ channelName: AppStrings.appName,
+ channelDescription: '',
+ onlyAlertOnce: true,
+ ),
+ iosNotificationOptions: const IOSNotificationOptions(
+ showNotification: false,
+ playSound: false,
+ ),
+ foregroundTaskOptions: ForegroundTaskOptions(
+ eventAction: ForegroundTaskEventAction.repeat(5000),
+ autoRunOnBoot: true,
+ autoRunOnMyPackageReplaced: true,
+ allowWakeLock: true,
+ allowWifiLock: true,
+ ),
+ );
+ }
+
+ Future _startForegroundService() async {
+ await FlutterForegroundTask.startService(
+ notificationTitle: AppStrings.appName,
+ notificationText: 'App is running in foreground',
+ callback: startCallback,
+ );
+ }
+
+ // Initialize foreground task first
+ _initializeForegroundTask();
+
+ // Register lifecycle callback (e.g., reopen app on detach)
+ LifecycleHandler(
+ onDetached: () {
+ getIt().reopenApp();
+ },
+ ).register();
+
+ // Start foreground service AFTER initialization
+ await _startForegroundService();
+ */
+
runApp(const MyApp());
}
@@ -26,26 +73,26 @@ class MyApp extends StatelessWidget {
builder: (context, constraints) {
return OrientationBuilder(builder: (context, orientation) {
SizeConfig().init(constraints, orientation);
- // SystemChrome.setPreferredOrientations([DeviceOrientation.portraitDown]);
SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []);
return MultiProvider(
- providers: [
- ChangeNotifierProvider(create: (context) => getIt.get()),
- ChangeNotifierProvider(create: (context) => getIt.get()),
- ],
- child: MaterialApp(
- showSemanticsDebugger: false,
- title: AppStrings.appName,
- theme: ThemeData(
- fontFamily: AppStrings.fontNamePoppins,
- colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey).copyWith(
- surface: const Color.fromRGBO(255, 255, 255, 1),
- ),
+ providers: [
+ ChangeNotifierProvider(create: (context) => getIt.get()),
+ ChangeNotifierProvider(create: (context) => getIt.get()),
+ ],
+ child: MaterialApp(
+ showSemanticsDebugger: false,
+ title: AppStrings.appName,
+ theme: ThemeData(
+ fontFamily: AppStrings.fontNamePoppins,
+ colorScheme: ColorScheme.fromSwatch(primarySwatch: Colors.grey).copyWith(
+ surface: const Color.fromRGBO(255, 255, 255, 1),
),
- initialRoute: AppRoutes.initialRoute,
- routes: AppRoutes.routes,
- debugShowCheckedModeBanner: false,
- ));
+ ),
+ initialRoute: AppRoutes.initialRoute,
+ routes: AppRoutes.routes,
+ debugShowCheckedModeBanner: false,
+ ),
+ );
});
},
);
diff --git a/lib/repositories/signalR_repo.dart b/lib/repositories/signalR_repo.dart
index 6facecf..a03e232 100644
--- a/lib/repositories/signalR_repo.dart
+++ b/lib/repositories/signalR_repo.dart
@@ -48,9 +48,27 @@ class SignalrRepoImp implements SignalrRepo {
.build();
connection!.serverTimeoutInMilliseconds = 120000;
- connection!.onclose((exception) {
+ int reconnectAttempts = 0;
+ const int maxReconnectAttempts = 10;
+ const Duration reconnectDelay = Duration(seconds: 5);
+ connection!.onclose((exception) async {
log(exception.toString());
onHubDisconnected(exception);
+ // Attempt to reconnect with limit and delay
+ if (reconnectAttempts < maxReconnectAttempts) {
+ reconnectAttempts++;
+ loggerService.logToFile("SignalR reconnect attempt #$reconnectAttempts", type: LogTypeEnum.data);
+ await Future.delayed(reconnectDelay);
+ try {
+ await connection!.start();
+ loggerService.logToFile("SignalR reconnected after disconnect", type: LogTypeEnum.data);
+ reconnectAttempts = 0; // Reset on success
+ } catch (e) {
+ loggerService.logError("Reconnect failed: $e");
+ }
+ } else {
+ loggerService.logError("Max SignalR reconnect attempts reached.");
+ }
});
connection!.onreconnecting((exception) => onHubConnecting(exception));
diff --git a/lib/services/logger_service.dart b/lib/services/logger_service.dart
index 0e71890..ee65a83 100644
--- a/lib/services/logger_service.dart
+++ b/lib/services/logger_service.dart
@@ -23,7 +23,7 @@ class LoggerServiceImp implements LoggerService {
@override
Future logToFile(String message, {LogTypeEnum type = LogTypeEnum.data}) async {
try {
- final timestamp = DateFormat('yyyy-MM-dd HH:mm:ss a').format(DateTime.now());
+ final timestamp = DateFormat('yyyy-MM-dd hh:mm:ss a').format(DateTime.now());
final formattedMessage = "[$timestamp] ${type.name.toUpperCase()}: $message";
final dir = await getApplicationDocumentsDirectory();
diff --git a/lib/utilities/foreground_task_handler.dart b/lib/utilities/foreground_task_handler.dart
new file mode 100644
index 0000000..5e9af0b
--- /dev/null
+++ b/lib/utilities/foreground_task_handler.dart
@@ -0,0 +1,22 @@
+import 'package:flutter_foreground_task/flutter_foreground_task.dart';
+
+void startCallback() {
+ FlutterForegroundTask.setTaskHandler(MyTaskHandler());
+}
+
+class MyTaskHandler extends TaskHandler {
+ @override
+ Future onStart(DateTime timestamp, TaskStarter? starter) async {
+ // Initialization code, e.g. open DB or services
+ }
+
+ @override
+ Future onDestroy(DateTime timestamp, bool isTimeout) async {
+ // Clean up if needed
+ }
+
+ @override
+ void onRepeatEvent(DateTime timestamp) {
+ // Called based on the eventAction set in ForegroundTaskOptions
+ }
+}
diff --git a/lib/utilities/lifecycle_handler.dart b/lib/utilities/lifecycle_handler.dart
new file mode 100644
index 0000000..da3142b
--- /dev/null
+++ b/lib/utilities/lifecycle_handler.dart
@@ -0,0 +1,51 @@
+import 'package:flutter/widgets.dart';
+import 'package:hmg_qline/config/dependency_injection.dart';
+import 'package:hmg_qline/services/logger_service.dart';
+import 'package:hmg_qline/utilities/enums.dart';
+
+class LifecycleHandler extends WidgetsBindingObserver {
+ final void Function()? onResumed;
+ final void Function()? onPaused;
+ final void Function()? onDetached;
+ final void Function()? onInactive;
+ final void Function()? onHidden;
+
+ LifecycleHandler({
+ this.onResumed,
+ this.onPaused,
+ this.onDetached,
+ this.onInactive,
+ this.onHidden,
+ });
+
+ @override
+ void didChangeAppLifecycleState(AppLifecycleState state) {
+ LoggerService loggerService = getIt.get();
+ loggerService.logInfo("[didChangeAppLifecycleState] : ${state.toString()}");
+ switch (state) {
+ case AppLifecycleState.resumed:
+ onResumed?.call();
+ break;
+ case AppLifecycleState.paused:
+ onPaused?.call();
+ break;
+ case AppLifecycleState.inactive:
+ onInactive?.call();
+ break;
+ case AppLifecycleState.detached:
+ onDetached?.call();
+ break;
+ case AppLifecycleState.hidden:
+ onHidden?.call();
+ break;
+ }
+ }
+
+ void register() {
+ WidgetsBinding.instance.addObserver(this);
+ }
+
+ void unregister() {
+ WidgetsBinding.instance.removeObserver(this);
+ }
+}
diff --git a/lib/utilities/native_method_handler.dart b/lib/utilities/native_method_handler.dart
new file mode 100644
index 0000000..bec0cfd
--- /dev/null
+++ b/lib/utilities/native_method_handler.dart
@@ -0,0 +1,38 @@
+import 'package:flutter/services.dart';
+import 'package:hmg_qline/constants/app_constants.dart';
+import 'package:hmg_qline/services/logger_service.dart';
+import 'package:hmg_qline/utilities/enums.dart';
+import 'package:restart_app/restart_app.dart';
+
+abstract class NativeMethodChannelService {
+ void reopenApp();
+
+ void restartApp();
+}
+
+class NativeMethodChannelServiceImp implements NativeMethodChannelService {
+ MethodChannel platform;
+ LoggerService loggerService;
+
+ NativeMethodChannelServiceImp({required this.platform, required this.loggerService});
+
+ @override
+ void reopenApp() async {
+ try {
+ await platform.invokeMethod(AppStrings.openAppNativeFunctionName);
+ } catch (e) {
+ loggerService.logError("Error launching app: $e");
+ loggerService.logToFile("Error launching app: $e", type: LogTypeEnum.error);
+ }
+ }
+
+ @override
+ void restartApp() async {
+ try {
+ await Restart.restartApp();
+ } catch (e) {
+ loggerService.logError("Error restarting App : $e");
+ loggerService.logToFile("Error restarting App : $e", type: LogTypeEnum.error);
+ }
+ }
+}
diff --git a/lib/view_models/queuing_view_model.dart b/lib/view_models/queuing_view_model.dart
index 7e69ec2..5e9b9f8 100644
--- a/lib/view_models/queuing_view_model.dart
+++ b/lib/view_models/queuing_view_model.dart
@@ -89,6 +89,8 @@ class QueuingViewModel extends ChangeNotifier {
Future onHubReconnected(var response) async {
log("onHubConnected: $response");
+ loggerService.logToFile("onHubConnected", type: LogTypeEnum.data);
+
ScreenConfigViewModel screenConfigViewModel = getIt.get();
screenConfigViewModel.updateIsHubConnected(true);
screenConfigViewModel.notifyListeners();
@@ -96,6 +98,8 @@ class QueuingViewModel extends ChangeNotifier {
Future onHubDisconnected(var response) async {
log("onHubDisconnected: $response");
+ loggerService.logToFile("onHubDisconnected", type: LogTypeEnum.data);
+
ScreenConfigViewModel screenConfigViewModel = getIt.get();
screenConfigViewModel.updateIsHubConnected(false);
screenConfigViewModel.notifyListeners();
diff --git a/lib/view_models/screen_config_view_model.dart b/lib/view_models/screen_config_view_model.dart
index b198d02..39a25b0 100644
--- a/lib/view_models/screen_config_view_model.dart
+++ b/lib/view_models/screen_config_view_model.dart
@@ -1,8 +1,10 @@
import 'dart:developer';
+import 'dart:async';
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
import 'package:hmg_qline/constants/app_constants.dart';
+import 'package:hmg_qline/main.dart';
import 'package:hmg_qline/models/generic_response_model.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/models/kiosk_language_config_model.dart';
@@ -17,6 +19,7 @@ import 'package:hmg_qline/services/connectivity_service.dart';
import 'package:hmg_qline/services/logger_service.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
+import 'package:hmg_qline/utilities/native_method_handler.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/views/view_helpers/info_components.dart';
import 'package:qr_code_scanner_plus/qr_code_scanner_plus.dart';
@@ -27,12 +30,14 @@ class ScreenConfigViewModel extends ChangeNotifier {
final CacheService cacheService;
final ConnectivityService connectivityService;
final LoggerService loggerService;
+ final NativeMethodChannelService nativeMethodChannelService;
ScreenConfigViewModel({
required this.screenDetailsRepo,
required this.cacheService,
required this.connectivityService,
required this.loggerService,
+ required this.nativeMethodChannelService,
});
Future initializeScreenConfigVM() async {
@@ -42,6 +47,18 @@ class ScreenConfigViewModel extends ChangeNotifier {
getTheWidgetsConfigurationsEveryMidnight();
}
+ Future onAppResumed() async {
+ loggerService.logToFile("[didChangeAppLifecycleState] : [onAppResumed]", type: LogTypeEnum.data);
+ }
+
+ Future onAppPaused() async {
+ loggerService.logToFile("[didChangeAppLifecycleState] : [onAppPaused]", type: LogTypeEnum.data);
+
+ // nativeMethodChannelService.reopenApp();
+ nativeMethodChannelService.restartApp();
+ runApp(const MyApp());
+ }
+
Future waitForIPAndInitializeConfigVM() async {
while (currentScreenIP == "") {
await getCurrentScreenIP();
@@ -103,14 +120,17 @@ class ScreenConfigViewModel extends ChangeNotifier {
}
void listenNetworkConnectivity() {
- return connectivityService.subscribeToConnectivityChange(onInternetDisConnected: () {
- updateIsInternetConnected(false);
- updateIsHubConnected(false);
- }, onInternetConnected: () {
- updateIsInternetConnected(true);
- QueuingViewModel queuingViewModel = getIt.get();
- queuingViewModel.startHubConnection();
- });
+ return connectivityService.subscribeToConnectivityChange(
+ onInternetDisConnected: () {
+ updateIsInternetConnected(false);
+ updateIsHubConnected(false);
+ },
+ onInternetConnected: () {
+ updateIsInternetConnected(true);
+ QueuingViewModel queuingViewModel = getIt.get();
+ queuingViewModel.startHubConnection();
+ },
+ );
}
GlobalConfigurationsModel globalConfigurationsModel = GlobalConfigurationsModel();
@@ -267,48 +287,51 @@ class ScreenConfigViewModel extends ChangeNotifier {
int counter = 0;
+ Timer? _midnightTimer;
+
Future getTheWidgetsConfigurationsEveryMidnight() async {
+ // Cancel any existing timer to avoid multiple timers running
+ _midnightTimer?.cancel();
+
if (!(globalConfigurationsModel.isWeatherReq) && !(globalConfigurationsModel.isPrayerTimeReq) && !(globalConfigurationsModel.isRssFeedReq)) {
return;
}
- DateTime current = DateTime.now();
- Stream timer = Stream.periodic(const Duration(minutes: 1), (i) {
- current = current.add(const Duration(minutes: 1));
- return current;
- });
-
- timer.listen((data) async {
- DateTime dateTime = DateTime.parse(data.toString());
+ int counter = 0;
+ DateTime lastChecked = DateTime.now();
+ _midnightTimer = Timer.periodic(const Duration(minutes: 5), (timer) async {
counter++;
-
+ DateTime now = DateTime.now();
log("counterValue: $counter");
- if (counter == 60 && globalConfigurationsModel.isRssFeedReq) {
+ // Every hour, update RSS feed if required
+ if (counter % 12 == 0 && globalConfigurationsModel.isRssFeedReq) {
await getRssFeedDetailsFromServer();
}
- if (globalConfigurationsModel.isWeatherReq) {
- if (dateTime.day > currentLastTimeUpdated.day) {
+ log("lastChecked: [${lastChecked.day}]");
+ log("now: [${now.day}]");
+
+ // At midnight, update weather and prayer details if required
+ if (now.day != lastChecked.day) {
+ if (globalConfigurationsModel.isWeatherReq) {
await getWeatherDetailsFromServer();
}
- }
-
- if (globalConfigurationsModel.isPrayerTimeReq) {
- if (dateTime.day > currentLastTimeUpdated.day) {
+ if (globalConfigurationsModel.isPrayerTimeReq) {
await getPrayerDetailsFromServer();
}
+ lastChecked = now;
}
+ });
+ }
- if (globalConfigurationsModel.isRssFeedReq) {
- if (dateTime.day > currentLastTimeUpdated.day) {
- await getRssFeedDetailsFromServer();
- }
- }
+ @override
+ void dispose() {
+ _midnightTimer?.cancel();
+ patientIdController.dispose();
- getNextPrayerToShow();
- });
+ super.dispose();
}
DateTime currentLastTimeUpdated = DateTime.now();
@@ -432,12 +455,6 @@ class ScreenConfigViewModel extends ChangeNotifier {
final TextEditingController patientIdController = TextEditingController();
- @override
- void dispose() {
- patientIdController.dispose();
- super.dispose();
- }
-
Future onPatientIdSubmitted(String text) async {
int? patientId = int.tryParse(text);
if (patientId != null && patientId > 0) {
diff --git a/lib/views/common_widgets/app_footer.dart b/lib/views/common_widgets/app_footer.dart
index 81d6f04..5b436a2 100644
--- a/lib/views/common_widgets/app_footer.dart
+++ b/lib/views/common_widgets/app_footer.dart
@@ -32,10 +32,10 @@ class AppFooter extends StatelessWidget {
AppStrings.poweredBy,
fontSize: SizeConfig.getWidthMultiplier() * 2.5,
),
- Text("v${screenConfigVM.currentScreenIP}_${AppConstants.currentBuildVersion}",
+ Text("v${screenConfigVM.currentScreenIP}(${AppConstants.currentBuildVersion})",
style: TextStyle(
fontWeight: FontWeight.w500,
- fontSize: SizeConfig.getWidthMultiplier() * 1.7,
+ fontSize: SizeConfig.getWidthMultiplier() * 1.5,
)),
Row(
children: [
diff --git a/lib/views/main_queue_screen/main_queue_screen.dart b/lib/views/main_queue_screen/main_queue_screen.dart
index 7fcf7d5..cdc50b4 100644
--- a/lib/views/main_queue_screen/main_queue_screen.dart
+++ b/lib/views/main_queue_screen/main_queue_screen.dart
@@ -1,7 +1,12 @@
+import 'dart:developer';
+
import 'package:flutter/material.dart';
+import 'package:hmg_qline/config/dependency_injection.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/utilities/enums.dart';
import 'package:hmg_qline/utilities/extensions.dart';
+import 'package:hmg_qline/utilities/lifecycle_handler.dart';
+import 'package:hmg_qline/utilities/native_method_handler.dart';
import 'package:hmg_qline/view_models/queuing_view_model.dart';
import 'package:hmg_qline/view_models/screen_config_view_model.dart';
import 'package:hmg_qline/views/common_widgets/app_footer.dart';
@@ -12,9 +17,34 @@ import 'package:hmg_qline/views/main_queue_screen/components/priority_tickets.da
import 'package:hmg_qline/views/main_queue_screen/components/priority_tickets_sidelist.dart';
import 'package:provider/provider.dart';
-class MainQueueScreen extends StatelessWidget {
+class MainQueueScreen extends StatefulWidget {
const MainQueueScreen({super.key});
+ @override
+ State createState() => _MainQueueScreenState();
+}
+
+class _MainQueueScreenState extends State {
+ late LifecycleHandler lifecycleHandler;
+
+ @override
+ void initState() {
+ super.initState();
+
+ final ScreenConfigViewModel screenConfigViewModel = context.read();
+ lifecycleHandler = LifecycleHandler(
+ onResumed: () => screenConfigViewModel.onAppResumed(),
+ onPaused: () => screenConfigViewModel.onAppPaused(),
+ );
+ lifecycleHandler.register();
+ }
+
+ @override
+ void dispose() {
+ lifecycleHandler.unregister();
+ super.dispose();
+ }
+
Widget dataContent({required BuildContext context}) {
return Consumer2(
builder: (BuildContext context, ScreenConfigViewModel screenConfigViewModel, QueuingViewModel queuingViewModel, Widget? child) {
@@ -41,7 +71,7 @@ class MainQueueScreen extends StatelessWidget {
fontFamily = AppStrings.fontNameCairo;
}
- if (screenConfigViewModel.state == ViewState.error) {
+ if (screenConfigViewModel.state == ViewState.error && queuingViewModel.currentTickets.isEmpty) {
widget = intimationWidget(
text: AppStrings.configurationIssueContactAdmin,
fontName: AppStrings.fontNamePoppins,
diff --git a/pubspec.lock b/pubspec.lock
index d3f7080..dfa3b51 100644
--- a/pubspec.lock
+++ b/pubspec.lock
@@ -158,6 +158,14 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
+ flutter_foreground_task:
+ dependency: "direct main"
+ description:
+ name: flutter_foreground_task
+ sha256: "9f1b25a81db95d7119d2c5cffc654048cbdd49d4056183e1beadc1a6a38f3e29"
+ url: "https://pub.dev"
+ source: hosted
+ version: "9.1.0"
flutter_lints:
dependency: "direct dev"
description:
@@ -472,6 +480,14 @@ packages:
url: "https://pub.dev"
source: hosted
version: "2.0.10+1"
+ restart_app:
+ dependency: "direct main"
+ description:
+ name: restart_app
+ sha256: "00d5ec3e9de871cedbe552fc41e615b042b5ec654385e090e0983f6d02f655ed"
+ url: "https://pub.dev"
+ source: hosted
+ version: "1.3.2"
rxdart:
dependency: transitive
description:
@@ -484,10 +500,10 @@ packages:
dependency: "direct main"
description:
name: shared_preferences
- sha256: a752ce92ea7540fc35a0d19722816e04d0e72828a4200e83a98cf1a1eb524c9a
+ sha256: "6e8bf70b7fef813df4e9a36f658ac46d107db4b4cfe1048b477d4e453a8159f5"
url: "https://pub.dev"
source: hosted
- version: "2.3.5"
+ version: "2.5.3"
shared_preferences_android:
dependency: transitive
description:
diff --git a/pubspec.yaml b/pubspec.yaml
index a0a31ad..04bd6f3 100644
--- a/pubspec.yaml
+++ b/pubspec.yaml
@@ -53,6 +53,8 @@ dependencies:
fluttertoast: ^8.2.8
qr_code_scanner_plus: ^2.0.10+1
path_provider: ^2.1.5
+ flutter_foreground_task: ^9.1.0
+ restart_app: ^1.3.2
# esc_pos_printer: ^4.0.0 # Ensure you are using the latest version
# esc_pos_utils: ^1.0.0