mirror of
https://github.com/revanced/revanced-manager
synced 2024-05-14 13:56:57 +02:00
fix: pass worker inputs without serialization (#44)
Because androidx.work.Data sucks and causes our app to crash.
This commit is contained in:
parent
1eac42dab8
commit
4302ea8832
@ -91,7 +91,6 @@ dependencies {
|
|||||||
// KotlinX
|
// KotlinX
|
||||||
val serializationVersion = "1.5.1"
|
val serializationVersion = "1.5.1"
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
|
implementation("org.jetbrains.kotlinx:kotlinx-serialization-json:$serializationVersion")
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-serialization-cbor:$serializationVersion")
|
|
||||||
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")
|
implementation("org.jetbrains.kotlinx:kotlinx-collections-immutable:0.3.5")
|
||||||
|
|
||||||
// Room
|
// Room
|
||||||
|
@ -5,6 +5,7 @@ import app.revanced.manager.domain.repository.ReVancedRepository
|
|||||||
import app.revanced.manager.network.api.ManagerAPI
|
import app.revanced.manager.network.api.ManagerAPI
|
||||||
import app.revanced.manager.domain.repository.SourcePersistenceRepository
|
import app.revanced.manager.domain.repository.SourcePersistenceRepository
|
||||||
import app.revanced.manager.domain.repository.SourceRepository
|
import app.revanced.manager.domain.repository.SourceRepository
|
||||||
|
import app.revanced.manager.domain.worker.WorkerRepository
|
||||||
import org.koin.core.module.dsl.singleOf
|
import org.koin.core.module.dsl.singleOf
|
||||||
import org.koin.dsl.module
|
import org.koin.dsl.module
|
||||||
|
|
||||||
@ -14,4 +15,5 @@ val repositoryModule = module {
|
|||||||
singleOf(::SourcePersistenceRepository)
|
singleOf(::SourcePersistenceRepository)
|
||||||
singleOf(::PatchSelectionRepository)
|
singleOf(::PatchSelectionRepository)
|
||||||
singleOf(::SourceRepository)
|
singleOf(::SourceRepository)
|
||||||
|
singleOf(::WorkerRepository)
|
||||||
}
|
}
|
@ -0,0 +1,7 @@
|
|||||||
|
package app.revanced.manager.domain.worker
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import androidx.work.CoroutineWorker
|
||||||
|
import androidx.work.WorkerParameters
|
||||||
|
|
||||||
|
abstract class Worker<ARGS>(context: Context, parameters: WorkerParameters) : CoroutineWorker(context, parameters)
|
@ -0,0 +1,36 @@
|
|||||||
|
package app.revanced.manager.domain.worker
|
||||||
|
|
||||||
|
import android.app.Application
|
||||||
|
import androidx.work.ExistingWorkPolicy
|
||||||
|
import androidx.work.OneTimeWorkRequest
|
||||||
|
import androidx.work.OutOfQuotaPolicy
|
||||||
|
import androidx.work.WorkManager
|
||||||
|
import java.util.UUID
|
||||||
|
|
||||||
|
class WorkerRepository(app: Application) {
|
||||||
|
val workManager = WorkManager.getInstance(app)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The standard WorkManager communication APIs use [androidx.work.Data], which has too many limitations.
|
||||||
|
* We can get around those limits by passing inputs using global variables instead.
|
||||||
|
*/
|
||||||
|
val workerInputs = mutableMapOf<UUID, Any>()
|
||||||
|
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
fun <A : Any, W : Worker<A>> claimInput(worker: W): A {
|
||||||
|
val data = workerInputs[worker.id] ?: throw IllegalStateException("Worker was not launched via WorkerRepository")
|
||||||
|
workerInputs.remove(worker.id)
|
||||||
|
|
||||||
|
return data as A
|
||||||
|
}
|
||||||
|
|
||||||
|
inline fun <reified W : Worker<A>, A : Any> launchExpedited(name: String, input: A): UUID {
|
||||||
|
val request =
|
||||||
|
OneTimeWorkRequest.Builder(W::class.java) // create Worker
|
||||||
|
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
|
||||||
|
.build()
|
||||||
|
workerInputs[request.id] = input
|
||||||
|
workManager.enqueueUniqueWork(name, ExistingWorkPolicy.REPLACE, request)
|
||||||
|
return request.id
|
||||||
|
}
|
||||||
|
}
|
@ -3,10 +3,9 @@ package app.revanced.manager.patcher.worker
|
|||||||
import android.content.Context
|
import android.content.Context
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.util.serialize
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
import kotlinx.collections.immutable.persistentListOf
|
import kotlinx.collections.immutable.persistentListOf
|
||||||
import kotlinx.serialization.SerialName
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
|
|
||||||
sealed class Progress {
|
sealed class Progress {
|
||||||
object Unpacking : Progress()
|
object Unpacking : Progress()
|
||||||
@ -18,23 +17,19 @@ sealed class Progress {
|
|||||||
object Saving : Progress()
|
object Saving : Progress()
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
enum class State {
|
enum class State {
|
||||||
WAITING, COMPLETED, FAILED
|
WAITING, COMPLETED, FAILED
|
||||||
}
|
}
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class SubStep(
|
class SubStep(
|
||||||
val name: String,
|
val name: String,
|
||||||
val state: State = State.WAITING,
|
val state: State = State.WAITING,
|
||||||
@SerialName("msg")
|
|
||||||
val message: String? = null
|
val message: String? = null
|
||||||
)
|
)
|
||||||
|
|
||||||
@Serializable
|
|
||||||
class Step(
|
class Step(
|
||||||
@StringRes val name: Int,
|
@StringRes val name: Int,
|
||||||
val substeps: List<SubStep>,
|
val substeps: ImmutableList<SubStep>,
|
||||||
val state: State = State.WAITING
|
val state: State = State.WAITING
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -58,7 +53,7 @@ class PatcherProgressManager(context: Context, selectedPatches: List<String>) {
|
|||||||
|
|
||||||
Step(step.name, step.substeps.mapIndexed { index, subStep ->
|
Step(step.name, step.substeps.mapIndexed { index, subStep ->
|
||||||
if (index != key.substep) subStep else SubStep(subStep.name, state, message)
|
if (index != key.substep) subStep else SubStep(subStep.name, state, message)
|
||||||
}, newStepState)
|
}.toImmutableList(), newStepState)
|
||||||
}
|
}
|
||||||
|
|
||||||
val isFinal = isLastSubStep && key.step == steps.lastIndex
|
val isFinal = isLastSubStep && key.step == steps.lastIndex
|
||||||
@ -95,7 +90,7 @@ class PatcherProgressManager(context: Context, selectedPatches: List<String>) {
|
|||||||
|
|
||||||
fun success() = updateCurrent(State.COMPLETED)
|
fun success() = updateCurrent(State.COMPLETED)
|
||||||
|
|
||||||
fun workData() = steps.serialize()
|
fun getProgress(): List<Step> = steps
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
/**
|
/**
|
||||||
@ -110,7 +105,7 @@ class PatcherProgressManager(context: Context, selectedPatches: List<String>) {
|
|||||||
|
|
||||||
private fun generatePatchesStep(selectedPatches: List<String>) = Step(
|
private fun generatePatchesStep(selectedPatches: List<String>) = Step(
|
||||||
R.string.patcher_step_group_patching,
|
R.string.patcher_step_group_patching,
|
||||||
selectedPatches.map { SubStep(it) }
|
selectedPatches.map { SubStep(it) }.toImmutableList()
|
||||||
)
|
)
|
||||||
|
|
||||||
fun generateSteps(context: Context, selectedPatches: List<String>) = mutableListOf(
|
fun generateSteps(context: Context, selectedPatches: List<String>) = mutableListOf(
|
||||||
|
@ -11,36 +11,39 @@ import android.os.PowerManager
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import android.view.WindowManager
|
import android.view.WindowManager
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.work.CoroutineWorker
|
|
||||||
import androidx.work.ForegroundInfo
|
import androidx.work.ForegroundInfo
|
||||||
import androidx.work.WorkerParameters
|
import androidx.work.WorkerParameters
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.domain.repository.SourceRepository
|
import app.revanced.manager.domain.repository.SourceRepository
|
||||||
|
import app.revanced.manager.domain.worker.Worker
|
||||||
|
import app.revanced.manager.domain.worker.WorkerRepository
|
||||||
import app.revanced.manager.patcher.Session
|
import app.revanced.manager.patcher.Session
|
||||||
import app.revanced.manager.patcher.aapt.Aapt
|
import app.revanced.manager.patcher.aapt.Aapt
|
||||||
import app.revanced.manager.util.PatchesSelection
|
import app.revanced.manager.util.PatchesSelection
|
||||||
import app.revanced.manager.util.deserialize
|
|
||||||
import app.revanced.manager.util.tag
|
import app.revanced.manager.util.tag
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
import app.revanced.patcher.extensions.PatchExtensions.patchName
|
||||||
|
import kotlinx.collections.immutable.ImmutableList
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.first
|
import kotlinx.coroutines.flow.first
|
||||||
import kotlinx.serialization.Serializable
|
|
||||||
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
|
||||||
import java.io.FileNotFoundException
|
import java.io.FileNotFoundException
|
||||||
|
|
||||||
class PatcherWorker(context: Context, parameters: WorkerParameters) :
|
class PatcherWorker(context: Context, parameters: WorkerParameters) :
|
||||||
CoroutineWorker(context, parameters),
|
Worker<PatcherWorker.Args>(context, parameters),
|
||||||
KoinComponent {
|
KoinComponent {
|
||||||
private val sourceRepository: SourceRepository by inject()
|
private val sourceRepository: SourceRepository by inject()
|
||||||
|
private val workerRepository: WorkerRepository by inject()
|
||||||
|
|
||||||
@Serializable
|
|
||||||
data class Args(
|
data class Args(
|
||||||
val input: String,
|
val input: String,
|
||||||
val output: String,
|
val output: String,
|
||||||
val selectedPatches: PatchesSelection,
|
val selectedPatches: PatchesSelection,
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val packageVersion: String
|
val packageVersion: String,
|
||||||
|
val progress: MutableStateFlow<ImmutableList<Step>>
|
||||||
)
|
)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -75,7 +78,7 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
|
|||||||
return Result.failure()
|
return Result.failure()
|
||||||
}
|
}
|
||||||
|
|
||||||
val args = inputData.deserialize<Args>()!!
|
val args = workerRepository.claimInput(this)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// This does not always show up for some reason.
|
// This does not always show up for some reason.
|
||||||
@ -113,9 +116,11 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
|
|||||||
val progressManager =
|
val progressManager =
|
||||||
PatcherProgressManager(applicationContext, args.selectedPatches.flatMap { it.value })
|
PatcherProgressManager(applicationContext, args.selectedPatches.flatMap { it.value })
|
||||||
|
|
||||||
suspend fun updateProgress(progress: Progress) {
|
val progressFlow = args.progress
|
||||||
progressManager.handle(progress)
|
|
||||||
setProgress(progressManager.workData())
|
fun updateProgress(progress: Progress?) {
|
||||||
|
progress?.let { progressManager.handle(it) }
|
||||||
|
progressFlow.value = progressManager.getProgress().toImmutableList()
|
||||||
}
|
}
|
||||||
|
|
||||||
return try {
|
return try {
|
||||||
@ -143,11 +148,13 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
|
|||||||
|
|
||||||
Log.i(tag, "Patching succeeded".logFmt())
|
Log.i(tag, "Patching succeeded".logFmt())
|
||||||
progressManager.success()
|
progressManager.success()
|
||||||
Result.success(progressManager.workData())
|
Result.success()
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
Log.e(tag, "Got exception while patching".logFmt(), e)
|
Log.e(tag, "Got exception while patching".logFmt(), e)
|
||||||
progressManager.failure(e)
|
progressManager.failure(e)
|
||||||
Result.failure(progressManager.workData())
|
Result.failure()
|
||||||
|
} finally {
|
||||||
|
updateProgress(null)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -11,8 +11,6 @@ import androidx.compose.foundation.verticalScroll
|
|||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Cancel
|
import androidx.compose.material.icons.filled.Cancel
|
||||||
import androidx.compose.material.icons.filled.CheckCircle
|
import androidx.compose.material.icons.filled.CheckCircle
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowDown
|
|
||||||
import androidx.compose.material.icons.filled.KeyboardArrowUp
|
|
||||||
import androidx.compose.material.icons.outlined.HelpOutline
|
import androidx.compose.material.icons.outlined.HelpOutline
|
||||||
import androidx.compose.material.icons.outlined.MoreVert
|
import androidx.compose.material.icons.outlined.MoreVert
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
@ -33,6 +31,7 @@ import androidx.compose.ui.semantics.semantics
|
|||||||
import androidx.compose.ui.text.style.TextOverflow
|
import androidx.compose.ui.text.style.TextOverflow
|
||||||
import androidx.compose.ui.unit.Dp
|
import androidx.compose.ui.unit.Dp
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.patcher.worker.Step
|
import app.revanced.manager.patcher.worker.Step
|
||||||
import app.revanced.manager.patcher.worker.State
|
import app.revanced.manager.patcher.worker.State
|
||||||
@ -41,7 +40,6 @@ import app.revanced.manager.ui.component.AppTopBar
|
|||||||
import app.revanced.manager.ui.component.ArrowButton
|
import app.revanced.manager.ui.component.ArrowButton
|
||||||
import app.revanced.manager.ui.viewmodel.InstallerViewModel
|
import app.revanced.manager.ui.viewmodel.InstallerViewModel
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
import kotlin.math.exp
|
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@ -52,8 +50,9 @@ fun InstallerScreen(
|
|||||||
) {
|
) {
|
||||||
val exportApkLauncher =
|
val exportApkLauncher =
|
||||||
rememberLauncherForActivityResult(CreateDocument(APK_MIMETYPE), vm::export)
|
rememberLauncherForActivityResult(CreateDocument(APK_MIMETYPE), vm::export)
|
||||||
val patcherState by vm.patcherState.observeAsState(vm.initialState)
|
val patcherState by vm.patcherState.observeAsState(null)
|
||||||
val canInstall by remember { derivedStateOf { patcherState.succeeded == true && (vm.installedPackageName != null || !vm.isInstalling) } }
|
val steps by vm.progress.collectAsStateWithLifecycle()
|
||||||
|
val canInstall by remember { derivedStateOf { patcherState == true && (vm.installedPackageName != null || !vm.isInstalling) } }
|
||||||
|
|
||||||
AppScaffold(
|
AppScaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
@ -77,7 +76,7 @@ fun InstallerScreen(
|
|||||||
.verticalScroll(rememberScrollState())
|
.verticalScroll(rememberScrollState())
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
) {
|
) {
|
||||||
patcherState.steps.forEach {
|
steps.forEach {
|
||||||
InstallStep(it)
|
InstallStep(it)
|
||||||
}
|
}
|
||||||
Spacer(modifier = Modifier.weight(1f))
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
@ -18,18 +18,19 @@ import androidx.lifecycle.map
|
|||||||
import androidx.work.*
|
import androidx.work.*
|
||||||
import app.revanced.manager.domain.manager.KeystoreManager
|
import app.revanced.manager.domain.manager.KeystoreManager
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.domain.worker.WorkerRepository
|
||||||
import app.revanced.manager.patcher.worker.PatcherProgressManager
|
import app.revanced.manager.patcher.worker.PatcherProgressManager
|
||||||
import app.revanced.manager.patcher.worker.PatcherWorker
|
import app.revanced.manager.patcher.worker.PatcherWorker
|
||||||
import app.revanced.manager.patcher.worker.Step
|
|
||||||
import app.revanced.manager.service.InstallService
|
import app.revanced.manager.service.InstallService
|
||||||
import app.revanced.manager.service.UninstallService
|
import app.revanced.manager.service.UninstallService
|
||||||
import app.revanced.manager.util.AppInfo
|
import app.revanced.manager.util.AppInfo
|
||||||
import app.revanced.manager.util.PM
|
import app.revanced.manager.util.PM
|
||||||
import app.revanced.manager.util.PatchesSelection
|
import app.revanced.manager.util.PatchesSelection
|
||||||
import app.revanced.manager.util.deserialize
|
|
||||||
import app.revanced.manager.util.serialize
|
|
||||||
import app.revanced.manager.util.tag
|
import app.revanced.manager.util.tag
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
|
import kotlinx.collections.immutable.toImmutableList
|
||||||
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
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
|
||||||
@ -43,6 +44,7 @@ class InstallerViewModel(
|
|||||||
private val keystoreManager: KeystoreManager by inject()
|
private val keystoreManager: KeystoreManager by inject()
|
||||||
private val app: Application by inject()
|
private val app: Application by inject()
|
||||||
private val pm: PM by inject()
|
private val pm: PM by inject()
|
||||||
|
private val workerRepository: WorkerRepository by inject()
|
||||||
|
|
||||||
val packageName: String = input.packageName
|
val packageName: String = input.packageName
|
||||||
private val outputFile = File(app.cacheDir, "output.apk")
|
private val outputFile = File(app.cacheDir, "output.apk")
|
||||||
@ -57,38 +59,31 @@ class InstallerViewModel(
|
|||||||
|
|
||||||
private val workManager = WorkManager.getInstance(app)
|
private val workManager = WorkManager.getInstance(app)
|
||||||
|
|
||||||
private val patcherWorker =
|
private val _progress = MutableStateFlow(PatcherProgressManager.generateSteps(
|
||||||
OneTimeWorkRequest.Builder(PatcherWorker::class.java) // create Worker
|
app,
|
||||||
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).setInputData(
|
selectedPatches.flatMap { (_, selected) -> selected }
|
||||||
PatcherWorker.Args(
|
).toImmutableList())
|
||||||
input.path!!.absolutePath,
|
val progress = _progress.asStateFlow()
|
||||||
outputFile.path,
|
|
||||||
selectedPatches,
|
|
||||||
input.packageName,
|
|
||||||
input.packageInfo!!.versionName,
|
|
||||||
).serialize()
|
|
||||||
).build()
|
|
||||||
|
|
||||||
val initialState = PatcherState(
|
private val patcherWorkerId =
|
||||||
succeeded = null,
|
workerRepository.launchExpedited<PatcherWorker, PatcherWorker.Args>(
|
||||||
steps = PatcherProgressManager.generateSteps(
|
"patching", PatcherWorker.Args(
|
||||||
app,
|
input.path!!.absolutePath,
|
||||||
selectedPatches.flatMap { (_, selected) -> selected }
|
outputFile.path,
|
||||||
|
selectedPatches,
|
||||||
|
input.packageName,
|
||||||
|
input.packageInfo!!.versionName,
|
||||||
|
_progress
|
||||||
|
)
|
||||||
)
|
)
|
||||||
)
|
|
||||||
val patcherState =
|
val patcherState =
|
||||||
workManager.getWorkInfoByIdLiveData(patcherWorker.id).map { workInfo: WorkInfo ->
|
workManager.getWorkInfoByIdLiveData(patcherWorkerId).map { workInfo: WorkInfo ->
|
||||||
var status: Boolean? = null
|
when (workInfo.state) {
|
||||||
val steps = when (workInfo.state) {
|
WorkInfo.State.SUCCEEDED -> true
|
||||||
WorkInfo.State.RUNNING -> workInfo.progress
|
WorkInfo.State.FAILED -> false
|
||||||
WorkInfo.State.FAILED, WorkInfo.State.SUCCEEDED -> workInfo.outputData.also {
|
|
||||||
status = workInfo.state == WorkInfo.State.SUCCEEDED
|
|
||||||
}
|
|
||||||
|
|
||||||
else -> null
|
else -> null
|
||||||
}?.deserialize<List<Step>>()
|
}
|
||||||
|
|
||||||
PatcherState(status, steps ?: initialState.steps)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private val installBroadcastReceiver = object : BroadcastReceiver() {
|
private val installBroadcastReceiver = object : BroadcastReceiver() {
|
||||||
@ -114,7 +109,6 @@ class InstallerViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
init {
|
init {
|
||||||
workManager.enqueueUniqueWork("patching", ExistingWorkPolicy.KEEP, patcherWorker)
|
|
||||||
app.registerReceiver(installBroadcastReceiver, IntentFilter().apply {
|
app.registerReceiver(installBroadcastReceiver, IntentFilter().apply {
|
||||||
addAction(InstallService.APP_INSTALL_ACTION)
|
addAction(InstallService.APP_INSTALL_ACTION)
|
||||||
addAction(UninstallService.APP_UNINSTALL_ACTION)
|
addAction(UninstallService.APP_UNINSTALL_ACTION)
|
||||||
@ -124,7 +118,7 @@ class InstallerViewModel(
|
|||||||
override fun onCleared() {
|
override fun onCleared() {
|
||||||
super.onCleared()
|
super.onCleared()
|
||||||
app.unregisterReceiver(installBroadcastReceiver)
|
app.unregisterReceiver(installBroadcastReceiver)
|
||||||
workManager.cancelWorkById(patcherWorker.id)
|
workManager.cancelWorkById(patcherWorkerId)
|
||||||
|
|
||||||
outputFile.delete()
|
outputFile.delete()
|
||||||
signedFile.delete()
|
signedFile.delete()
|
||||||
@ -165,6 +159,4 @@ class InstallerViewModel(
|
|||||||
isInstalling = false
|
isInstalling = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class PatcherState(val succeeded: Boolean?, val steps: List<Step>)
|
|
||||||
}
|
}
|
@ -12,8 +12,6 @@ import androidx.lifecycle.Lifecycle
|
|||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import androidx.work.Data
|
|
||||||
import androidx.work.workDataOf
|
|
||||||
import io.ktor.http.Url
|
import io.ktor.http.Url
|
||||||
import kotlinx.coroutines.CoroutineScope
|
import kotlinx.coroutines.CoroutineScope
|
||||||
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
import kotlinx.coroutines.ExperimentalCoroutinesApi
|
||||||
@ -21,10 +19,6 @@ import kotlinx.coroutines.flow.Flow
|
|||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.flatMapLatest
|
import kotlinx.coroutines.flow.flatMapLatest
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.serialization.ExperimentalSerializationApi
|
|
||||||
import kotlinx.serialization.cbor.Cbor
|
|
||||||
import kotlinx.serialization.decodeFromByteArray
|
|
||||||
import kotlinx.serialization.encodeToByteArray
|
|
||||||
|
|
||||||
typealias PatchesSelection = Map<Int, List<String>>
|
typealias PatchesSelection = Map<Int, List<String>>
|
||||||
|
|
||||||
@ -99,13 +93,3 @@ inline fun <T, reified R, C> Flow<Iterable<T>>.flatMapLatestAndCombine(
|
|||||||
combiner(it)
|
combiner(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const val workDataKey = "payload"
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
inline fun <reified T> T.serialize(): Data =
|
|
||||||
workDataOf(workDataKey to Cbor.Default.encodeToByteArray(this))
|
|
||||||
|
|
||||||
@OptIn(ExperimentalSerializationApi::class)
|
|
||||||
inline fun <reified T> Data.deserialize(): T? =
|
|
||||||
getByteArray(workDataKey)?.let { Cbor.Default.decodeFromByteArray(it) }
|
|
Loading…
x
Reference in New Issue
Block a user