fix(installer): sign and install on threads

This is needed to avoid ANRs because it takes a while if the Apk is 100+
MB.
This commit is contained in:
Ax333l 2023-07-07 12:31:31 +02:00
parent d611ae7e9c
commit a09e00c7a9
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
3 changed files with 22 additions and 11 deletions

View File

@ -15,6 +15,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.map import androidx.lifecycle.map
import androidx.lifecycle.viewModelScope
import androidx.work.WorkInfo import androidx.work.WorkInfo
import androidx.work.WorkManager import androidx.work.WorkManager
import app.revanced.manager.domain.manager.KeystoreManager import app.revanced.manager.domain.manager.KeystoreManager
@ -32,8 +33,11 @@ import app.revanced.manager.util.toast
import app.revanced.patcher.logging.Logger import app.revanced.patcher.logging.Logger
import kotlinx.collections.immutable.ImmutableList import kotlinx.collections.immutable.ImmutableList
import kotlinx.collections.immutable.toImmutableList import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent import org.koin.core.component.KoinComponent
import org.koin.core.component.inject import org.koin.core.component.inject
import java.io.File import java.io.File
@ -146,10 +150,12 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
signedFile.delete() signedFile.delete()
} }
private fun signApk(): Boolean { private suspend fun signApk(): Boolean {
if (!hasSigned) { if (!hasSigned) {
try { try {
keystoreManager.sign(outputFile, signedFile) withContext(Dispatchers.Default) {
keystoreManager.sign(outputFile, signedFile)
}
} catch (e: Exception) { } catch (e: Exception) {
Log.e(tag, "Got exception while signing", e) Log.e(tag, "Got exception while signing", e)
app.toast(app.getString(R.string.sign_fail, e::class.simpleName)) app.toast(app.getString(R.string.sign_fail, e::class.simpleName))
@ -160,22 +166,27 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
return true return true
} }
fun export(uri: Uri?) = uri?.let { fun export(uri: Uri?) = viewModelScope.launch {
if (signApk()) { uri?.let {
Files.copy(signedFile.toPath(), app.contentResolver.openOutputStream(it)) if (signApk()) {
app.toast(app.getString(R.string.export_app_success)) withContext(Dispatchers.IO) {
app.contentResolver.openOutputStream(it)
.use { stream -> Files.copy(signedFile.toPath(), stream) }
}
app.toast(app.getString(R.string.export_app_success))
}
} }
} }
fun installOrOpen() { fun installOrOpen() = viewModelScope.launch {
installedPackageName?.let { installedPackageName?.let {
pm.launch(it) pm.launch(it)
return return@launch
} }
isInstalling = true isInstalling = true
try { try {
if (!signApk()) return if (!signApk()) return@launch
pm.installApp(listOf(signedFile)) pm.installApp(listOf(signedFile))
} finally { } finally {
isInstalling = false isInstalling = false

View File

@ -39,7 +39,7 @@ class UpdateProgressViewModel(
} }
} }
fun installUpdate() { fun installUpdate() = viewModelScope.launch {
pm.installApp(listOf(location)) pm.installApp(listOf(location))
} }

View File

@ -107,7 +107,7 @@ class PM(
}) })
} }
fun installApp(apks: List<File>) { suspend fun installApp(apks: List<File>) = withContext(Dispatchers.IO) {
val packageInstaller = app.packageManager.packageInstaller val packageInstaller = app.packageManager.packageInstaller
packageInstaller.openSession(packageInstaller.createSession(sessionParams)).use { session -> packageInstaller.openSession(packageInstaller.createSession(sessionParams)).use { session ->
apks.forEach { apk -> session.writeApk(apk) } apks.forEach { apk -> session.writeApk(apk) }