From a6df0764fb1f023e6ed1bd1989d907369f06216c Mon Sep 17 00:00:00 2001 From: Faiz Hashmi Date: Thu, 9 Oct 2025 11:55:16 +0300 Subject: [PATCH] send the release v9 to tk women. fixed restart issue --- .../hmg_qline/hmg_qline/MainActivity.kt | 345 +++++++++++++++++- lib/config/dependency_injection.dart | 2 +- lib/main.dart | 26 +- lib/utilities/native_method_handler.dart | 75 +++- lib/view_models/queuing_view_model.dart | 20 +- lib/view_models/screen_config_view_model.dart | 24 +- lib/views/common_widgets/app_header.dart | 13 +- pubspec.lock | 8 - pubspec.yaml | 2 +- 9 files changed, 460 insertions(+), 55 deletions(-) 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 962b001..09952db 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 @@ -2,10 +2,13 @@ package com.example.hmg_qline.hmg_qline import android.content.Intent import android.os.Bundle +import android.os.Handler +import android.os.Looper import android.util.Log import io.flutter.embedding.android.FlutterActivity import io.flutter.embedding.engine.FlutterEngine import io.flutter.plugin.common.MethodChannel +import java.io.File class MainActivity : FlutterActivity() { private val CHANNEL = "com.example.hmg_qline/foreground" @@ -15,17 +18,353 @@ class MainActivity : FlutterActivity() { MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> Log.d("MainActivity", "MethodChannel call received: ${call.method}") + when (call.method) { "reopenApp" -> { Log.d("MainActivity", "reopenApp called, bringing app to foreground") - // Simply bring current activity to foreground moveTaskToBack(false) - // Or trigger Flutter navigation if needed result.success("App brought to foreground") } - else -> result.notImplemented() + "restartApp" -> { + Log.d("MainActivity", "Restarting application") + restartApplication() + result.success("App restart initiated") + } + + "restartDevice" -> { + Log.d("MainActivity", "Attempting device restart") + restartDevice(result) + } + + "runShellScript" -> { + Log.d("MainActivity", "Executing shell restart command") + executeShellRestart(result) + } + + "clearAudioCache" -> { + Log.d("MainActivity", "Clearing audio cache") + clearAudioResources() + result.success("Audio cache cleared") + } + + "clearAllResources" -> { + Log.d("MainActivity", "Clearing all native resources") + clearAllNativeResources() + result.success("All resources cleared") + } + + else -> { + Log.w("MainActivity", "Method not implemented: ${call.method}") + result.notImplemented() + } + } + } + } + + private fun restartApplication() { + try { + Log.d("MainActivity", "Initiating app restart") + + // Clear resources before restart + clearAllNativeResources() + + // Create restart intent + val intent = packageManager.getLaunchIntentForPackage(packageName)?.apply { + addFlags(Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK) + putExtra("restarted", true) + } + + if (intent != null) { + // Use a shorter delay for faster restart + Handler(Looper.getMainLooper()).postDelayed({ + startActivity(intent) + finishAffinity() + // Remove exitProcess() call if present + // android.os.Process.killProcess(android.os.Process.myPid()) + }, 100) // Reduced delay + + Log.d("MainActivity", "App restart initiated") + } else { + Log.e("MainActivity", "Could not create restart intent") + } + + } catch (e: Exception) { + Log.e("MainActivity", "Error during restart: ${e.message}") + // Fallback - don't exit, just log the error + } + } + + private fun restartDevice(result: MethodChannel.Result) { + try { + Log.d("MainActivity", "Attempting device restart (requires root)") + + // Try different reboot commands + val rebootCommands = arrayOf( + arrayOf("su", "-c", "reboot"), + arrayOf("su", "-c", "reboot now"), + arrayOf("reboot") + ) + + var success = false + for (command in rebootCommands) { + try { + val process = Runtime.getRuntime().exec(command) + val exitCode = process.waitFor() + + if (exitCode == 0) { + success = true + Log.d("MainActivity", "Device restart command executed successfully") + result.success("Device restart initiated") + break + } + } catch (e: Exception) { + Log.w("MainActivity", "Reboot command failed: ${command.joinToString(" ")}") + } + } + + if (!success) { + Log.e("MainActivity", "All reboot commands failed - device may not have root access") + result.error("RESTART_ERROR", "Device restart failed - root access required", null) + } + + } catch (e: Exception) { + Log.e("MainActivity", "Exception during device restart: ${e.message}") + result.error("RESTART_ERROR", "Device restart failed: ${e.message}", null) + } + } + + private fun executeShellRestart(result: MethodChannel.Result) { + try { + Log.d("MainActivity", "Executing shell restart script") + + // Copy script from assets to internal storage + val scriptFile = copyAssetToFile("scripts/restart_app.sh") + + if (scriptFile != null && scriptFile.exists()) { + // Make script executable + Runtime.getRuntime().exec("chmod 755 ${scriptFile.absolutePath}") + + // Execute the script + val process = Runtime.getRuntime().exec("su -c ${scriptFile.absolutePath}") + val exitCode = process.waitFor() + + if (exitCode == 0) { + result.success("Shell restart executed successfully") + } else { + result.error("SHELL_ERROR", "Script execution failed with code: $exitCode", null) + } + } else { + result.error("SCRIPT_ERROR", "Could not copy script from assets", null) + } + + } catch (e: Exception) { + Log.e("MainActivity", "Shell restart error: ${e.message}") + result.error("SHELL_EXCEPTION", e.message, null) + } + } + + private fun copyAssetToFile(assetPath: String): File? { + return try { + val inputStream = assets.open(assetPath) + val file = File(filesDir, "restart_app.sh") + + file.outputStream().use { output -> + inputStream.copyTo(output) + } + + file + } catch (e: Exception) { + Log.e("MainActivity", "Error copying asset: ${e.message}") + null + } + } + + + private fun clearAudioResources() { + try { + Log.d("MainActivity", "Clearing audio resources") + + // Force garbage collection + System.gc() + + // Clear audio-related system caches + val commands = arrayOf( + "am force-stop com.google.android.tts", + "am force-stop com.android.providers.media" + ) + + for (command in commands) { + try { + Runtime.getRuntime().exec(command) + } catch (e: Exception) { + Log.w("MainActivity", "Failed to clear audio service: $command") + } + } + + } catch (e: Exception) { + Log.e("MainActivity", "Error clearing audio resources: ${e.message}") + } + } + + private fun clearAllNativeResources() { + try { + Log.d("MainActivity", "Clearing all native resources") + + // Clear audio resources + clearAudioResources() + + // Clear image caches + try { + Runtime.getRuntime().exec("am broadcast -a android.intent.action.MEDIA_MOUNTED") + } catch (e: Exception) { + Log.w("MainActivity", "Failed to clear media cache") } + + // Force garbage collection + System.gc() + System.runFinalization() + + Log.d("MainActivity", "Native resources cleared") + + } catch (e: Exception) { + Log.e("MainActivity", "Error clearing native resources: ${e.message}") + } + } + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + + // Log if app was restarted + if (intent.getBooleanExtra("restarted", false)) { + Log.d("MainActivity", "App restarted successfully") + } + + // Log if launched from boot + if (intent.getBooleanExtra("launched_from_boot", false)) { + Log.d("MainActivity", "App launched from boot") + // Give system time to settle after boot + Thread.sleep(2000) } } + + override fun onResume() { + super.onResume() + Log.d("MainActivity", "Activity resumed") + } + + override fun onPause() { + super.onPause() + Log.d("MainActivity", "Activity paused - cleaning up resources") + + // Light cleanup when app goes to background + System.gc() + } + + override fun onDestroy() { + super.onDestroy() + Log.d("MainActivity", "Activity destroyed") + + // Final cleanup + clearAllNativeResources() + } } + + +//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() { +// private val CHANNEL = "com.example.hmg_qline/foreground" +// +// override fun configureFlutterEngine(flutterEngine: FlutterEngine) { +// super.configureFlutterEngine(flutterEngine) +// +// MethodChannel(flutterEngine.dartExecutor.binaryMessenger, CHANNEL).setMethodCallHandler { call, result -> +// Log.d("MainActivity", "MethodChannel call received: ${call.method}") +// when (call.method) { +// "reopenApp" -> { +// Log.d("MainActivity", "reopenApp called, bringing app to foreground") +// // Simply bring current activity to foreground +// moveTaskToBack(false) +// // Or trigger Flutter navigation if needed +// result.success("App brought to foreground") +// } +// +// else -> result.notImplemented() +// } +// +// +// when (call.method) { +// "runShellScript" -> { +// Log.d("MainActivity", "reopenApp called, bringing app to foreground") +// // Simply bring current activity to foreground +// moveTaskToBack(false) +// // Or trigger Flutter navigation if needed +// result.success("App brought to foreground") +// } +// +// else -> result.notImplemented() +// } +// +// +// } +// } +// +// +//} +// +// +////} +//// +//// private fun hasShellPermissions(): Boolean { +//// return try { +//// val process = Runtime.getRuntime().exec("id") +//// process.waitFor() +//// val exitCode = process.exitValue() +//// Log.d("MainActivity", "Shell permission check exit code: $exitCode") +//// exitCode == 0 +//// } catch (e: Exception) { +//// Log.e("MainActivity", "Error checking shell permissions: ${e.message}") +//// false +//// } +//// } +//// +//// private fun executeShellScript(result: MethodChannel.Result) { +//// try { +//// // Copy script from assets to internal storage +//// val assetManager = assets +//// val inputStream = assetManager.open("restart_app.sh") +//// val scriptFile = java.io.File(filesDir, "restart_app.sh") +//// +//// scriptFile.outputStream().use { output -> +//// inputStream.copyTo(output) +//// } +//// +//// // Make script executable +//// scriptFile.setExecutable(true) +//// +//// // Execute the script +//// val process = Runtime.getRuntime().exec("sh ${scriptFile.absolutePath}") +//// val exitCode = process.waitFor() +//// +//// if (exitCode == 0) { +//// Log.d("MainActivity", "Shell script executed successfully") +//// result.success("Script executed successfully") +//// } else { +//// val errorStream = process.errorStream.bufferedReader().readText() +//// Log.e("MainActivity", "Script execution failed with exit code: $exitCode, error: $errorStream") +//// result.error("SCRIPT_ERROR", "Script execution failed", errorStream) +//// } +//// +//// } catch (e: Exception) { +//// Log.e("MainActivity", "Error executing shell script: ${e.message}") +//// result.error("EXECUTION_ERROR", "Failed to execute script: ${e.message}", null) +//// } +//// } diff --git a/lib/config/dependency_injection.dart b/lib/config/dependency_injection.dart index 0572e41..d9f3c6f 100644 --- a/lib/config/dependency_injection.dart +++ b/lib/config/dependency_injection.dart @@ -42,7 +42,7 @@ class AppDependencies { getIt.registerSingleton(ScreenDetailsRepoImp(apiClientInstance: getIt.get(), loggerService: getIt.get())); //repos - getIt.registerSingleton(NativeMethodChannelServiceImp(loggerService: getIt.get(), platform: const MethodChannel('com.example.hmg_qline/foreground'))); + getIt.registerSingleton(NativeMethodChannelServiceImp(loggerService: getIt.get())); //ThirdPartyServices getIt.registerSingleton(ConnectivityServiceImp(connectivityInstance: Connectivity())); diff --git a/lib/main.dart b/lib/main.dart index 6f290c7..d7c139d 100644 --- a/lib/main.dart +++ b/lib/main.dart @@ -1,3 +1,5 @@ +import 'dart:developer'; + import 'package:flutter/material.dart'; import 'package:flutter/services.dart'; import 'package:hmg_qline/view_models/screen_config_view_model.dart'; @@ -11,8 +13,14 @@ import 'package:wakelock_plus/wakelock_plus.dart'; void main() async { WidgetsFlutterBinding.ensureInitialized(); - await AppDependencies.addDependencies(); - await WakelockPlus.enable(); + + try { + await AppDependencies.addDependencies(); + await WakelockPlus.enable(); + } catch (e) { + log('Initialization error: $e'); + } + runApp(const MyApp()); } @@ -25,12 +33,18 @@ class MyApp extends StatelessWidget { builder: (context, constraints) { return OrientationBuilder(builder: (context, orientation) { SizeConfig().init(constraints, orientation); - SystemChrome.setEnabledSystemUIMode(SystemUiMode.manual, overlays: []); + SystemChrome.setEnabledSystemUIMode(SystemUiMode.immersiveSticky); SystemChrome.setPreferredOrientations([DeviceOrientation.portraitUp]); return MultiProvider( providers: [ - ChangeNotifierProvider(create: (context) => getIt.get()), - ChangeNotifierProvider(create: (context) => getIt.get()), + ChangeNotifierProvider( + create: (context) => getIt.get(), + lazy: false, + ), + ChangeNotifierProvider( + create: (context) => getIt.get(), + lazy: false, + ), ], child: MaterialApp( showSemanticsDebugger: false, @@ -51,3 +65,5 @@ class MyApp extends StatelessWidget { ); } } + +// vTJJHSbhMyeq04KfmM3KAg diff --git a/lib/utilities/native_method_handler.dart b/lib/utilities/native_method_handler.dart index 7110c7c..c87c85b 100644 --- a/lib/utilities/native_method_handler.dart +++ b/lib/utilities/native_method_handler.dart @@ -1,38 +1,95 @@ 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(); Future restartApp(); + + Future restartDevice(); + + Future clearAllResources(); + + Future smartRestart({bool forceRestart = false, bool cleanupFirst = true}); } class NativeMethodChannelServiceImp implements NativeMethodChannelService { - MethodChannel platform; + static const MethodChannel _platform = MethodChannel('com.example.hmg_qline/foreground'); LoggerService loggerService; - NativeMethodChannelServiceImp({required this.platform, required this.loggerService}); + NativeMethodChannelServiceImp({required this.loggerService}); @override void reopenApp() async { try { - await platform.invokeMethod(AppStrings.openAppNativeFunctionName); + loggerService.logInfo("Attempting to reopen app"); + await _platform.invokeMethod('reopenApp'); + loggerService.logInfo("App reopened successfully"); } catch (e) { loggerService.logError("Error launching app: $e"); - loggerService.logToFile(message: "Error launching app: $e", source: "reopenApp -> native_method_handler.dart ", type: LogTypeEnum.error); + loggerService.logToFile(message: "Error launching app: $e", source: "reopenApp -> native_method_handler.dart", type: LogTypeEnum.error); } } @override Future restartApp() async { try { - await Restart.restartApp(); + loggerService.logInfo("Initiating app restart"); + await _platform.invokeMethod('restartApp'); + loggerService.logInfo("App restart command sent successfully"); + } catch (e) { + loggerService.logError("Error restarting app: $e"); + loggerService.logToFile(message: "Error restarting app: $e", source: "restartApp -> native_method_handler.dart", type: LogTypeEnum.error); + } + } + + @override + Future restartDevice() async { + try { + loggerService.logInfo("Attempting device restart (requires root)"); + + final result = await _platform.invokeMethod('restartDevice'); + loggerService.logInfo("Device restart initiated: $result"); } catch (e) { - loggerService.logError("Error restarting App : $e"); - loggerService.logToFile(message: "Error restarting app: $e", source: "restartApp -> native_method_handler.dart ", type: LogTypeEnum.error); + loggerService.logError("Device restart failed: $e"); + loggerService.logToFile(message: "Device restart failed: $e", source: "restartDevice -> native_method_handler.dart", type: LogTypeEnum.error); + } + } + + @override + Future clearAllResources() async { + try { + loggerService.logInfo("Clearing all native resources"); + + final result = await _platform.invokeMethod('clearAllResources'); + loggerService.logInfo("All resources cleared: $result"); + } catch (e) { + loggerService.logError("Error clearing resources: $e"); + loggerService.logToFile(message: "Error clearing resources: $e", source: "clearAllResources -> native_method_handler.dart", type: LogTypeEnum.error); + } + } + + // Enhanced restart method with multiple fallback options + @override + Future smartRestart({bool forceRestart = false, bool cleanupFirst = true}) async { + try { + loggerService.logInfo("Starting smart restart - forceRestart: $forceRestart, cleanupFirst: $cleanupFirst"); + + if (cleanupFirst) { + await clearAllResources(); + await Future.delayed(const Duration(seconds: 1)); + } + try { + loggerService.logInfo("Initiating app restart"); + await _platform.invokeMethod('restartApp'); + loggerService.logInfo("App restart command sent successfully"); + } catch (e) { + loggerService.logError("Error restarting app: $e"); + loggerService.logToFile(message: "Error restarting app: $e", source: "restartApp -> native_method_handler.dart", type: LogTypeEnum.error); + } + } catch (primaryError) { + loggerService.logError("Primary restart failed, trying fallback methods: $primaryError"); } } } diff --git a/lib/view_models/queuing_view_model.dart b/lib/view_models/queuing_view_model.dart index a26daf6..eadc77c 100644 --- a/lib/view_models/queuing_view_model.dart +++ b/lib/view_models/queuing_view_model.dart @@ -113,11 +113,7 @@ class QueuingViewModel extends ChangeNotifier { Future onToneCompleted() async { GlobalConfigurationsModel globalConfigurationsModel = getIt.get().globalConfigurationsModel; - if (true) { - await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq)); - } else { - waitAndCallNextTicketIfAvailable(); - } + await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq)); } Future onVoiceCompleted() async { @@ -214,15 +210,8 @@ class QueuingViewModel extends ChangeNotifier { } log("globalConfigurationsModel: ${globalConfigurationsModel.toString()}"); - if (true) { - isCallingInProgress = true; - await audioService.playTone(path: AppAssets.callTone, isMute: !(globalConfigurationsModel.isToneReq)); - } else if (globalConfigurationsModel.isVoiceReq) { - isCallingInProgress = true; - await textToSpeechService.speechText(globalConfigurationsModel: globalConfigurationsModel, ticket: currentTickets.first, isMute: !(globalConfigurationsModel.isVoiceReq)); - } else { - waitAndCallNextTicketIfAvailable(); - } + isCallingInProgress = true; + await audioService.playTone(path: AppAssets.callTone, isMute: !(globalConfigurationsModel.isToneReq)); } } @@ -249,3 +238,6 @@ class QueuingViewModel extends ChangeNotifier { // waitAndCallNextTicketIfAvailable(); // } // } + +// jQ +//ucq diff --git a/lib/view_models/screen_config_view_model.dart b/lib/view_models/screen_config_view_model.dart index 3125745..cd5263b 100644 --- a/lib/view_models/screen_config_view_model.dart +++ b/lib/view_models/screen_config_view_model.dart @@ -54,12 +54,12 @@ class ScreenConfigViewModel extends ChangeNotifier { Future onAppPaused() async { loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppPaused]", source: "onAppPaused -> screen_config_view_model.dart", type: LogTypeEnum.data); - nativeMethodChannelService.restartApp(); + // nativeMethodChannelService.restartApp(); } Future onAppDetached() async { loggerService.logToFile(message: "[didChangeAppLifecycleState] : [onAppDetached]", source: "onAppDetached -> screen_config_view_model.dart", type: LogTypeEnum.data); - nativeMethodChannelService.restartApp(); + // nativeMethodChannelService.restartApp(); } Future waitForIPAndInitializeConfigVM() async { @@ -333,15 +333,17 @@ class ScreenConfigViewModel extends ChangeNotifier { // At midnight, update weather and prayer details if required if (now.day != lastChecked.day) { if (now.difference(now.copyWith(hour: 0, minute: 0, second: 0, millisecond: 0, microsecond: 0)).inMinutes >= 5) { - QueuingViewModel queuingViewModel = getIt.get(); - await queuingViewModel.stopHubConnection(); - nativeMethodChannelService.restartApp(); - if (globalConfigurationsModel.isWeatherReq) { - await getWeatherDetailsFromServer(); - } - if (globalConfigurationsModel.isPrayerTimeReq) { - await getPrayerDetailsFromServer(); - } + await nativeMethodChannelService.smartRestart(forceRestart: true, cleanupFirst: true); + + // if (globalConfigurationsModel.isRssFeedReq) { + // await getRssFeedDetailsFromServer(); + // } + // if (globalConfigurationsModel.isWeatherReq) { + // await getWeatherDetailsFromServer(); + // } + // if (globalConfigurationsModel.isPrayerTimeReq) { + // await getPrayerDetailsFromServer(); + // } lastChecked = now; } } diff --git a/lib/views/common_widgets/app_header.dart b/lib/views/common_widgets/app_header.dart index 7746741..66d7ebc 100644 --- a/lib/views/common_widgets/app_header.dart +++ b/lib/views/common_widgets/app_header.dart @@ -2,8 +2,10 @@ 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/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/screen_config_view_model.dart'; import 'package:hmg_qline/views/common_widgets/app_general_widgets.dart'; import 'package:provider/provider.dart'; @@ -30,9 +32,14 @@ class AppHeader extends StatelessWidget implements PreferredSizeWidget { Row( mainAxisAlignment: MainAxisAlignment.spaceBetween, children: [ - engArabicTextWithSeparatorWidget( - englishText: globalConfigurationsModel.currentServeTextEng ?? "", - arabicText: globalConfigurationsModel.currentServeTextArb ?? "", + InkWell( + onTap: () async { + await getIt.get().smartRestart(); + }, + child: engArabicTextWithSeparatorWidget( + englishText: globalConfigurationsModel.currentServeTextEng ?? "", + arabicText: globalConfigurationsModel.currentServeTextArb ?? "", + ), ), Row( mainAxisAlignment: MainAxisAlignment.end, diff --git a/pubspec.lock b/pubspec.lock index 241b66a..e25a83f 100644 --- a/pubspec.lock +++ b/pubspec.lock @@ -488,14 +488,6 @@ 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: diff --git a/pubspec.yaml b/pubspec.yaml index 58c7ccc..be645d2 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -54,7 +54,7 @@ dependencies: qr_code_scanner_plus: ^2.0.10+1 path_provider: ^2.1.5 flutter_foreground_task: ^9.1.0 - restart_app: ^1.3.2 + # restart_app: ^1.3.2 zo_animated_border: ^1.0.1 animated_flip_counter: ^0.3.4 # smooth_corner: ^1.1.1