@@ -34,22 +34,24 @@ import androidx.compose.ui.res.stringResource
3434import androidx.lifecycle.compose.collectAsStateWithLifecycle
3535import app.morphe.manager.R
3636import app.morphe.manager.domain.installer.InstallerManager
37+ import app.morphe.manager.domain.manager.InstallerPreferenceTokens
3738import app.morphe.manager.domain.manager.PreferencesManager
3839import app.morphe.manager.ui.model.State
3940import app.morphe.manager.ui.screen.patcher.*
41+ import app.morphe.manager.ui.screen.patcher.game.MiniGameState
4042import app.morphe.manager.ui.screen.settings.advanced.NotificationPermissionDialog
4143import app.morphe.manager.ui.screen.settings.system.InstallerSelectionDialog
4244import app.morphe.manager.ui.screen.shared.MorpheAnimations
4345import app.morphe.manager.ui.viewmodel.InstallViewModel
4446import app.morphe.manager.ui.viewmodel.PatcherViewModel
4547import app.morphe.manager.util.APK_MIMETYPE
4648import app.morphe.manager.util.EventEffect
47- import app.morphe.manager.ui.screen.patcher.game.MiniGameState
4849import app.morphe.manager.util.tag
4950import com.google.android.gms.common.ConnectionResult
5051import com.google.android.gms.common.GoogleApiAvailability
5152import kotlinx.coroutines.delay
5253import kotlinx.coroutines.isActive
54+ import kotlinx.coroutines.launch
5355import org.koin.androidx.compose.koinViewModel
5456import org.koin.compose.koinInject
5557import kotlin.math.exp
@@ -141,6 +143,28 @@ fun PatcherScreen(
141143 // Get output file from viewModel
142144 val outputFile = patcherViewModel.outputFile
143145
146+ val autoInstallWithShizuku by prefs.autoInstallWithShizuku.getAsState()
147+ val primaryInstallerPref by prefs.installerPrimary.getAsState()
148+ val promptInstallerOnInstall by prefs.promptInstallerOnInstall.getAsState()
149+
150+ // Auto-install: triggers when success screen appears with Shizuku as primary installer.
151+ // Skipped when promptInstallerOnInstall is enabled - user gets the manual Install button instead.
152+ LaunchedEffect (showSuccessScreen) {
153+ if (! showSuccessScreen) return @LaunchedEffect
154+ if (! autoInstallWithShizuku) return @LaunchedEffect
155+ if (usingMountInstall) return @LaunchedEffect
156+ if (patcherSucceeded != true ) return @LaunchedEffect
157+ if (primaryInstallerPref != InstallerPreferenceTokens .SHIZUKU ) return @LaunchedEffect
158+ if (promptInstallerOnInstall) return @LaunchedEffect
159+ if (installViewModel.installState !is InstallViewModel .InstallState .Ready ) return @LaunchedEffect
160+ delay(300 )
161+ installViewModel.install(
162+ outputFile = outputFile,
163+ originalPackageName = patcherViewModel.packageName,
164+ onPersistApp = { pkg, type -> patcherViewModel.persistPatchedApp(pkg, type) }
165+ )
166+ }
167+
144168 // Progress animation logic: drives displayProgress and showSuccessScreen
145169 LaunchedEffect (patcherSucceeded) {
146170 var lastProgressUpdate = 0.0f
@@ -425,7 +449,12 @@ fun PatcherScreen(
425449 onConfirm = { selectedToken ->
426450 installViewModel.proceedWithSelectedInstaller(selectedToken)
427451 },
428- onOpenShizuku = installerManager::openShizukuApp
452+ onOpenShizuku = installerManager::openShizukuApp,
453+ autoInstallEnabled = autoInstallWithShizuku,
454+ onAutoInstallToggle = { enabled ->
455+ scope.launch { prefs.autoInstallWithShizuku.update(enabled) }
456+ },
457+ installerPromptEnabled = promptInstallerOnInstall
429458 )
430459 }
431460
@@ -468,8 +497,16 @@ fun PatcherScreen(
468497 }
469498
470499 PatcherState .SUCCESS -> {
500+ val effectiveIsInstalling = isInstalling || (
501+ autoInstallWithShizuku &&
502+ primaryInstallerPref == InstallerPreferenceTokens .SHIZUKU &&
503+ patcherSucceeded == true &&
504+ ! usingMountInstall &&
505+ ! promptInstallerOnInstall &&
506+ installState is InstallViewModel .InstallState .Ready
507+ )
471508 PatchingSuccess (
472- isInstalling = isInstalling ,
509+ isInstalling = effectiveIsInstalling ,
473510 isInstalled = isInstalled,
474511 isError = isError,
475512 isConflict = isConflict,
0 commit comments