From a1e172a852d3885c86450a73801c0f7864fde521 Mon Sep 17 00:00:00 2001 From: Canny Date: Fri, 23 Sep 2022 11:32:18 +0300 Subject: [PATCH] feat: patching screen --- app/build.gradle.kts | 4 +- app/src/main/AndroidManifest.xml | 13 ++- .../java/app/revanced/manager/MainActivity.kt | 6 +- .../revanced/manager/ManagerApplication.kt | 9 +- .../main/java/app/revanced/manager/api/API.kt | 5 +- .../revanced/manager/di/ViewModelModule.kt | 8 +- .../app/revanced/manager/di/WorkerModule.kt | 10 ++ .../manager/patcher/worker/PatcherWorker.kt | 75 ++++++++----- .../ui/component/PatchCompatibilityDialog.kt | 4 +- .../manager/ui/navigation/AppDestination.kt | 3 + .../manager/ui/screen/MainDashboardScreen.kt | 3 +- .../manager/ui/screen/NewPatcherScreen.kt | 4 +- .../manager/ui/screen/PatcherScreen.kt | 13 +-- .../manager/ui/screen/PatchingScreen.kt | 74 ++++++++++++ .../subscreens/PatchesSelectorSubscreen.kt | 4 +- ...ViewModel.kt => PatcherScreenViewModel.kt} | 105 ++++++------------ .../ui/viewmodel/PatchingScreenViewModel.kt | 46 ++++++++ .../app/revanced/manager/util/Constants.kt | 3 +- 18 files changed, 259 insertions(+), 130 deletions(-) create mode 100644 app/src/main/java/app/revanced/manager/di/WorkerModule.kt create mode 100644 app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt rename app/src/main/java/app/revanced/manager/ui/viewmodel/{PatcherViewModel.kt => PatcherScreenViewModel.kt} (75%) create mode 100644 app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 55d494f..bd58672 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") + } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index 129d5e8..1194e75 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -33,8 +33,19 @@ android:supportsRtl="true" android:largeHeap="true" android:theme="@style/Theme.ReVancedManager" - tools:targetApi="33"> + + + + + 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) } } } diff --git a/app/src/main/java/app/revanced/manager/ManagerApplication.kt b/app/src/main/java/app/revanced/manager/ManagerApplication.kt index eacc2aa..75368bf 100644 --- a/app/src/main/java/app/revanced/manager/ManagerApplication.kt +++ b/app/src/main/java/app/revanced/manager/ManagerApplication.kt @@ -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) } } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/api/API.kt b/app/src/main/java/app/revanced/manager/api/API.kt index 288b0b0..dada585 100644 --- a/app/src/main/java/app/revanced/manager/api/API.kt +++ b/app/src/main/java/app/revanced/manager/api/API.kt @@ -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 { diff --git a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt index 3bc6759..b940a3d 100644 --- a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -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) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/di/WorkerModule.kt b/app/src/main/java/app/revanced/manager/di/WorkerModule.kt new file mode 100644 index 0000000..3656012 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/di/WorkerModule.kt @@ -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())} +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt b/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt index 3a9955f..862aecd 100644 --- a/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt +++ b/app/src/main/java/app/revanced/manager/patcher/worker/PatcherWorker.kt @@ -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() } + } } diff --git a/app/src/main/java/app/revanced/manager/ui/component/PatchCompatibilityDialog.kt b/app/src/main/java/app/revanced/manager/ui/component/PatchCompatibilityDialog.kt index 46a12b9..3746f54 100644 --- a/app/src/main/java/app/revanced/manager/ui/component/PatchCompatibilityDialog.kt +++ b/app/src/main/java/app/revanced/manager/ui/component/PatchCompatibilityDialog.kt @@ -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 diff --git a/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt index 20a493c..97a7298 100644 --- a/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt +++ b/app/src/main/java/app/revanced/manager/ui/navigation/AppDestination.kt @@ -21,6 +21,9 @@ sealed interface AppDestination : Destination { @Parcelize object PatchSelector : AppDestination + + @Parcelize + object Patcher : AppDestination } @Parcelize diff --git a/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt index 8c79b2b..d82fb29 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/MainDashboardScreen.kt @@ -69,7 +69,8 @@ fun MainDashboardScreen(navigator: BackstackNavigator) { 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() } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/NewPatcherScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/NewPatcherScreen.kt index fd1b664..5ef41ce 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/NewPatcherScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/NewPatcherScreen.kt @@ -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( diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt index d2c024a..0296eea 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatcherScreen.kt @@ -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) diff --git a/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt new file mode 100644 index 0000000..4d8601d --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/screen/PatchingScreen.kt @@ -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, + 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 + ) + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchesSelectorSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchesSelectorSubscreen.kt index d2a26c2..641f333 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchesSelectorSubscreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchesSelectorSubscreen.kt @@ -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, - pvm: PatcherViewModel = getViewModel(), + pvm: PatcherScreenViewModel = getViewModel(), ) { val patches = rememberSaveable { pvm.getFilteredPatchesAndCheckOptions() } var query by mutableStateOf("") diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt similarity index 75% rename from app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherViewModel.kt rename to app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt index a92c442..c73c60a 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt @@ -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, 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 { 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 diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt new file mode 100644 index 0000000..b8f1349 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt @@ -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("") +} \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/util/Constants.kt b/app/src/main/java/app/revanced/manager/util/Constants.kt index 3ce234c..2a8b7c8 100644 --- a/app/src/main/java/app/revanced/manager/util/Constants.kt +++ b/app/src/main/java/app/revanced/manager/util/Constants.kt @@ -5,4 +5,5 @@ const val ghOrganization = "https://github.com/$team" const val ghPatches = "$team/revanced-patches" const val ghPatcher = "$team/revanced-patcher" const val ghManager = "$team/revanced-manager" -const val ghIntegrations = "$team/revanced-integrations" \ No newline at end of file +const val ghIntegrations = "$team/revanced-integrations" +const val tag = "ReVanced Manager" \ No newline at end of file