diff --git a/android/app/build.gradle b/android/app/build.gradle
index c31d5ad..c4d0a62 100644
--- a/android/app/build.gradle
+++ b/android/app/build.gradle
@@ -45,4 +45,5 @@ flutter {
dependencies {
implementation 'androidx.lifecycle:lifecycle-service:2.8.7'
+ implementation 'androidx.core:core-ktx:1.13.1'
}
diff --git a/android/app/src/main/AndroidManifest.xml b/android/app/src/main/AndroidManifest.xml
index f4b26f2..ad16a12 100644
--- a/android/app/src/main/AndroidManifest.xml
+++ b/android/app/src/main/AndroidManifest.xml
@@ -5,6 +5,12 @@
+
+
+
+
+
+
@@ -24,14 +30,30 @@
-
+
+
+
+
+
+
+
+
+ android:exported="true"
+ android:foregroundServiceType="specialUse">
+
+
+
= Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> {
+ scheduleForAndroid14Plus(context, alarmManager, pendingIntent, calendar.timeInMillis)
+ }
+ // Android 12-13 (API 31-33)
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.S -> {
+ scheduleForAndroid12To13(context, alarmManager, pendingIntent, calendar.timeInMillis)
+ }
+ // Android 6-11 (API 23-30)
+ Build.VERSION.SDK_INT >= Build.VERSION_CODES.M -> {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ calendar.timeInMillis,
+ pendingIntent
+ )
+ Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 23-30)")
+ }
+ // Android < 6 (API < 23)
+ else -> {
+ alarmManager.setExact(
+ AlarmManager.RTC_WAKEUP,
+ calendar.timeInMillis,
+ pendingIntent
+ )
+ Log.d(TAG, "Alarm scheduled using setExact (API < 23)")
+ }
+ }
+ }
+
+ /**
+ * Schedule alarm for Android 14+ (API 34+)
+ * Android 14 requires special handling for exact alarms.
+ */
+ private fun scheduleForAndroid14Plus(
+ context: Context,
+ alarmManager: AlarmManager,
+ pendingIntent: PendingIntent,
+ triggerTime: Long
+ ) {
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (alarmManager.canScheduleExactAlarms()) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 34+)")
+ } else {
+ // Fallback to inexact alarm if permission not granted
+ alarmManager.setAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ Log.w(TAG, "Exact alarm permission not granted, using setAndAllowWhileIdle (API 34+)")
+ }
+ }
+ } catch (e: SecurityException) {
+ Log.e(TAG, "SecurityException scheduling alarm: ${e.message}")
+ // Fallback to inexact alarm
+ alarmManager.setAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ }
+ }
+
+ /**
+ * Schedule alarm for Android 12-13 (API 31-33)
+ */
+ private fun scheduleForAndroid12To13(
+ context: Context,
+ alarmManager: AlarmManager,
+ pendingIntent: PendingIntent,
+ triggerTime: Long
+ ) {
+ try {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ if (alarmManager.canScheduleExactAlarms()) {
+ alarmManager.setExactAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ Log.d(TAG, "Alarm scheduled using setExactAndAllowWhileIdle (API 31-33)")
+ } else {
+ alarmManager.setAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ Log.w(TAG, "Exact alarm permission not granted, using setAndAllowWhileIdle (API 31-33)")
+ }
+ }
+ } catch (e: SecurityException) {
+ Log.e(TAG, "SecurityException scheduling alarm: ${e.message}")
+ alarmManager.setAndAllowWhileIdle(
+ AlarmManager.RTC_WAKEUP,
+ triggerTime,
+ pendingIntent
+ )
+ }
+ }
+
+ /**
+ * Cancel the scheduled restart alarm.
+ */
+ fun cancelRestartAlarm(context: Context) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+
+ val intent = Intent(context, RestartAlarmReceiver::class.java).apply {
+ action = RestartAlarmReceiver.ACTION_SCHEDULED_RESTART
+ }
+
+ val pendingIntent = PendingIntent.getBroadcast(
+ context,
+ RESTART_ALARM_REQUEST_CODE,
+ intent,
+ PendingIntent.FLAG_UPDATE_CURRENT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ alarmManager.cancel(pendingIntent)
+ Log.d(TAG, "Restart alarm cancelled")
+ }
+
+ /**
+ * Check if the app can schedule exact alarms.
+ * Returns true for Android < 12 (always allowed) or if permission is granted on Android 12+.
+ */
+ fun canScheduleExactAlarms(context: Context): Boolean {
+ return if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ val alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ alarmManager.canScheduleExactAlarms()
+ } else {
+ true // Always allowed on older versions
+ }
+ }
+
+ /**
+ * Open system settings to request exact alarm permission (Android 12+).
+ */
+ fun requestExactAlarmPermission(context: Context) {
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
+ try {
+ val intent = Intent(Settings.ACTION_REQUEST_SCHEDULE_EXACT_ALARM).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
+ }
+ context.startActivity(intent)
+ } catch (e: Exception) {
+ Log.e(TAG, "Error opening exact alarm settings: ${e.message}")
+ }
+ }
+ }
+}
+
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 e5d8522..915a2bc 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
@@ -4,46 +4,128 @@ import android.app.Notification
import android.app.NotificationChannel
import android.app.NotificationManager
import android.content.Intent
+import android.content.pm.ServiceInfo
import android.os.Build
+import android.util.Log
import androidx.core.app.NotificationCompat
+import androidx.core.app.ServiceCompat
import androidx.lifecycle.LifecycleService
+/**
+ * Foreground service that launches the app after device boot or scheduled restart.
+ * Compatible with Android 14+ (API 34+) and older versions.
+ *
+ * Android 14+ requires:
+ * - Explicit foreground service type in manifest and code
+ * - FOREGROUND_SERVICE_SPECIAL_USE permission
+ */
class BootForegroundService : LifecycleService() {
+
+ companion object {
+ private const val TAG = "BootForegroundService"
+ private const val CHANNEL_ID = "boot_service_channel"
+ private const val NOTIFICATION_ID = 1
+ }
+
override fun onCreate() {
super.onCreate()
- startForegroundService()
+ Log.d(TAG, "Service created - Android SDK: ${Build.VERSION.SDK_INT}")
+ startForegroundServiceCompat()
+ }
+
+ override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
+ super.onStartCommand(intent, flags, startId)
+
+ val source = intent?.getStringExtra("source") ?: "unknown"
+ Log.d(TAG, "Service started from source: $source")
+
+ // Launch the main activity
+ launchMainActivity(source)
+
+ // Stop the service after launching the app
+ stopSelf()
+
+ return START_NOT_STICKY
}
- private fun startForegroundService() {
- val channelId = "boot_service_channel"
+ /**
+ * Start foreground service with Android version compatibility.
+ * Android 14+ requires explicit foreground service type.
+ */
+ private fun startForegroundServiceCompat() {
createNotificationChannel()
- val notification: Notification = NotificationCompat.Builder(this, channelId)
+ val notification: Notification = NotificationCompat.Builder(this, CHANNEL_ID)
.setContentTitle("QLine App")
- .setContentText("Monitoring QLine activity...")
+ .setContentText("Starting QLine...")
.setSmallIcon(R.mipmap.ic_launcher)
+ .setPriority(NotificationCompat.PRIORITY_LOW)
+ .setAutoCancel(true)
.build()
- startForeground(1, notification)
+ // Use ServiceCompat for Android 14+ compatibility
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE) {
+ // Android 14+ (API 34+) requires explicit foreground service type
+ ServiceCompat.startForeground(
+ this,
+ NOTIFICATION_ID,
+ notification,
+ ServiceInfo.FOREGROUND_SERVICE_TYPE_SPECIAL_USE
+ )
+ Log.d(TAG, "Foreground service started with SPECIAL_USE type (API 34+)")
+ } else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) {
+ // Android 10-13 (API 29-33)
+ startForeground(NOTIFICATION_ID, notification, ServiceInfo.FOREGROUND_SERVICE_TYPE_NONE)
+ Log.d(TAG, "Foreground service started with NONE type (API 29-33)")
+ } else {
+ // Android 8-9 (API 26-28)
+ startForeground(NOTIFICATION_ID, notification)
+ Log.d(TAG, "Foreground service started (API 26-28)")
+ }
+ }
+
+ /**
+ * Launch the main activity.
+ */
+ private fun launchMainActivity(source: String) {
+ try {
+ Log.d(TAG, "Launching MainActivity from source: $source")
+
+ val intent = Intent(this, MainActivity::class.java).apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ putExtra("launched_from_boot", source == "boot_completed")
+ putExtra("launched_from_scheduled_restart", source == "scheduled_restart")
+ }
+ startActivity(intent)
- // Only launch MainActivity if this service is started by the system (e.g. on boot)
- val intent = Intent(this, MainActivity::class.java).apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ Log.d(TAG, "MainActivity launched successfully")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error launching MainActivity: ${e.message}")
}
- startActivity(intent)
- stopSelf() // Stop the service after initialization
}
+ /**
+ * Create notification channel for Android 8.0+ (API 26+).
+ */
private fun createNotificationChannel() {
- val channelId = "boot_service_channel"
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
val channel = NotificationChannel(
- channelId,
+ CHANNEL_ID,
"Boot Service Channel",
- NotificationManager.IMPORTANCE_HIGH
- )
+ NotificationManager.IMPORTANCE_LOW // Use LOW to avoid sound/vibration
+ ).apply {
+ description = "Used to start QLine app after device boot"
+ setShowBadge(false)
+ }
+
val manager = getSystemService(NotificationManager::class.java)
manager?.createNotificationChannel(channel)
+ Log.d(TAG, "Notification channel created")
}
}
+
+ override fun onDestroy() {
+ super.onDestroy()
+ Log.d(TAG, "Service destroyed")
+ }
}
\ No newline at end of file
diff --git a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootReceiver.kt b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootReceiver.kt
index 4c83c97..7e02b1b 100644
--- a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootReceiver.kt
+++ b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/BootReceiver.kt
@@ -6,17 +6,58 @@ import android.content.Intent
import android.os.Build
import android.util.Log
+/**
+ * BroadcastReceiver that handles device boot events.
+ * Starts the app automatically after device boot and schedules daily restart alarm.
+ * Compatible with Android 14+ (API 34+) and older versions.
+ */
class BootBroadcastReceiver : BroadcastReceiver() {
+
+ companion object {
+ private const val TAG = "BootBroadcastReceiver"
+ }
+
override fun onReceive(context: Context, intent: Intent) {
- Log.d("BootReceiver", "Received intent: ${intent.action}")
+ Log.d(TAG, "Received intent: ${intent.action}")
if (intent.action == Intent.ACTION_BOOT_COMPLETED ||
intent.action == "android.intent.action.QUICKBOOT_POWERON" ||
intent.action == "com.htc.intent.action.QUICKBOOT_POWERON"
) {
+ Log.d(TAG, "Boot completed detected - Android SDK: ${Build.VERSION.SDK_INT}")
- Log.d("BootReceiver", "Starting BootForegroundService.")
- val serviceIntent = Intent(context, BootForegroundService::class.java)
+ // Schedule the daily restart alarm first
+ scheduleRestartAlarm(context)
+
+ // Then start the foreground service to launch the app
+ startAppViaForegroundService(context)
+ }
+ }
+
+ /**
+ * Schedule daily restart alarm at 00:15.
+ * This ensures the alarm is set even if the app wasn't running before reboot.
+ */
+ private fun scheduleRestartAlarm(context: Context) {
+ try {
+ Log.d(TAG, "Scheduling daily restart alarm after boot")
+ AlarmScheduler.scheduleRestartAlarm(context, 0, 15) // 00:15 (12:15 AM)
+ Log.d(TAG, "Daily restart alarm scheduled successfully")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error scheduling restart alarm after boot: ${e.message}")
+ }
+ }
+
+ /**
+ * Start the app via foreground service.
+ * Uses different approach based on Android version for compatibility.
+ */
+ private fun startAppViaForegroundService(context: Context) {
+ try {
+ Log.d(TAG, "Starting BootForegroundService")
+ val serviceIntent = Intent(context, BootForegroundService::class.java).apply {
+ putExtra("source", "boot_completed")
+ }
// Use foreground service for Android 8.0+ (API 26+)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
@@ -24,6 +65,30 @@ class BootBroadcastReceiver : BroadcastReceiver() {
} else {
context.startService(serviceIntent)
}
+
+ Log.d(TAG, "BootForegroundService started successfully")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error starting foreground service: ${e.message}")
+
+ // Fallback: try direct activity launch
+ tryDirectActivityLaunch(context)
+ }
+ }
+
+ /**
+ * Fallback method to launch activity directly if service fails.
+ */
+ private fun tryDirectActivityLaunch(context: Context) {
+ try {
+ Log.d(TAG, "Attempting direct activity launch as fallback")
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ putExtra("launched_from_boot", true)
+ }
+ context.startActivity(launchIntent)
+ Log.d(TAG, "Direct activity launch successful")
+ } catch (fallbackError: Exception) {
+ Log.e(TAG, "Direct activity launch also failed: ${fallbackError.message}")
}
}
}
\ 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 09952db..8225164 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,8 @@
package com.example.hmg_qline.hmg_qline
+import android.app.AlarmManager
+import android.app.PendingIntent
+import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.os.Handler
@@ -13,85 +16,168 @@ import java.io.File
class MainActivity : FlutterActivity() {
private val CHANNEL = "com.example.hmg_qline/foreground"
+ companion object {
+ private const val TAG = "MainActivity"
+ }
+
override fun configureFlutterEngine(flutterEngine: FlutterEngine) {
super.configureFlutterEngine(flutterEngine)
MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result ->
- Log.d("MainActivity", "MethodChannel call received: ${call.method}")
+ Log.d(TAG, "MethodChannel call received: ${call.method}")
when (call.method) {
"reopenApp" -> {
- Log.d("MainActivity", "reopenApp called, bringing app to foreground")
+ Log.d(TAG, "reopenApp called, bringing app to foreground")
moveTaskToBack(false)
result.success("App brought to foreground")
}
"restartApp" -> {
- Log.d("MainActivity", "Restarting application")
+ Log.d(TAG, "Restarting application")
restartApplication()
result.success("App restart initiated")
}
"restartDevice" -> {
- Log.d("MainActivity", "Attempting device restart")
+ Log.d(TAG, "Attempting device restart")
restartDevice(result)
}
"runShellScript" -> {
- Log.d("MainActivity", "Executing shell restart command")
+ Log.d(TAG, "Executing shell restart command")
executeShellRestart(result)
}
"clearAudioCache" -> {
- Log.d("MainActivity", "Clearing audio cache")
+ Log.d(TAG, "Clearing audio cache")
clearAudioResources()
result.success("Audio cache cleared")
}
"clearAllResources" -> {
- Log.d("MainActivity", "Clearing all native resources")
+ Log.d(TAG, "Clearing all native resources")
clearAllNativeResources()
result.success("All resources cleared")
}
+ // === NEW: Alarm Scheduling Methods for Android 14+ compatibility ===
+
+ "scheduleRestartAlarm" -> {
+ val hour = call.argument("hour") ?: 0
+ val minute = call.argument("minute") ?: 15
+ Log.d(TAG, "Scheduling restart alarm for $hour:$minute")
+ scheduleRestartAlarm(hour, minute)
+ result.success("Restart alarm scheduled for $hour:$minute")
+ }
+
+ "cancelRestartAlarm" -> {
+ Log.d(TAG, "Cancelling restart alarm")
+ AlarmScheduler.cancelRestartAlarm(this)
+ result.success("Restart alarm cancelled")
+ }
+
+ "canScheduleExactAlarms" -> {
+ val canSchedule = AlarmScheduler.canScheduleExactAlarms(this)
+ Log.d(TAG, "Can schedule exact alarms: $canSchedule")
+ result.success(canSchedule)
+ }
+
+ "requestExactAlarmPermission" -> {
+ Log.d(TAG, "Requesting exact alarm permission")
+ AlarmScheduler.requestExactAlarmPermission(this)
+ result.success("Permission request initiated")
+ }
+
else -> {
- Log.w("MainActivity", "Method not implemented: ${call.method}")
+ Log.w(TAG, "Method not implemented: ${call.method}")
result.notImplemented()
}
}
}
}
- private fun restartApplication() {
+ /**
+ * Schedule daily restart alarm at specified time.
+ * Compatible with Android 14+ and older versions.
+ */
+ private fun scheduleRestartAlarm(hour: Int, minute: Int) {
try {
- Log.d("MainActivity", "Initiating app restart")
+ AlarmScheduler.scheduleRestartAlarm(this, hour, minute)
+ Log.d(TAG, "Restart alarm scheduled successfully for $hour:$minute")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error scheduling restart alarm: ${e.message}")
+ }
+ }
- // Clear resources before restart
- clearAllNativeResources()
+ private fun restartApplication() {
+ try {
+ Log.d(TAG, "Initiating app restart")
- // Create restart intent
- val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
- addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
- putExtra("restarted", true)
- }
+ // Get the launch intent
+ val intent = packageManager.getLaunchIntentForPackage(packageName)
if (intent != null) {
- // Use a shorter delay for faster restart
+ // Configure intent for clean restart
+ intent.apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ putExtra("restarted", true)
+ }
+
+ // Use AlarmManager for reliable restart (works better on Android 14+)
+ val alarmManager = getSystemService(Context.ALARM_SERVICE) as AlarmManager
+ val pendingIntent = PendingIntent.getActivity(
+ this,
+ 0,
+ intent,
+ PendingIntent.FLAG_ONE_SHOT or PendingIntent.FLAG_IMMUTABLE
+ )
+
+ // Schedule restart in 500ms
+ alarmManager.set(
+ AlarmManager.RTC,
+ System.currentTimeMillis() + 500,
+ pendingIntent
+ )
+
+ Log.d(TAG, "Restart scheduled via AlarmManager")
+
+ // Now safely exit the app
Handler(Looper.getMainLooper()).postDelayed({
- startActivity(intent)
finishAffinity()
- // Remove exitProcess() call if present
- // android.os.Process.killProcess(android.os.Process.myPid())
- }, 100) // Reduced delay
+ android.os.Process.killProcess(android.os.Process.myPid())
+ }, 100)
- Log.d("MainActivity", "App restart initiated")
} else {
- Log.e("MainActivity", "Could not create restart intent")
+ Log.e(TAG, "Could not create restart intent")
+ }
+
+ } catch (e: Exception) {
+ Log.e(TAG, "Error during restart: ${e.message}")
+ // Fallback: try simple restart
+ fallbackRestart()
+ }
+ }
+
+ /**
+ * Fallback restart method if AlarmManager approach fails
+ */
+ private fun fallbackRestart() {
+ try {
+ Log.d(TAG, "Attempting fallback restart")
+ val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK)
+ putExtra("restarted", true)
}
+ if (intent != null) {
+ startActivity(intent)
+ finishAffinity()
+ Runtime.getRuntime().exit(0)
+ }
} catch (e: Exception) {
- Log.e("MainActivity", "Error during restart: ${e.message}")
- // Fallback - don't exit, just log the error
+ Log.e(TAG, "Fallback restart also failed: ${e.message}")
}
}
@@ -237,25 +323,43 @@ class MainActivity : FlutterActivity() {
// Log if app was restarted
if (intent.getBooleanExtra("restarted", false)) {
- Log.d("MainActivity", "App restarted successfully")
+ Log.d(TAG, "App restarted successfully")
}
// Log if launched from boot
if (intent.getBooleanExtra("launched_from_boot", false)) {
- Log.d("MainActivity", "App launched from boot")
+ Log.d(TAG, "App launched from boot")
// Give system time to settle after boot
Thread.sleep(2000)
}
+
+ // Schedule daily restart alarm at 00:15 (12:15 AM)
+ // This ensures the alarm is always set when app starts
+ initializeRestartAlarm()
+ }
+
+ /**
+ * Initialize the daily restart alarm.
+ * Called on app start to ensure alarm is always scheduled.
+ */
+ private fun initializeRestartAlarm() {
+ try {
+ Log.d(TAG, "Initializing daily restart alarm")
+ AlarmScheduler.scheduleRestartAlarm(this, 0, 15) // 00:15 (12:15 AM)
+ Log.d(TAG, "Daily restart alarm initialized for 00:15")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error initializing restart alarm: ${e.message}")
+ }
}
override fun onResume() {
super.onResume()
- Log.d("MainActivity", "Activity resumed")
+ Log.d(TAG, "Activity resumed")
}
override fun onPause() {
super.onPause()
- Log.d("MainActivity", "Activity paused - cleaning up resources")
+ Log.d(TAG, "Activity paused - cleaning up resources")
// Light cleanup when app goes to background
System.gc()
@@ -263,7 +367,7 @@ class MainActivity : FlutterActivity() {
override fun onDestroy() {
super.onDestroy()
- Log.d("MainActivity", "Activity destroyed")
+ Log.d(TAG, "Activity destroyed")
// Final cleanup
clearAllNativeResources()
diff --git a/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/RestartAlarmReceiver.kt b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/RestartAlarmReceiver.kt
new file mode 100644
index 0000000..b12ca9d
--- /dev/null
+++ b/android/app/src/main/kotlin/com/example/hmg_qline/hmg_qline/RestartAlarmReceiver.kt
@@ -0,0 +1,82 @@
+package com.example.hmg_qline.hmg_qline
+
+import android.content.BroadcastReceiver
+import android.content.Context
+import android.content.Intent
+import android.os.Build
+import android.util.Log
+
+/**
+ * BroadcastReceiver for handling scheduled app restart alarms.
+ * This is triggered by AlarmManager at the scheduled time (e.g., 12:15 AM).
+ * Compatible with Android 14+ (API 34+) and older versions.
+ */
+class RestartAlarmReceiver : BroadcastReceiver() {
+
+ companion object {
+ private const val TAG = "RestartAlarmReceiver"
+ const val ACTION_SCHEDULED_RESTART = "com.example.hmg_qline.SCHEDULED_RESTART"
+ }
+
+ override fun onReceive(context: Context, intent: Intent) {
+ Log.d(TAG, "Received intent: ${intent.action}")
+
+ when (intent.action) {
+ ACTION_SCHEDULED_RESTART -> {
+ Log.d(TAG, "Scheduled restart triggered")
+ launchApp(context)
+
+ // Re-schedule the alarm for the next day
+ rescheduleAlarm(context)
+ }
+ Intent.ACTION_BOOT_COMPLETED,
+ "android.intent.action.QUICKBOOT_POWERON",
+ "com.htc.intent.action.QUICKBOOT_POWERON" -> {
+ Log.d(TAG, "Boot completed - re-scheduling daily restart alarm")
+ rescheduleAlarm(context)
+ }
+ }
+ }
+
+ private fun launchApp(context: Context) {
+ try {
+ Log.d(TAG, "Launching app via foreground service")
+
+ val serviceIntent = Intent(context, BootForegroundService::class.java).apply {
+ putExtra("source", "scheduled_restart")
+ }
+
+ // Use foreground service for Android 8.0+ (API 26+)
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
+ context.startForegroundService(serviceIntent)
+ } else {
+ context.startService(serviceIntent)
+ }
+
+ Log.d(TAG, "App launch initiated successfully")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error launching app: ${e.message}")
+
+ // Fallback: try direct activity launch
+ try {
+ val launchIntent = context.packageManager.getLaunchIntentForPackage(context.packageName)?.apply {
+ addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TOP)
+ }
+ context.startActivity(launchIntent)
+ } catch (fallbackError: Exception) {
+ Log.e(TAG, "Fallback launch also failed: ${fallbackError.message}")
+ }
+ }
+ }
+
+ private fun rescheduleAlarm(context: Context) {
+ try {
+ // Re-schedule for the next day at 00:15
+ AlarmScheduler.scheduleRestartAlarm(context, 0, 15)
+ Log.d(TAG, "Alarm rescheduled for next day at 00:15")
+ } catch (e: Exception) {
+ Log.e(TAG, "Error rescheduling alarm: ${e.message}")
+ }
+ }
+}
+
diff --git a/android/build.gradle b/android/build.gradle
index 81e43bb..d0adc08 100644
--- a/android/build.gradle
+++ b/android/build.gradle
@@ -1,5 +1,5 @@
buildscript {
- ext.kotlin_version = '1.9.10'
+ ext.kotlin_version = '2.1.0'
repositories {
google()
mavenCentral()
diff --git a/android/settings.gradle b/android/settings.gradle
index 1c66220..e3bfcfb 100644
--- a/android/settings.gradle
+++ b/android/settings.gradle
@@ -19,7 +19,7 @@ pluginManagement {
plugins {
id "dev.flutter.flutter-plugin-loader" version "1.0.0"
id "com.android.application" version "8.7.0" apply false
- id "org.jetbrains.kotlin.android" version "1.8.22" apply false
+ id "org.jetbrains.kotlin.android" version "2.1.0" apply false
}
diff --git a/lib/constants/app_constants.dart b/lib/constants/app_constants.dart
index 9223e46..ae2936e 100644
--- a/lib/constants/app_constants.dart
+++ b/lib/constants/app_constants.dart
@@ -8,11 +8,11 @@ bool useTestIP = false;
bool isNeedToBreakVoiceForArabic = true;
bool isSpeechCompleted = true;
+bool isAndroid14 = true;
class AppStrings {
static String timeRemainingText = "Time Remaining";
static String namazTimeText = "Namaz Time";
-
static String poweredBy = "Powered By";
static String appName = "QLine";
static String fontNamePoppins = "Poppins";
@@ -167,7 +167,7 @@ class AppConstants {
static String apiKey = 'EE17D21C7943485D9780223CCE55DCE5';
static String testIP = '12.4.5.1'; // projectID.QlineType.ScreenType.AnyNumber (1 to 10)
static int thresholdForListUI = 5;
- static double currentBuildVersion = 9.2;
+ static double currentBuildVersion = 9.3;
static double clearLogsHoursThreshold = 48;
// Maximum log file size in bytes before rotation/clearing. Default 2 MB.
static int maxLogFileSizeBytes = 2 * 1024 * 1024;
diff --git a/lib/services/logger_service.dart b/lib/services/logger_service.dart
index 24cb012..6c0732a 100644
--- a/lib/services/logger_service.dart
+++ b/lib/services/logger_service.dart
@@ -95,7 +95,7 @@ class LoggerServiceImp implements LoggerService {
try {
ScreenConfigViewModel screenConfigViewModel = getIt.get();
final timestamp = DateFormat('yyyy-MM-dd hh:mm:ss a').format(DateTime.now());
- final sep = '==================== CRASH ====================';
+ const sep = '==================== CRASH ====================';
final contextStr = context ?? 'No context provided';
final errorMsg = error.toString();
final stack = stackTrace?.toString() ?? 'No stack trace available';
diff --git a/lib/services/text_to_speech_service.dart b/lib/services/text_to_speech_service.dart
index 868dafb..302e821 100644
--- a/lib/services/text_to_speech_service.dart
+++ b/lib/services/text_to_speech_service.dart
@@ -1,4 +1,5 @@
import 'dart:developer';
+
import 'package:flutter_tts/flutter_tts.dart';
import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/models/global_config_model.dart';
@@ -6,7 +7,6 @@ import 'package:hmg_qline/models/ticket_model.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:logger/logger.dart';
abstract class TextToSpeechService {
Future speechText({
@@ -15,8 +15,6 @@ abstract class TextToSpeechService {
bool isMute = false,
});
- // Future speechTextTest(TicketData ticket);
-
void listenToTextToSpeechEvents({required Function() onVoiceCompleted});
}
@@ -30,122 +28,6 @@ class TextToSpeechServiceImp implements TextToSpeechService {
double pitch = 0.6;
Map arabicVoice = {"name": "ar-xa-x-ard-local", "locale": "ar"};
- @override
- // Future speechTextTest(TicketData ticket) async {
- // const ttsGoogleEngine = 'com.google.android.tts';
- // LanguageEnum langEnum = ticket.voiceLanguageEnum;
- // List engines = await textToSpeechInstance.getEngines;
- // if (engines.contains(ttsGoogleEngine)) {
- // await textToSpeechInstance.setEngine(ttsGoogleEngine);
- // }
- //
- // textToSpeechInstance.setVolume(1.0);
- //
- // // final voices = await textToSpeechInstance.getVoices;
- // // log ("voices:: $voices");
- //
- // await textToSpeechInstance.setVoice(arabicVoice);
- //
- // if (langEnum == LanguageEnum.arabic) {
- // try {
- // await textToSpeechInstance.setLanguage(LanguageEnum.arabic.enumToString());
- // } catch (e) {
- // log("error setting language english: ${e.toString()}");
- // }
- // } else if (langEnum == LanguageEnum.english) {
- // try {
- // await textToSpeechInstance.setLanguage(LanguageEnum.english.enumToString());
- // } catch (e) {
- // log("error setting language english: ${e.toString()}");
- // }
- // }
- // String preVoice = ticket.ticketNoText;
- // String postVoice = ticket.postVoiceText;
- // if (preVoice.isNotEmpty) {
- // preVoice = '$preVoice..';
- // }
- // String ticketNo = ticket.queueNo!.trim().toString();
- //
- // log("areLanguagesInstalled: ${await textToSpeechInstance.areLanguagesInstalled(["en", "ar"])}");
- //
- // log("lang: $langEnum");
- // log("preVoice: $preVoice");
- // log("postVoice: $postVoice");
- // log("ticketNo: $ticketNo");
- //
- // String patientAlpha = "";
- // String patientNumeric = "";
- // String clinicName = "";
- //
- // bool isClinicNameAdded = (ticket.queueNo != ticket.callNoStr);
- //
- // if (isClinicNameAdded) {
- // var queueNo = "";
- // var clinic = ticketNo.split(" ");
- // if (clinic.length > 1) {
- // clinicName = clinic[0];
- // queueNo = clinic[1];
- // } else {
- // queueNo = ticketNo;
- // }
- //
- // var queueNoArray = queueNo.split("-");
- // if (queueNoArray.length > 2) {
- // patientAlpha = "${queueNoArray[0]} .. ${queueNoArray[1]}";
- // patientNumeric = queueNoArray[2];
- // } else {
- // patientAlpha = queueNoArray[0];
- // patientNumeric = queueNoArray[1];
- // }
- // } else {
- // var queueNoArray = ticketNo.split("-");
- // if (queueNoArray.length > 2) {
- // patientAlpha = "${queueNoArray[0]} .. ${queueNoArray[1]}";
- // patientNumeric = queueNoArray[2];
- // } else {
- // patientAlpha = queueNoArray[0];
- // patientNumeric = queueNoArray[1];
- // }
- // }
- //
- // patientAlpha = patientAlpha.split('').join(' .. ');
- // String roomNo = "";
- //
- // log("I will now all this:{ $preVoice .. $clinicName .. $patientAlpha .. $patientNumeric .. $postVoice $roomNo } ");
- //
- // if (langEnum == LanguageEnum.english) {
- // await textToSpeechInstance.speak("$preVoice .. $clinicName .. $patientAlpha .. $patientNumeric .. $postVoice $roomNo");
- // return;
- // }
- //
- // if (isNeedToBreakVoiceForArabic) {
- // await textToSpeechInstance.awaitSpeakCompletion(true);
- //
- // isSpeechCompleted = false;
- // if (preVoice.isNotEmpty) {
- // await textToSpeechInstance.speak("$preVoice ");
- // }
- // try {
- // await textToSpeechInstance.setLanguage(LanguageEnum.english.enumToString());
- // } catch (e) {
- // log("error setting language english: ${e.toString()}");
- // }
- // await textToSpeechInstance.speak("$patientAlpha .. $patientNumeric");
- //
- // try {
- // await textToSpeechInstance.setLanguage(langEnum.enumToString());
- // } catch (e) {
- // log("error setting language langEnum: ${e.toString()}");
- // }
- //
- // await textToSpeechInstance.speak("$postVoice $roomNo").whenComplete(() {
- // isSpeechCompleted = true;
- // });
- // } else {
- // await textToSpeechInstance.speak("$preVoice .. $clinicName .. $patientAlpha .. $patientNumeric .. $postVoice $roomNo");
- // }
- // }
-
@override
Future speechText({
required TicketDetailsModel ticket,
@@ -169,7 +51,16 @@ class TextToSpeechServiceImp implements TextToSpeechService {
textToSpeechInstance.setVolume(1.0);
}
- textToSpeechInstance.setSpeechRate(0.4);
+ if (isAndroid14) {
+ if (langEnum == LanguageEnum.arabic) {
+ textToSpeechInstance.setSpeechRate(0.5);
+ } else {
+ textToSpeechInstance.setSpeechRate(0.4);
+ }
+ textToSpeechInstance.setPitch(0.9);
+ } else {
+ textToSpeechInstance.setSpeechRate(0.4);
+ }
if (langEnum == LanguageEnum.arabic) {
try {
await textToSpeechInstance.setLanguage(LanguageEnum.arabic.enumToString());
@@ -191,7 +82,9 @@ class TextToSpeechServiceImp implements TextToSpeechService {
postVoice = ticket.ticketModel!.postVoiceText;
}
String roomNo = '';
- if (globalConfigurationsModel.qTypeEnum != QTypeEnum.appointment && ticket.ticketModel!.roomNo != null && ticket.ticketModel!.roomNo!.isNotEmpty) {
+ if (globalConfigurationsModel.qTypeEnum != QTypeEnum.appointment &&
+ ticket.ticketModel!.roomNo != null &&
+ ticket.ticketModel!.roomNo!.isNotEmpty) {
roomNo = ".. ${ticket.ticketModel!.roomNo.toString()}";
}
diff --git a/lib/utilities/native_method_handler.dart b/lib/utilities/native_method_handler.dart
index c87c85b..a3f2535 100644
--- a/lib/utilities/native_method_handler.dart
+++ b/lib/utilities/native_method_handler.dart
@@ -12,6 +12,19 @@ abstract class NativeMethodChannelService {
Future clearAllResources();
Future smartRestart({bool forceRestart = false, bool cleanupFirst = true});
+
+ /// Schedule daily restart alarm at specified time.
+ /// Works on Android 14+ and older versions.
+ Future scheduleRestartAlarm({int hour = 0, int minute = 15});
+
+ /// Cancel the scheduled restart alarm.
+ Future cancelRestartAlarm();
+
+ /// Check if the app can schedule exact alarms (Android 12+).
+ Future canScheduleExactAlarms();
+
+ /// Request permission to schedule exact alarms (Android 12+).
+ Future requestExactAlarmPermission();
}
class NativeMethodChannelServiceImp implements NativeMethodChannelService {
@@ -92,4 +105,85 @@ class NativeMethodChannelServiceImp implements NativeMethodChannelService {
loggerService.logError("Primary restart failed, trying fallback methods: $primaryError");
}
}
+
+ // === NEW: Alarm Scheduling Methods for Android 14+ compatibility ===
+
+ /// Schedule daily restart alarm at specified time.
+ /// Default is 00:15 (12:15 AM).
+ /// Works on Android 14+ and older versions.
+ @override
+ Future scheduleRestartAlarm({int hour = 0, int minute = 15}) async {
+ try {
+ loggerService.logInfo("Scheduling restart alarm for $hour:$minute");
+
+ // First check if we can schedule exact alarms
+ final canSchedule = await canScheduleExactAlarms();
+ if (!canSchedule) {
+ loggerService.logInfo("Exact alarm permission not granted. Requesting permission...");
+ await requestExactAlarmPermission();
+ }
+
+ await _platform.invokeMethod('scheduleRestartAlarm', {
+ 'hour': hour,
+ 'minute': minute,
+ });
+ loggerService.logInfo("Restart alarm scheduled successfully for $hour:$minute");
+ } catch (e) {
+ loggerService.logError("Error scheduling restart alarm: $e");
+ loggerService.logToFile(
+ message: "Error scheduling restart alarm: $e",
+ source: "scheduleRestartAlarm -> native_method_handler.dart",
+ type: LogTypeEnum.error,
+ );
+ }
+ }
+
+ /// Cancel the scheduled restart alarm.
+ @override
+ Future cancelRestartAlarm() async {
+ try {
+ loggerService.logInfo("Cancelling restart alarm");
+ await _platform.invokeMethod('cancelRestartAlarm');
+ loggerService.logInfo("Restart alarm cancelled successfully");
+ } catch (e) {
+ loggerService.logError("Error cancelling restart alarm: $e");
+ loggerService.logToFile(
+ message: "Error cancelling restart alarm: $e",
+ source: "cancelRestartAlarm -> native_method_handler.dart",
+ type: LogTypeEnum.error,
+ );
+ }
+ }
+
+ /// Check if the app can schedule exact alarms.
+ /// Returns true on Android < 12 (always allowed) or if permission is granted on Android 12+.
+ @override
+ Future canScheduleExactAlarms() async {
+ try {
+ final result = await _platform.invokeMethod('canScheduleExactAlarms');
+ loggerService.logInfo("Can schedule exact alarms: $result");
+ return result ?? false;
+ } catch (e) {
+ loggerService.logError("Error checking exact alarm permission: $e");
+ return false;
+ }
+ }
+
+ /// Request permission to schedule exact alarms (Android 12+).
+ /// Opens system settings for the user to grant permission.
+ @override
+ Future requestExactAlarmPermission() async {
+ try {
+ loggerService.logInfo("Requesting exact alarm permission");
+ await _platform.invokeMethod('requestExactAlarmPermission');
+ loggerService.logInfo("Exact alarm permission request initiated");
+ } catch (e) {
+ loggerService.logError("Error requesting exact alarm permission: $e");
+ loggerService.logToFile(
+ message: "Error requesting exact alarm permission: $e",
+ source: "requestExactAlarmPermission -> native_method_handler.dart",
+ type: LogTypeEnum.error,
+ );
+ }
+ }
}
diff --git a/lib/view_models/queuing_view_model.dart b/lib/view_models/queuing_view_model.dart
index d37f396..53da5a3 100644
--- a/lib/view_models/queuing_view_model.dart
+++ b/lib/view_models/queuing_view_model.dart
@@ -94,8 +94,8 @@ class QueuingViewModel extends ChangeNotifier {
loggerService.logToFile(message: response.toString(), source: "onHubTicketCall -> queueing_view_model.dart ", type: LogTypeEnum.data);
log("onHubTicketCall: $response");
-
log("isCallingInProgress: $isCallingInProgress");
+
if (response != null && response.isNotEmpty) {
TicketDetailsModel ticketDetailsModel = TicketDetailsModel.fromJson(response.first as Map);
addNewTicket(ticketDetailsModel);
diff --git a/lib/view_models/screen_config_view_model.dart b/lib/view_models/screen_config_view_model.dart
index d5c8665..617c67d 100644
--- a/lib/view_models/screen_config_view_model.dart
+++ b/lib/view_models/screen_config_view_model.dart
@@ -1,5 +1,6 @@
-import 'dart:developer';
import 'dart:async';
+import 'dart:developer';
+
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
@@ -49,16 +50,19 @@ class ScreenConfigViewModel extends ChangeNotifier {
}
Future onAppResumed() async {
- loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppResumed]", source: "onAppResumed -> screen_config_view_model.dart", type: LogTypeEnum.data);
+ loggerService.logToFile(
+ message: "[didChangeAppLifecycleState] : [onAppResumed]", source: "onAppResumed -> screen_config_view_model.dart", type: LogTypeEnum.data);
}
Future onAppPaused() async {
- loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppPaused]", source: "onAppPaused -> screen_config_view_model.dart", type: LogTypeEnum.data);
+ loggerService.logToFile(
+ message: "[didChangeAppLifecycleState] : [onAppPaused]", source: "onAppPaused -> screen_config_view_model.dart", type: LogTypeEnum.data);
// nativeMethodChannelService.restartApp();
}
Future onAppDetached() async {
- loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppDetached]", source: "onAppDetached -> screen_config_view_model.dart", type: LogTypeEnum.data);
+ loggerService.logToFile(
+ message: "[didChangeAppLifecycleState] : [onAppDetached]", source: "onAppDetached -> screen_config_view_model.dart", type: LogTypeEnum.data);
// nativeMethodChannelService.restartApp();
}
@@ -229,7 +233,9 @@ class ScreenConfigViewModel extends ChangeNotifier {
Future getWeatherDetailsFromServer() async {
int testCityKey = 297030;
WeathersWidgetModel? response = await screenDetailsRepo.getWeatherDetailsByCity(
- cityId: ((globalConfigurationsModel.cityKey == null || globalConfigurationsModel.cityKey == 0) ? testCityKey : globalConfigurationsModel.cityKey).toString(),
+ cityId:
+ ((globalConfigurationsModel.cityKey == null || globalConfigurationsModel.cityKey == 0) ? testCityKey : globalConfigurationsModel.cityKey)
+ .toString(),
);
if (response == null) {
@@ -336,7 +342,7 @@ class ScreenConfigViewModel extends ChangeNotifier {
DateTime now = DateTime.now();
log("counterValue: $counter");
- if (globalConfigurationsModel.id == null) {
+ if (globalConfigurationsModel.id == null || state == ViewState.error) {
await getGlobalConfigurationsByIP();
}
@@ -393,7 +399,9 @@ class ScreenConfigViewModel extends ChangeNotifier {
Future getLastTimeLogsClearedFromCache() async {
lastTimeLogsCleared = await cacheService.getLastTimeLogsCleared();
if (lastTimeLogsCleared == null) {
- await cacheService.setLastTimeLogsCleared(lastTimeCleared: DateTime.now().millisecondsSinceEpoch).whenComplete(() => lastTimeLogsCleared = DateTime.now());
+ await cacheService
+ .setLastTimeLogsCleared(lastTimeCleared: DateTime.now().millisecondsSinceEpoch)
+ .whenComplete(() => lastTimeLogsCleared = DateTime.now());
}
}
@@ -491,7 +499,8 @@ class ScreenConfigViewModel extends ChangeNotifier {
}
}
- Future acknowledgeTicketForAppointmentOnly({required int ticketQueueID, required String ipAddress, required CallTypeEnum callTypeEnum}) async {
+ Future acknowledgeTicketForAppointmentOnly(
+ {required int ticketQueueID, required String ipAddress, required CallTypeEnum callTypeEnum}) async {
GenericRespModel? response = await screenDetailsRepo.acknowledgeTicketForAppointment(
ticketId: ticketQueueID,
ipAddress: ipAddress,
diff --git a/lib/views/common_widgets/app_header.dart b/lib/views/common_widgets/app_header.dart
index 68bd294..9f318f5 100644
--- a/lib/views/common_widgets/app_header.dart
+++ b/lib/views/common_widgets/app_header.dart
@@ -1,19 +1,14 @@
-import 'dart:developer';
-
import 'package:flutter/material.dart';
import 'package:flutter_svg/flutter_svg.dart';
import 'package:hmg_qline/config/dependency_injection.dart';
+import 'package:hmg_qline/constants/app_constants.dart';
import 'package:hmg_qline/models/global_config_model.dart';
import 'package:hmg_qline/utilities/enums.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_general_widgets.dart';
-import 'package:provider/provider.dart';
-import 'package:hmg_qline/constants/app_constants.dart';
-
-import 'package:hmg_qline/views/common_widgets/app_texts_widget.dart';
import 'package:hmg_qline/views/view_helpers/size_config.dart';
+import 'package:provider/provider.dart';
class AppHeader extends StatelessWidget implements PreferredSizeWidget {
const AppHeader({super.key});
@@ -34,8 +29,10 @@ class AppHeader extends StatelessWidget implements PreferredSizeWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
InkWell(
+ onTap: () async {
+ final nativeMethodChannelService = getIt.get();
+ await nativeMethodChannelService.smartRestart(forceRestart: true, cleanupFirst: true);
- onTap: () {
// getIt.get().triggerOOM();
},
child: engArabicTextWithSeparatorWidget(
diff --git a/lib/views/main_queue_screen/components/priority_tickets_sidelist.dart b/lib/views/main_queue_screen/components/priority_tickets_sidelist.dart
index c7fbc91..13b28ff 100644
--- a/lib/views/main_queue_screen/components/priority_tickets_sidelist.dart
+++ b/lib/views/main_queue_screen/components/priority_tickets_sidelist.dart
@@ -86,7 +86,7 @@ class PriorityTicketsWithSideSection extends StatelessWidget {
SizedBox(width: SizeConfig.getWidthMultiplier()),
if (callMessageEng.isNotEmpty) ...[
Expanded(
- flex: 2,
+ flex: 3,
child: AppText(
"($callMessageEng)",
color: ticketModel.callTypeEnum.getColorByCallType(),
@@ -170,7 +170,7 @@ class PriorityTicketsWithSideSection extends StatelessWidget {
mainAxisAlignment: MainAxisAlignment.spaceBetween,
children: [
Expanded(
- flex: 3,
+ flex: 4,
child: engArabicTextWithSeparatorWidget(
englishText: globalConfigurationsModel.queueNoTextEng ?? "",
arabicText: globalConfigurationsModel.queueNoTextArb ?? "",
diff --git a/lib/views/main_queue_screen/main_queue_screen.dart b/lib/views/main_queue_screen/main_queue_screen.dart
index 1915e2d..df71d51 100644
--- a/lib/views/main_queue_screen/main_queue_screen.dart
+++ b/lib/views/main_queue_screen/main_queue_screen.dart
@@ -192,7 +192,9 @@ class _MainQueueScreenState extends State {
// context.read().createAutoTickets(numOfTicketsToCreate: 20);
// context.read().testSpeech();
return RotatedBox(
- quarterTurns: globalConfigurationsModel.isFromTakhasusiMain ? screenOrientationEnum.getTurnsByOrientationForOlderVersions() : screenOrientationEnum.getTurnsByOrientation(),
+ quarterTurns: globalConfigurationsModel.isFromTakhasusiMain
+ ? screenOrientationEnum.getTurnsByOrientationForOlderVersions()
+ : screenOrientationEnum.getTurnsByOrientation(),
child: AppScaffold(
backgroundColor: AppColors.backgroundColor,
appBar: const AppHeader(),