feat: patching screen

This commit is contained in:
Canny 2022-09-23 11:32:18 +03:00
parent 09e1082c5e
commit a1e172a852
No known key found for this signature in database
GPG Key ID: 395CCB0AA979F27B
18 changed files with 259 additions and 130 deletions

View File

@ -86,6 +86,7 @@ dependencies {
val koinVersion = "3.2.0"
implementation("io.insert-koin:koin-android:$koinVersion")
implementation("io.insert-koin:koin-androidx-compose:$koinVersion")
implementation("io.insert-koin:koin-androidx-workmanager:3.2.1")
// Compose
val composeVersion = "1.3.0-alpha03"
@ -117,7 +118,7 @@ dependencies {
implementation("io.ktor:ktor-serialization-kotlinx-json:$ktorVersion")
// ReVanced
implementation("app.revanced:revanced-patcher:4.2.3")
implementation("app.revanced:revanced-patcher:4.4.2")
// Coil for network image
implementation("io.coil-kt:coil-compose:2.1.0")
@ -125,4 +126,5 @@ dependencies {
// Signing & aligning
implementation("org.bouncycastle:bcpkix-jdk15on:1.70")
implementation("com.android.tools.build:apksig:7.2.2")
}

View File

@ -33,8 +33,19 @@
android:supportsRtl="true"
android:largeHeap="true"
android:theme="@style/Theme.ReVancedManager"
tools:targetApi="33">
<provider
android:name="androidx.startup.InitializationProvider"
android:authorities="${applicationId}.androidx-startup"
android:exported="false"
tools:node="merge">
<meta-data
android:name="androidx.work.WorkManagerInitializer"
android:value="androidx.startup"
tools:node="remove" />
</provider>
<activity
android:name=".MainActivity"
android:exported="true"

View File

@ -15,6 +15,7 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import app.revanced.manager.preferences.PreferencesManager
import app.revanced.manager.ui.navigation.AppDestination
import app.revanced.manager.ui.screen.MainDashboardScreen
import app.revanced.manager.ui.screen.PatchingScreen
import app.revanced.manager.ui.screen.subscreens.AppSelectorSubscreen
import app.revanced.manager.ui.screen.subscreens.PatchesSelectorSubscreen
import app.revanced.manager.ui.theme.ReVancedManagerTheme
@ -48,10 +49,9 @@ class MainActivity : ComponentActivity() {
) { destination ->
when (destination) {
is AppDestination.Dashboard -> MainDashboardScreen(navigator = navigator)
is AppDestination.AppSelector -> AppSelectorSubscreen(
navigator = navigator
)
is AppDestination.AppSelector -> AppSelectorSubscreen(navigator = navigator)
is AppDestination.PatchSelector -> PatchesSelectorSubscreen(navigator = navigator)
is AppDestination.Patcher -> PatchingScreen(navigator = navigator)
}
}
}

View File

@ -1,11 +1,9 @@
package app.revanced.manager
import android.app.Application
import app.revanced.manager.di.httpModule
import app.revanced.manager.di.preferencesModule
import app.revanced.manager.di.repositoryModule
import app.revanced.manager.di.viewModelModule
import app.revanced.manager.di.*
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.core.context.startKoin
class ManagerApplication : Application() {
@ -14,7 +12,8 @@ class ManagerApplication : Application() {
startKoin {
androidContext(this@ManagerApplication)
modules(httpModule, preferencesModule, viewModelModule, repositoryModule)
workManagerFactory()
modules(httpModule, preferencesModule, viewModelModule, repositoryModule, workerModule)
}
}
}

View File

@ -29,7 +29,7 @@ val client = HttpClient(Android) {
class API(private val repository: GitHubRepository, private val prefs: PreferencesManager) {
private suspend fun findAsset(repo: String, file: String): PatchesAsset {
suspend fun findAsset(repo: String, file: String): PatchesAsset {
val release = repository.getLatestRelease(repo)
val asset = release.assets.findAsset(file) ?: throw MissingAssetException()
return PatchesAsset(release, asset)
@ -47,7 +47,6 @@ class API(private val repository: GitHubRepository, private val prefs: Preferenc
throw Exception("Failed to download patch bundle", e)
}
}
suspend fun downloadIntegrations(workdir: File): File {
return try {
val (_, out) = downloadAsset(
@ -60,7 +59,7 @@ class API(private val repository: GitHubRepository, private val prefs: Preferenc
}
}
private suspend fun downloadAsset(
suspend fun downloadAsset(
workdir: File,
patchesAsset: PatchesAsset
): Pair<PatchesAsset, File> {

View File

@ -1,15 +1,13 @@
package app.revanced.manager.di
import app.revanced.manager.ui.viewmodel.AppSelectorViewModel
import app.revanced.manager.ui.viewmodel.DashboardViewModel
import app.revanced.manager.ui.viewmodel.PatcherViewModel
import app.revanced.manager.ui.viewmodel.SettingsViewModel
import app.revanced.manager.ui.viewmodel.*
import org.koin.androidx.viewmodel.dsl.viewModelOf
import org.koin.dsl.module
val viewModelModule = module {
viewModelOf(::SettingsViewModel)
viewModelOf(::DashboardViewModel)
viewModelOf(::PatcherViewModel)
viewModelOf(::PatcherScreenViewModel)
viewModelOf(::AppSelectorViewModel)
viewModelOf(::PatchingScreenViewModel)
}

View File

@ -0,0 +1,10 @@
package app.revanced.manager.di
import app.revanced.manager.patcher.worker.PatcherWorker
import org.koin.android.ext.koin.androidContext
import org.koin.androidx.workmanager.dsl.worker
import org.koin.dsl.module
val workerModule = module {
worker { PatcherWorker( androidContext(), get(), get())}
}

View File

@ -14,13 +14,16 @@ import androidx.work.ForegroundInfo
import androidx.work.WorkerParameters
import app.revanced.manager.R
import app.revanced.manager.Variables.patches
import app.revanced.manager.Variables.selectedAppPackage
import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.api.API
import app.revanced.manager.patcher.aapt.Aapt
import app.revanced.manager.patcher.aligning.ZipAligner
import app.revanced.manager.patcher.aligning.zip.ZipFile
import app.revanced.manager.patcher.aligning.zip.structures.ZipEntry
import app.revanced.manager.patcher.signing.Signer
import app.revanced.manager.ui.Resource
import app.revanced.manager.ui.viewmodel.Logging
import app.revanced.patcher.Patcher
import app.revanced.patcher.PatcherOptions
import app.revanced.patcher.data.Data
@ -29,15 +32,19 @@ import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.logging.Logger
import app.revanced.patcher.patch.Patch
import app.revanced.patcher.patch.impl.ResourcePatch
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.koin.core.component.KoinComponent
import java.io.File
import java.nio.file.Files
import java.nio.file.StandardCopyOption
class PatcherWorker(context: Context, parameters: WorkerParameters, private val api: API): CoroutineWorker(context, parameters) ,KoinComponent {
class PatcherWorker(context: Context, parameters: WorkerParameters) :
CoroutineWorker(context, parameters) {
val tag = "ReVanced Manager"
private val workdir = File(inputData.getString("workdir")!!)
private val workdir = createWorkDir()
override suspend fun doWork(): Result {
if (runAttemptCount > 0) {
return Result.failure(
androidx.work.Data.Builder()
@ -77,32 +84,41 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
}
}
private fun runPatcher(
private suspend fun runPatcher(
workdir: File
): Boolean {
val aaptPath = Aapt.binary(applicationContext).absolutePath
val frameworkPath =
applicationContext.filesDir.resolve("framework").also { it.mkdirs() }.absolutePath
val integrationsCacheDir =
applicationContext.filesDir.resolve("integrations-cache").also { it.mkdirs() }
val appInfo = applicationContext.packageManager.getApplicationInfo(selectedAppPackage.value.get(), 3)
Log.d(tag, "Checking prerequisites")
Logging.log += "Checking prerequisites\n"
val patches = findPatchesByIds(selectedPatches)
if (patches.isEmpty()) return true
Log.d(tag, "Creating directories")
File(inputData.getString("input")!!).copyTo(
applicationContext.filesDir.resolve("base.apk"),
true
)
val inputFile = File(applicationContext.filesDir, "base.apk")
Logging.log += "Creating directories\n"
val inputFile = File(applicationContext.filesDir, "input.apk")
val patchedFile = File(workdir, "patched.apk")
val outputFile = File(applicationContext.filesDir, "out.apk")
val outputFile = File(applicationContext.filesDir, "output.apk")
val cacheDirectory = workdir.resolve("cache")
val integrations = workdir.resolve("integrations.apk")
Logging.log += "Downloading integrations\n"
val integrations = api.downloadIntegrations(integrationsCacheDir)
Logging.log += "Copying base.apk from device\n"
withContext(Dispatchers.IO) {
Files.copy(
File(appInfo.publicSourceDir).toPath(),
inputFile.toPath(),
StandardCopyOption.REPLACE_EXISTING
)
}
try {
Log.d(tag, "Creating patcher")
Logging.log += "Decoding resources\n"
val patcher = Patcher( // start patcher
PatcherOptions(
inputFile,
@ -134,37 +150,42 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
Log.d(tag, "Adding ${patches.size} patch(es)")
patcher.addPatches(patches)
Logging.log += "Merging integrations\n"
patcher.addFiles(listOf(integrations)) {}
patcher.applyPatches().forEach { (patch, result) ->
Logging.log += "Applying $patch\n"
if (result.isSuccess) {
Log.i(tag, "[success] $patch")
Logging.log += "$patch has been applied successfully\n"
return@forEach
}
Log.e(tag, "[error] $patch:", result.exceptionOrNull()!!)
Logging.log += "Failed to apply $patch \n" + result.exceptionOrNull()!!
}
Log.d(tag, "Saving file")
Logging.log += "Saving file\n"
val result = patcher.save() // compile apk
if (patchedFile.exists()) Files.delete(patchedFile.toPath())
if (patchedFile.exists()) withContext(Dispatchers.IO) {
Files.delete(patchedFile.toPath())
}
ZipFile(patchedFile).use { fs -> // somehow this function is the most resource intensive
result.dexFiles.forEach { Log.d(tag, "Writing dex file ${it.name}")
result.dexFiles.forEach { Logging.log += "Writing dex file ${it.name}\n"
fs.addEntryCompressData(ZipEntry.createWithName(it.name), it.stream.readBytes())}
Logging.log += "Aligning apk!\n"
result.resourceFile?.let {
fs.copyEntriesFromFileAligned(ZipFile(it), ZipAligner::getEntryAlignment)
}
fs.copyEntriesFromFileAligned(ZipFile(inputFile), ZipAligner::getEntryAlignment)
}
Logging.log += "Signing apk\n"
Signer("ReVanced", "s3cur3p@ssw0rd").signApk(patchedFile, outputFile)
Log.i(tag, "Successfully patched into $outputFile")
Logging.log += "Successfully patched!\n"
} finally {
Log.d(tag, "Deleting workdir")
// workdir.deleteRecursively()
workdir.deleteRecursively()
}
return false
}
@ -187,4 +208,8 @@ class PatcherWorker(context: Context, parameters: WorkerParameters) :
}
return false
}
private fun createWorkDir(): File {
return applicationContext.filesDir.resolve("tmp-${System.currentTimeMillis()}")
.also { it.mkdirs() }
}
}

View File

@ -10,14 +10,14 @@ import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.ui.viewmodel.PatchClass
import app.revanced.manager.ui.viewmodel.PatcherViewModel
import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
import app.revanced.patcher.annotation.Package
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
import org.koin.androidx.compose.getViewModel
@Composable
fun PatchCompatibilityDialog(
patchClass: PatchClass, pvm: PatcherViewModel = getViewModel(), onClose: () -> Unit
patchClass: PatchClass, pvm: PatcherScreenViewModel = getViewModel(), onClose: () -> Unit
) {
val patch = patchClass.patch
val packageName = pvm.getSelectedPackageInfo()?.packageName

View File

@ -21,6 +21,9 @@ sealed interface AppDestination : Destination {
@Parcelize
object PatchSelector : AppDestination
@Parcelize
object Patcher : AppDestination
}
@Parcelize

View File

@ -69,7 +69,8 @@ fun MainDashboardScreen(navigator: BackstackNavigator<AppDestination>) {
DashboardDestination.DASHBOARD -> DashboardScreen()
DashboardDestination.PATCHER -> PatcherScreen(
onClickAppSelector = { navigator.push(AppDestination.AppSelector) },
onClickPatchSelector = { navigator.push(AppDestination.PatchSelector) }
onClickPatchSelector = { navigator.push(AppDestination.PatchSelector) },
onClickPatch = { navigator.push(AppDestination.Patcher) },
)
DashboardDestination.SETTINGS -> SettingsScreen()
}

View File

@ -16,7 +16,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.revanced.manager.ui.viewmodel.PatcherViewModel
import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
import org.koin.androidx.compose.getViewModel
@OptIn(ExperimentalMaterial3Api::class)
@ -24,7 +24,7 @@ import org.koin.androidx.compose.getViewModel
fun NewPatcherScreen(
onClickAppSelector: () -> Unit,
onClickPatchSelector: () -> Unit,
viewModel: PatcherViewModel = getViewModel()
viewModel: PatcherScreenViewModel = getViewModel()
) {
var validBundle = false
Column(

View File

@ -4,14 +4,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Build
import androidx.compose.material3.*
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.Variables.patches
@ -20,7 +18,7 @@ import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.ui.Resource
import app.revanced.manager.ui.component.FloatingActionButton
import app.revanced.manager.ui.component.SplitAPKDialog
import app.revanced.manager.ui.viewmodel.PatcherViewModel
import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
import org.koin.androidx.compose.getViewModel
@OptIn(ExperimentalMaterial3Api::class)
@ -28,7 +26,8 @@ import org.koin.androidx.compose.getViewModel
fun PatcherScreen(
onClickAppSelector: () -> Unit,
onClickPatchSelector: () -> Unit,
viewModel: PatcherViewModel = getViewModel()
onClickPatch: () -> Unit,
viewModel: PatcherScreenViewModel = getViewModel()
) {
val selectedAmount = selectedPatches.size
val selectedAppPackage by selectedAppPackage
@ -39,9 +38,7 @@ fun PatcherScreen(
Scaffold(floatingActionButton = {
FloatingActionButton(
enabled = hasAppSelected && viewModel.anyPatchSelected(),
onClick = {
if (viewModel.checkSplitApk()) { showDialog = true } else viewModel.startPatcher()
},
onClick = { if (viewModel.checkSplitApk()) { showDialog = true } else onClickPatch()},
icon = { Icon(Icons.Default.Build, contentDescription = "Patch") },
text = { Text(text = "Patch") }
)
@ -53,7 +50,7 @@ fun PatcherScreen(
.padding(16.dp),
) {
if (showDialog)
SplitAPKDialog(onDismiss = { showDialog = false }, onConfirm = { viewModel.startPatcher() })
SplitAPKDialog(onDismiss = { showDialog = false }, onConfirm = onClickPatch)
Card(
modifier = Modifier
.padding(4.dp)

View File

@ -0,0 +1,74 @@
package app.revanced.manager.ui.screen
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.ArrowBack
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import app.revanced.manager.ui.navigation.AppDestination
import app.revanced.manager.ui.viewmodel.Logging
import app.revanced.manager.ui.viewmodel.PatchingScreenViewModel
import com.xinto.taxi.BackstackNavigator
import org.koin.androidx.compose.getViewModel
@Composable
@OptIn(ExperimentalMaterial3Api::class)
fun PatchingScreen(
navigator: BackstackNavigator<AppDestination>,
vm: PatchingScreenViewModel = getViewModel()
) {
Scaffold(
topBar = {
Row {
IconButton(onClick = navigator::pop) {
Icon(
imageVector = Icons.Default.ArrowBack,
contentDescription = null
)
}
}
}
) { paddingValues ->
Column {
Column(
modifier =
Modifier
.fillMaxWidth()
.fillMaxHeight(0.2f)
.padding(paddingValues),
horizontalAlignment = Alignment.CenterHorizontally
) {
Column(
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
if (vm.patchingInProgress) {
CircularProgressIndicator(modifier = Modifier.padding(vertical = 16.dp).size(30.dp))
Text(text = "Patching...", fontSize = 30.sp)
}
}
}
Column(
Modifier
.fillMaxWidth()
.padding(20.dp)
) {
Card {
Text(
text = Logging.log,
modifier = Modifier.padding(horizontal = 20.dp, vertical = 10.dp).fillMaxSize(),
fontSize = 20.sp,
lineHeight = 35.sp
)
}
}
}
}
}

View File

@ -23,7 +23,7 @@ import app.revanced.manager.ui.component.PatchCompatibilityDialog
import app.revanced.manager.ui.navigation.AppDestination
import app.revanced.manager.ui.theme.Typography
import app.revanced.manager.ui.viewmodel.PatchClass
import app.revanced.manager.ui.viewmodel.PatcherViewModel
import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
import app.revanced.patcher.extensions.PatchExtensions.description
import app.revanced.patcher.extensions.PatchExtensions.patchName
import app.revanced.patcher.extensions.PatchExtensions.version
@ -34,7 +34,7 @@ import org.koin.androidx.compose.getViewModel
@Composable
fun PatchesSelectorSubscreen(
navigator: BackstackNavigator<AppDestination>,
pvm: PatcherViewModel = getViewModel(),
pvm: PatcherScreenViewModel = getViewModel(),
) {
val patches = rememberSaveable { pvm.getFilteredPatchesAndCheckOptions() }
var query by mutableStateOf("")

View File

@ -6,15 +6,10 @@ import android.os.Parcelable
import android.util.Log
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import app.revanced.manager.Variables.patches
import app.revanced.manager.Variables.selectedAppPackage
import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.api.API
import app.revanced.manager.patcher.worker.PatcherWorker
import app.revanced.manager.ui.Resource
import app.revanced.patcher.data.Data
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
@ -24,31 +19,22 @@ import app.revanced.patcher.patch.Patch
import app.revanced.patcher.util.patch.impl.DexPatchBundle
import dalvik.system.DexClassLoader
import kotlinx.coroutines.launch
import kotlinx.coroutines.runBlocking
import kotlinx.parcelize.Parcelize
import java.io.File
class PatcherViewModel(private val app: Application, private val api: API) : ViewModel() {
private val workdir = createWorkDir()
class PatcherScreenViewModel(private val app: Application, private val api: API) : ViewModel() {
private lateinit var patchBundleFile: String
private val tag = "ReVanced Manager"
init {
runBlocking {
viewModelScope.launch {
loadPatches()
downloadIntegrations()
}
}
fun selectPatch(patchId: String, state: Boolean) {
if (state) selectedPatches.add(patchId)
else selectedPatches.remove(patchId)
}
private suspend fun downloadIntegrations() {
api.downloadIntegrations(workdir).renameTo(File(workdir,"integrations.apk"))
}
fun selectAllPatches(patchList: List<PatchClass>, selectAll: Boolean) {
patchList.forEach { patch ->
val patchId = patch.patch.patchName
@ -85,6 +71,38 @@ class PatcherViewModel(private val app: Application, private val api: API) : Vie
)
else null
fun checkSplitApk(): Boolean {
if (getSelectedPackageInfo()!!.applicationInfo!!.metaData!!.getBoolean("com.android.vending.splits.required", false)) {
Log.d(tag, "APK is split.")
return true
}
Log.d(tag, "APK is not split.")
return false
}
private fun loadPatches() = viewModelScope.launch {
try {
val file = api.downloadPatchBundle(app.filesDir)
patchBundleFile = file.absolutePath
loadPatches0(file.absolutePath)
} catch (e: Exception) {
Log.e("ReVancedManager", "An error occurred while loading patches", e)
}
}
private fun loadPatches0(path: String) {
val patchClasses = DexPatchBundle(
path, DexClassLoader(
path,
app.codeCacheDir.absolutePath,
null,
javaClass.classLoader
)
).loadPatches()
patches.value = Resource.Success(patchClasses)
Log.d("ReVanced Manager", "Finished loading patches")
}
fun getFilteredPatchesAndCheckOptions(): List<PatchClass> {
return buildList {
val selected = getSelectedPackageInfo() ?: return@buildList
@ -108,61 +126,6 @@ class PatcherViewModel(private val app: Application, private val api: API) : Vie
}
}
}
fun checkSplitApk(): Boolean {
if (getSelectedPackageInfo()!!.applicationInfo!!.metaData!!.getBoolean("com.android.vending.splits.required", false)) {
Log.d(tag, "APK is split.")
return true
}
Log.d(tag, "APK is not split.")
return false
}
private fun loadPatches() = viewModelScope.launch {
try {
val file = api.downloadPatchBundle(app.filesDir)
patchBundleFile = file.absolutePath
loadPatches0(file.absolutePath)
} catch (e: Exception) {
Log.e("ReVancedManager", "An error occurred while loading patches", e)
}
}
private fun loadPatches0(path: String) {
val patchClasses = DexPatchBundle(
path, DexClassLoader(
path,
app.codeCacheDir.absolutePath,
null,
javaClass.classLoader
)
).loadPatches()
patches.value = Resource.Success(patchClasses)
Log.d("ReVanced Manager", "Finished loading patches")
}
fun startPatcher() {
WorkManager
.getInstance(app)
.enqueueUniqueWork(
"patching",
ExistingWorkPolicy.KEEP,
OneTimeWorkRequest.Builder(PatcherWorker::class.java)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setInputData(
androidx.work.Data.Builder()
.putString("workdir", workdir.toString())
.putString("input",
getSelectedPackageInfo()?.applicationInfo?.publicSourceDir
)
.build()).build()
)
}
private fun createWorkDir(): File {
return app.filesDir.resolve("tmp-${System.currentTimeMillis()}")
.also { it.mkdirs() }
}
}
@Parcelize

View File

@ -0,0 +1,46 @@
package app.revanced.manager.ui.viewmodel
import android.app.Application
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.work.ExistingWorkPolicy
import androidx.work.OneTimeWorkRequest
import androidx.work.OutOfQuotaPolicy
import androidx.work.WorkManager
import app.revanced.manager.patcher.worker.PatcherWorker
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
class PatchingScreenViewModel(val app: Application) : ViewModel() {
var patchingInProgress = true
init {
viewModelScope.launch(Dispatchers.Main) {
startPatcher()
}
}
private fun startPatcher() {
WorkManager
.getInstance(app)
.enqueueUniqueWork(
"patching",
ExistingWorkPolicy.KEEP,
OneTimeWorkRequest.Builder(PatcherWorker::class.java)
.setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST)
.setInputData(
androidx.work.Data.Builder()
.build()
).build()
)
patchingInProgress = false
}
}
object Logging {
var log by mutableStateOf("")
}

View File

@ -6,3 +6,4 @@ const val ghPatches = "$team/revanced-patches"
const val ghPatcher = "$team/revanced-patcher"
const val ghManager = "$team/revanced-manager"
const val ghIntegrations = "$team/revanced-integrations"
const val tag = "ReVanced Manager"