@@ -2,6 +2,7 @@ package com.github.capntrips.kernelflasher
22
33import android.animation.ObjectAnimator
44import android.animation.PropertyValuesHolder
5+ import android.app.Activity
56import android.content.ComponentName
67import android.content.Intent
78import android.content.ServiceConnection
@@ -16,11 +17,27 @@ import androidx.activity.compose.BackHandler
1617import androidx.activity.compose.setContent
1718import androidx.compose.animation.AnimatedVisibilityScope
1819import androidx.compose.animation.ExperimentalAnimationApi
20+ import androidx.compose.foundation.layout.Arrangement
21+ import androidx.compose.foundation.layout.Column
22+ import androidx.compose.foundation.layout.padding
1923import androidx.compose.material.ExperimentalMaterialApi
24+ import androidx.compose.material.TextButton
25+ import androidx.compose.material3.AlertDialog
2026import androidx.compose.material3.ExperimentalMaterial3Api
27+ import androidx.compose.material3.MaterialTheme
28+ import androidx.compose.material3.Text
2129import androidx.compose.runtime.Composable
30+ import androidx.compose.runtime.LaunchedEffect
31+ import androidx.compose.runtime.getValue
32+ import androidx.compose.runtime.mutableStateOf
33+ import androidx.compose.runtime.remember
34+ import androidx.compose.runtime.setValue
35+ import androidx.compose.ui.Modifier
36+ import androidx.compose.ui.platform.LocalContext
2237import androidx.compose.ui.res.stringResource
38+ import androidx.compose.ui.text.font.FontWeight
2339import androidx.compose.ui.unit.ExperimentalUnitApi
40+ import androidx.compose.ui.unit.dp
2441import androidx.core.animation.doOnEnd
2542import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
2643import androidx.core.view.WindowCompat
@@ -30,6 +47,7 @@ import androidx.navigation.NavBackStackEntry
3047import androidx.navigation.compose.NavHost
3148import androidx.navigation.compose.composable
3249import androidx.navigation.compose.rememberNavController
50+ import com.github.capntrips.kernelflasher.ui.components.DialogButton
3351import com.github.capntrips.kernelflasher.ui.screens.RefreshableScreen
3452import com.github.capntrips.kernelflasher.ui.screens.backups.BackupsContent
3553import com.github.capntrips.kernelflasher.ui.screens.backups.SlotBackupsContent
@@ -49,7 +67,7 @@ import com.topjohnwu.superuser.ipc.RootService
4967import com.topjohnwu.superuser.nio.FileSystemManager
5068import kotlinx.serialization.ExperimentalSerializationApi
5169import java.io.File
52-
70+ import kotlin.system.exitProcess
5371
5472@ExperimentalAnimationApi
5573@ExperimentalMaterialApi
@@ -185,6 +203,20 @@ class MainActivity : ComponentActivity() {
185203 MainViewModel (application, fileSystemManager, navController)
186204 }
187205 val mainViewModel = viewModel!!
206+
207+ val context = LocalContext .current
208+ val dialogData = viewModel!! .updateDialogData
209+ LaunchedEffect (Unit ) {
210+ AppUpdater .checkForUpdate(
211+ context.applicationContext,
212+ BuildConfig .VERSION_NAME
213+ ) { title, lines, confirm ->
214+ viewModel!! .showUpdateDialog(title, lines, confirm)
215+ }
216+ }
217+
218+ var showExitDialog by remember { mutableStateOf(false ) }
219+
188220 KernelFlasherTheme {
189221 if (! mainViewModel.hasError) {
190222 mainListener = MainListener {
@@ -195,11 +227,15 @@ class MainActivity : ComponentActivity() {
195227 val backupsViewModel = mainViewModel.backups
196228 val updatesViewModel = mainViewModel.updates
197229 val rebootViewModel = mainViewModel.reboot
198- BackHandler (enabled = mainViewModel.isRefreshing, onBack = {})
230+ BackHandler (enabled = ! mainViewModel.isRefreshing, onBack = {})
231+ // New back handler for exit
232+ BackHandler (enabled = true ) {
233+ showExitDialog = true
234+ }
199235 val slotContentA: @Composable AnimatedVisibilityScope .(NavBackStackEntry ) -> Unit = { backStackEntry ->
200236 val slotSuffix = " _a"
201237 val slotViewModel = slotViewModelA
202- if (slotViewModel!! .wasFlashSuccess != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
238+ if (slotViewModel!! .wasFlashSuccess.value != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
203239 slotViewModel.clearFlash(this @MainActivity)
204240 }
205241 RefreshableScreen (mainViewModel, navController, swipeEnabled = true ) {
@@ -210,7 +246,7 @@ class MainActivity : ComponentActivity() {
210246 val slotContentB: @Composable AnimatedVisibilityScope .(NavBackStackEntry ) -> Unit = { backStackEntry ->
211247 val slotSuffix = " _b"
212248 val slotViewModel = slotViewModelB
213- if (slotViewModel!! .wasFlashSuccess != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
249+ if (slotViewModel!! .wasFlashSuccess.value != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
214250 slotViewModel.clearFlash(this @MainActivity)
215251 }
216252 RefreshableScreen (mainViewModel, navController, swipeEnabled = true ) {
@@ -221,7 +257,7 @@ class MainActivity : ComponentActivity() {
221257 val slotContent: @Composable AnimatedVisibilityScope .(NavBackStackEntry ) -> Unit = { backStackEntry ->
222258 val slotSuffix = " "
223259 val slotViewModel = slotViewModelA
224- if (slotViewModel!! .wasFlashSuccess != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
260+ if (slotViewModel!! .wasFlashSuccess.value != null && listOf (" slot{slotSuffix}" , " slot" ).any { navController.currentDestination!! .route.equals(it) }) {
225261 slotViewModel.clearFlash(this @MainActivity)
226262 }
227263 RefreshableScreen (mainViewModel, navController, swipeEnabled = true ) {
@@ -424,6 +460,61 @@ class MainActivity : ComponentActivity() {
424460 } else {
425461 ErrorScreen (mainViewModel.error)
426462 }
463+
464+ if (dialogData != null ) {
465+ AlertDialog (
466+ onDismissRequest = { viewModel!! .hideUpdateDialog() },
467+ title = {
468+ Text (
469+ dialogData!! .title,
470+ style = MaterialTheme .typography.titleLarge,
471+ fontWeight = FontWeight .Bold
472+ )
473+ },
474+ text = {
475+ Column (verticalArrangement = Arrangement .spacedBy(8 .dp)) {
476+ dialogData!! .changelog.forEach {
477+ Text (it, fontWeight = FontWeight .Bold )
478+ }
479+ }
480+ },
481+ confirmButton = {
482+ DialogButton (" Update APK" ) {
483+ viewModel!! .hideUpdateDialog()
484+ dialogData!! .onConfirm()
485+ }
486+ },
487+ dismissButton = {
488+ DialogButton (" CANCEL" ) {
489+ viewModel!! .hideUpdateDialog()
490+ }
491+ },
492+ modifier = Modifier .padding(16 .dp)
493+ )
494+ }
495+
496+ if (showExitDialog) {
497+ AlertDialog (
498+ onDismissRequest = { showExitDialog = false },
499+ title = { Text (" Exit App" ) },
500+ text = { Text (" Are you sure you want to exit?" ) },
501+ confirmButton = {
502+ TextButton (onClick = {
503+ (context as ? Activity )?.let {
504+ it.finishAffinity()
505+ exitProcess(0 )
506+ }
507+ }) {
508+ Text (" Yes" )
509+ }
510+ },
511+ dismissButton = {
512+ TextButton (onClick = { showExitDialog = false }) {
513+ Text (" No" )
514+ }
515+ }
516+ )
517+ }
427518 }
428519 }
429520 }
0 commit comments