diff --git a/app/src/main/java/app/revanced/manager/di/WorkerModule.kt b/app/src/main/java/app/revanced/manager/di/WorkerModule.kt index 4f8178e..d5d9112 100644 --- a/app/src/main/java/app/revanced/manager/di/WorkerModule.kt +++ b/app/src/main/java/app/revanced/manager/di/WorkerModule.kt @@ -1,10 +1,9 @@ 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.androidx.workmanager.dsl.workerOf import org.koin.dsl.module val workerModule = module { - worker { PatcherWorker(androidContext(), get(), get(), get()) } + workerOf(::PatcherWorker) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/network/api/ManagerAPI.kt b/app/src/main/java/app/revanced/manager/network/api/ManagerAPI.kt index aaacc2c..3910b77 100644 --- a/app/src/main/java/app/revanced/manager/network/api/ManagerAPI.kt +++ b/app/src/main/java/app/revanced/manager/network/api/ManagerAPI.kt @@ -38,7 +38,7 @@ class ManagerAPI( return out } - suspend fun downloadPatches() = withContext(Dispatchers.Default) { + suspend fun downloadPatches() = withContext(Dispatchers.IO) { try { val asset = if (prefs.srcPatches!! == ghPatches) reVancedAPI.findAsset(ghPatches, ".jar") @@ -46,7 +46,6 @@ class ManagerAPI( asset.run { downloadAsset(app.cacheDir, downloadUrl).run { patcherUtils.run { - patchBundleFile = absolutePath loadPatchBundle(absolutePath) } } diff --git a/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt b/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt index 5949e53..9dbc706 100644 --- a/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt +++ b/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt @@ -7,11 +7,8 @@ import android.util.Log import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import app.revanced.manager.ui.Resource -import app.revanced.manager.ui.viewmodel.PatchClass import app.revanced.manager.util.tag import app.revanced.patcher.data.Context -import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages -import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.Patch import app.revanced.patcher.util.patch.PatchBundle import dalvik.system.DexClassLoader @@ -19,12 +16,10 @@ import io.sentry.Sentry import java.util.* class PatcherUtils(val app: Application) { - val patches = mutableStateOf>>>>(Resource.Loading) - val filteredPatches = mutableStateListOf() + val patches = mutableStateOf>>(Resource.Loading) val selectedAppPackage = mutableStateOf(Optional.empty()) val selectedAppPackagePath = mutableStateOf(null) - val selectedPatches = mutableStateListOf() - lateinit var patchBundleFile: String + val selectedPatches = mutableStateListOf() fun cleanup() { patches.value = Resource.Loading @@ -32,16 +27,15 @@ class PatcherUtils(val app: Application) { selectedPatches.clear() } - fun loadPatchBundle(file: String? = patchBundleFile) { + fun loadPatchBundle(file: String) { + cleanup() try { - if (this::patchBundleFile.isInitialized) { - val patchClasses = PatchBundle.Dex( - file!!, DexClassLoader( - file, app.codeCacheDir.absolutePath, null, javaClass.classLoader - ) - ).loadPatches() - patches.value = Resource.Success(patchClasses) - } else throw IllegalStateException("No patch bundle(s) selected.") + val patchClasses = PatchBundle.Dex( + file, DexClassLoader( + file, app.codeCacheDir.absolutePath, null, javaClass.classLoader + ) + ).loadPatches() + patches.value = Resource.Success(patchClasses) } catch (e: Exception) { Log.e(tag, "Failed to load patch bundle.", e) Sentry.captureException(e) @@ -50,7 +44,9 @@ class PatcherUtils(val app: Application) { fun getSelectedPackageInfo(): PackageInfo? { return if (selectedAppPackage.value.isPresent) { - val path = selectedAppPackage.value.get().publicSourceDir ?: selectedAppPackagePath.value ?: return null + val path = + selectedAppPackage.value.get().publicSourceDir ?: selectedAppPackagePath.value + ?: return null app.packageManager.getPackageArchiveInfo( path, 1 ) @@ -58,9 +54,5 @@ class PatcherUtils(val app: Application) { null } } - - fun findPatchesByIds(ids: Iterable): List>> { - val (patches) = patches.value as? Resource.Success ?: return listOf() - return patches.filter { patch -> ids.any { it == patch.patchName } && patch.compatiblePackages!!.any { it.name == getSelectedPackageInfo()?.packageName } } - } -} \ No newline at end of file +} +typealias ReVancedPatch = Class> 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 b9bc8fb..8930daa 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 @@ -13,6 +13,7 @@ import android.util.Log import android.view.WindowManager import androidx.core.content.ContextCompat import androidx.work.CoroutineWorker +import androidx.work.Data import androidx.work.ForegroundInfo import androidx.work.WorkerParameters import app.revanced.manager.R @@ -80,20 +81,6 @@ class PatcherWorker( Sentry.captureException(e) } - return try { - runPatcher(workdir) - Result.success() - } catch (e: Exception) { - log("Error while patching: ${e::class.simpleName}: ${e.message}", ERROR) - Log.e(tag, e.stackTraceToString()) - Sentry.captureException(e) - Result.failure() - } - } - - private suspend fun runPatcher( - workdir: File - ): Boolean { val wakeLock: PowerManager.WakeLock = (applicationContext.getSystemService(Context.POWER_SERVICE) as PowerManager).run { newWakeLock(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON, "$tag::Patcher").apply { @@ -102,7 +89,8 @@ class PatcherWorker( } Log.d(tag, "Acquired wakelock.") - try { + return try { + val aaptPath = Aapt.binary(applicationContext)?.absolutePath if (aaptPath == null) { log("AAPT2 not found.", ERROR) @@ -118,13 +106,14 @@ class PatcherWorker( val appPath = patcherUtils.selectedAppPackagePath.value log("Checking prerequisites...", INFO) - val patches = patcherUtils.findPatchesByIds(patcherUtils.selectedPatches) + val patches = patcherUtils.selectedPatches if (patches.isEmpty()) throw IllegalStateException("No patches selected.") log("Creating directories...", INFO) val inputFile = File(workdir, "input.apk") val patchedFile = File(workdir, "patched.apk") - val outputFile = File(inputData.getString("output")!!) + val outputFile = File(workdir, "output.apk") + val finalFile = reVancedFolder.resolve(appInfo.packageName + ".apk") val cacheDirectory = workdir.resolve("cache") val integrations = managerAPI.downloadIntegrations(integrationsCacheDir) @@ -203,19 +192,24 @@ class PatcherWorker( withContext(Dispatchers.IO) { Files.copy( outputFile.inputStream(), - reVancedFolder.resolve(appInfo.packageName + ".apk").toPath(), + finalFile.toPath(), StandardCopyOption.REPLACE_EXISTING ) } log("Successfully patched!", SUCCESS) - patcherUtils.cleanup() + Result.success(Data.Builder().putString(OUTPUT, finalFile.absolutePath).build()) + + } catch (e: Exception) { + log("Error while patching: ${e::class.simpleName}: ${e.message}", ERROR) + Log.e(tag, e.stackTraceToString()) + Sentry.captureException(e) + Result.failure() } finally { Log.d(tag, "Deleting workdir") workdir.deleteRecursively() wakeLock.release() Log.d(tag, "Released wakelock.") } - return false } @@ -236,6 +230,7 @@ class PatcherWorker( const val PATCH_MESSAGE = "PATCH_MESSAGE" const val PATCH_STATUS = "PATCH_STATUS" const val PATCH_LOG = "PATCH_LOG" + const val OUTPUT = "output" const val INFO = 0 const val ERROR = 1 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 803b391..ee099ed 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 @@ -29,14 +29,12 @@ fun PatcherScreen( onClickSourceSelector: () -> Unit, vm: PatcherScreenViewModel = getViewModel(), ) { - val hasAppSelected by mutableStateOf(vm.selectedAppPackage.isPresent) - val patchesLoaded by mutableStateOf(vm.patchesLoaded is Resource.Success) val context = LocalContext.current Scaffold( floatingActionButton = { FloatingActionButton( - enabled = hasAppSelected && vm.selectedPatches.isNotEmpty(), + enabled = vm.selectedAppPackage.isPresent && vm.selectedPatches.isNotEmpty(), onClick = onClickPatch, icon = { Icon(Icons.Default.Build, contentDescription = "Patch") }, text = { Text(stringResource(R.string.patch)) } @@ -66,7 +64,7 @@ fun PatcherScreen( modifier = Modifier .padding(vertical = 4.dp) .fillMaxWidth(), - enabled = patchesLoaded, + enabled = vm.patches is Resource.Success, onClick = onClickAppSelector ) { Column(modifier = Modifier.padding(16.dp)) { @@ -86,7 +84,7 @@ fun PatcherScreen( Spacer(Modifier.width(5.dp)) } Text( - text = if (patchesLoaded) { + text = if (vm.patches is Resource.Success) { if (vm.selectedAppPackage.isPresent) { vm.selectedAppPackage.get().packageName } else { @@ -105,7 +103,7 @@ fun PatcherScreen( modifier = Modifier .padding(vertical = 4.dp) .fillMaxWidth(), - enabled = hasAppSelected, + enabled = vm.selectedAppPackage.isPresent, onClick = onClickPatchSelector ) { Column(modifier = Modifier.padding(16.dp)) { @@ -114,7 +112,7 @@ fun PatcherScreen( style = MaterialTheme.typography.titleMedium ) Text( - text = if (!hasAppSelected) { + text = if (!vm.selectedAppPackage.isPresent) { stringResource(R.string.select_an_application_first) } else if (vm.selectedPatches.isNotEmpty()) { "${vm.selectedPatches.size} patches selected." diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt index 5012fd6..86049b7 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/ContributorsSubscreen.kt @@ -26,7 +26,7 @@ fun ContributorsSubscreen( onBackClick: () -> Unit, vm: ContributorsViewModel = getViewModel() ) { - val ctx = LocalContext.current.applicationContext + val ctx = LocalContext.current AppScaffold( topBar = { scrollBehavior -> AppLargeTopBar( 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 a4aad4d..ca33048 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 @@ -15,12 +15,9 @@ import androidx.compose.ui.unit.dp import app.revanced.manager.R import app.revanced.manager.ui.component.AppMediumTopBar import app.revanced.manager.ui.component.AppScaffold -import app.revanced.manager.ui.component.LoadingIndicator import app.revanced.manager.ui.component.PatchCard import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel import app.revanced.patcher.extensions.PatchExtensions.patchName -import kotlinx.coroutines.Dispatchers -import kotlinx.coroutines.launch import org.koin.androidx.compose.getViewModel @SuppressLint("UnrememberedMutableState") @@ -30,101 +27,79 @@ fun PatchesSelectorSubscreen( onBackClick: () -> Unit, vm: PatchesSelectorViewModel = getViewModel(), ) { - val patches = vm.filteredPatches - var query by mutableStateOf("") - LaunchedEffect(null) { - launch(Dispatchers.Default) { - vm.filterPatches() - } - } - AppScaffold( - topBar = { scrollBehavior -> - AppMediumTopBar( - topBarTitle = stringResource(id = R.string.card_patches_header), - scrollBehavior = scrollBehavior, - actions = { - IconButton(onClick = { - vm.selectAllPatches(patches, vm.selectedPatches.isEmpty()) - }) { - if (vm.selectedPatches.isEmpty()) Icon( - Icons.Default.SelectAll, contentDescription = null - ) else Icon(Icons.Default.Deselect, contentDescription = null) - } - }, - onBackClick = onBackClick - ) - } - ) { paddingValues -> + AppScaffold(topBar = { scrollBehavior -> + AppMediumTopBar( + topBarTitle = stringResource(id = R.string.card_patches_header), + scrollBehavior = scrollBehavior, + actions = { + IconButton(onClick = { + vm.selectAllPatches(vm.patches, vm.selectedPatches.isEmpty()) + }) { + if (vm.selectedPatches.isEmpty()) Icon( + Icons.Default.SelectAll, contentDescription = null + ) else Icon(Icons.Default.Deselect, contentDescription = null) + } + }, + onBackClick = onBackClick + ) + }) { paddingValues -> Column( modifier = Modifier.padding(paddingValues) ) { - if (!vm.loading) { - if (patches.isNotEmpty()) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp, 4.dp), + val search = vm.search + if (vm.patches.isNotEmpty()) { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp, 4.dp), + ) { + Row( + modifier = Modifier.fillMaxWidth() ) { - Row( - modifier = Modifier.fillMaxWidth() - ) { - OutlinedTextField( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp), - shape = RoundedCornerShape(12.dp), - value = query, - onValueChange = { newValue -> - query = newValue - }, - leadingIcon = { - Icon(Icons.Default.Search, "Search") - }, - trailingIcon = { - if (query.isNotEmpty()) { - IconButton(onClick = { - query = "" - }) { - Icon(Icons.Default.Clear, "Clear") - } - } - }, - ) - } - } - LazyColumn(Modifier.padding(0.dp, 2.dp)) { - - if (query.isEmpty() || query.isBlank()) { - items(count = patches.size) { - val patch = patches[it] - val name = patch.patch.patchName - PatchCard(patch, vm.isPatchSelected(name)) { - vm.selectPatch(name, !vm.isPatchSelected(name)) - } - } - } else { - items(count = patches.size) { - val patch = patches[it] - val name = patch.patch.patchName - if (name.contains(query.lowercase())) { - PatchCard(patch, vm.isPatchSelected(name)) { - vm.selectPatch(name, !vm.isPatchSelected(name)) + OutlinedTextField( + modifier = Modifier + .fillMaxWidth() + .padding(8.dp), + shape = RoundedCornerShape(12.dp), + value = search, + onValueChange = { vm.search(it) }, + leadingIcon = { + Icon(Icons.Default.Search, "Search") + }, + trailingIcon = { + if (search.isNotEmpty()) { + IconButton(onClick = { + vm.clearSearch() + }) { + Icon(Icons.Default.Clear, "Clear") } } - } - } - } - } else { - Column( - Modifier.fillMaxSize(), - Arrangement.Center, - Alignment.CenterHorizontally - ) { - Text(stringResource(R.string.no_compatible_patches)) + }, + ) } } - } else LoadingIndicator(null) + LazyColumn(Modifier.padding(0.dp, 2.dp)) { + items(count = vm.patches.size) { + val patchClass = vm.patches[it] + val name = patchClass.patch.patchName + if (search.isBlank() && name.contains(search.lowercase())) { + PatchCard(patchClass, vm.isPatchSelected(patchClass.patch)) { + vm.selectPatch( + patchClass.patch, + !vm.isPatchSelected(patchClass.patch) + ) + } + } + } + } + } else { + Column( + Modifier.fillMaxSize(), Arrangement.Center, Alignment.CenterHorizontally + ) { + Text(stringResource(R.string.no_compatible_patches)) + } + } } } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchingSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchingSubscreen.kt index 37ac38e..cccb5e7 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchingSubscreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/PatchingSubscreen.kt @@ -53,7 +53,7 @@ fun PatchingSubscreen( Column { if (vm.installFailure) { InstallFailureDialog( - onDismiss = { vm.installFailure = false }, + onDismiss = { vm.dismissDialog() }, status = vm.pmStatus, result = vm.extra ) @@ -149,7 +149,7 @@ fun PatchingSubscreen( ) { Spacer(Modifier.weight(1f, true)) Button(onClick = { - vm.installApk(vm.outputFile) + vm.installApk() }) { Text(text = stringResource(R.string.install)) } diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt index 49ae39b..32ffbcb 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AppSelectorViewModel.kt @@ -25,7 +25,6 @@ class AppSelectorViewModel( val filteredApps = mutableStateListOf() val patches = patcherUtils.patches - private val filteredPatches = patcherUtils.filteredPatches private val selectedAppPackage = patcherUtils.selectedAppPackage private val selectedAppPackagePath = patcherUtils.selectedAppPackagePath private val selectedPatches = patcherUtils.selectedPatches @@ -34,22 +33,27 @@ class AppSelectorViewModel( viewModelScope.launch { filterApps() } } - private suspend fun filterApps() = withContext(Dispatchers.Default) { + @Suppress("RemoveExplicitTypeArguments") + private suspend fun filterApps() = withContext(Dispatchers.IO) { try { val (patches) = patches.value as Resource.Success - patches.forEach patch@{ patch -> - patch.compatiblePackages?.forEach { pkg -> - try { - if (!(filteredApps.any { it.packageName == pkg.name })) { - val appInfo = app.packageManager.getApplicationInfo(pkg.name, 1) - filteredApps.add(appInfo) + val apps = buildList { + patches.forEach patch@{ patch -> + patch.compatiblePackages?.forEach { pkg -> + try { + if (!any { it.packageName == pkg.name }) { + add(app.packageManager.getApplicationInfo(pkg.name, 1)) + return@forEach + } + } catch (e: Exception) { return@forEach } - } catch (e: Exception) { - return@forEach } } } + withContext(Dispatchers.Main) { + filteredApps.addAll(apps) + } Log.d(tag, "Filtered apps.") } catch (e: Exception) { Log.e(tag, "An error occurred while filtering", e) @@ -69,7 +73,6 @@ class AppSelectorViewModel( selectedAppPackage.value.ifPresent { s -> if (s != appId) { selectedPatches.clear() - filteredPatches.clear() } } selectedAppPackage.value = Optional.of(appId) @@ -86,8 +89,7 @@ class AppSelectorViewModel( setSelectedAppPackage( app.packageManager.getPackageArchiveInfo( apkDir.path, 1 - )!!.applicationInfo, - apkDir.absolutePath + )!!.applicationInfo, apkDir.absolutePath ) } catch (e: Exception) { Log.e(tag, "Failed to load apk", e) diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt index b572c17..a44e663 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt @@ -21,5 +21,5 @@ class PatcherScreenViewModel( val selectedPatches = patcherUtils.selectedPatches val selectedAppPackage by patcherUtils.selectedAppPackage - val patchesLoaded by patcherUtils.patches + val patches by patcherUtils.patches } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt index c0722c2..9af084d 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchesSelectorViewModel.kt @@ -2,61 +2,79 @@ package app.revanced.manager.ui.viewmodel import android.os.Parcelable import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateListOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope import app.revanced.manager.patcher.PatcherUtils +import app.revanced.manager.patcher.ReVancedPatch import app.revanced.manager.ui.Resource import app.revanced.patcher.data.Context import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages -import app.revanced.patcher.extensions.PatchExtensions.patchName import app.revanced.patcher.patch.Patch +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch +import kotlinx.coroutines.withContext import kotlinx.parcelize.Parcelize class PatchesSelectorViewModel( private val patcherUtils: PatcherUtils ) : ViewModel() { - val filteredPatches = patcherUtils.filteredPatches + val patches = mutableStateListOf() val selectedPatches = patcherUtils.selectedPatches - var loading by mutableStateOf(true) + var search by mutableStateOf("") + private set - fun isPatchSelected(patchId: String): Boolean { - return selectedPatches.contains(patchId) + fun search(search: String) { + this.search = search } - fun selectPatch(patchId: String, state: Boolean) { - if (state) selectedPatches.add(patchId) - else selectedPatches.remove(patchId) + fun clearSearch() { + search = "" + } + + init { + viewModelScope.launch { filterPatches() } + } + + fun isPatchSelected(patch: ReVancedPatch): Boolean { + return selectedPatches.contains(patch) + } + + fun selectPatch(patch: ReVancedPatch, state: Boolean) { + if (state) selectedPatches.add(patch) + else selectedPatches.remove(patch) } fun selectAllPatches(patchList: List, selectAll: Boolean) { - patchList.forEach { patch -> - val patchId = patch.patch.patchName - if (selectAll && !patch.unsupported) selectedPatches.add(patchId) - else selectedPatches.remove(patchId) + patchList.forEach { patchClass -> + val patch = patchClass.patch + if (selectAll && !patchClass.unsupported) selectedPatches.add(patch) + else selectedPatches.remove(patch) } } - fun filterPatches() { - loading = true - val selected = patcherUtils.getSelectedPackageInfo() ?: return - val (patches) = patcherUtils.patches.value as? Resource.Success ?: return - if (filteredPatches.isNotEmpty()) { - loading = false; return - } - patches.forEach patch@{ patch -> - var unsupported = false - patch.compatiblePackages?.forEach { pkg -> - // if we detect unsupported once, don't overwrite it - if (pkg.name == selected.packageName) { - if (!unsupported) - unsupported = - pkg.versions.isNotEmpty() && !pkg.versions.any { it == selected.versionName } - filteredPatches.add(PatchClass(patch, unsupported)) + private suspend fun filterPatches() = withContext(Dispatchers.IO) { + val selected = patcherUtils.getSelectedPackageInfo() ?: return@withContext + val (patchList) = patcherUtils.patches.value as? Resource.Success ?: return@withContext + val filtered = buildList { + patchList.forEach { patch -> + var unsupported = false + patch.compatiblePackages?.forEach { pkg -> + // if we detect unsupported once, don't overwrite it + if (pkg.name == selected.packageName) { + if (!unsupported) + unsupported = + pkg.versions.isNotEmpty() && !pkg.versions.any { it == selected.versionName } + add(PatchClass(patch, unsupported)) + } } } } - loading = false + withContext(Dispatchers.Main) { + patches.addAll(filtered) + } } } 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 index e18a266..42d27ce 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatchingScreenViewModel.kt @@ -17,6 +17,7 @@ import app.revanced.manager.installer.service.InstallService import app.revanced.manager.installer.service.UninstallService import app.revanced.manager.installer.utils.PM import app.revanced.manager.patcher.worker.PatcherWorker +import app.revanced.manager.util.toast import java.io.File class PatchingScreenViewModel( @@ -38,32 +39,43 @@ class PatchingScreenViewModel( object Failure : Status() } - val workManager = WorkManager.getInstance(app) - var installFailure by mutableStateOf(false) - var pmStatus by mutableStateOf(-999) - var extra by mutableStateOf("") + private var output: String? = null - val outputFile = File(app.cacheDir, "output.apk") + var installFailure by mutableStateOf(false) + private set + + var pmStatus by mutableStateOf(-999) + private set + + var extra by mutableStateOf("") + private set + + private val workManager = WorkManager.getInstance(app) private val patcherWorker = OneTimeWorkRequest.Builder(PatcherWorker::class.java) // create Worker .setExpedited(OutOfQuotaPolicy.RUN_AS_NON_EXPEDITED_WORK_REQUEST).setInputData( - Data.Builder().putString("output", outputFile.path).build() + Data.Builder().putString(PatcherWorker.OUTPUT, null).build() ).build() - private val liveData = workManager.getWorkInfoByIdLiveData(patcherWorker.id) // get LiveData + private val liveData = workManager.getWorkInfoByIdLiveData(patcherWorker.id) - private val observer = Observer { workInfo: WorkInfo -> // observer for observing patch status + private val observer = Observer { workInfo: WorkInfo -> status = when (workInfo.state) { WorkInfo.State.RUNNING -> Status.Patching WorkInfo.State.SUCCEEDED -> Status.Success WorkInfo.State.FAILED -> Status.Failure else -> Status.Idle } + if (workInfo.state == WorkInfo.State.SUCCEEDED) { + output = workInfo.outputData.getString("output") + } } val logs = mutableStateListOf() + var status by mutableStateOf(Status.Idle) + private set private val installBroadcastReceiver = object : BroadcastReceiver() { @@ -95,15 +107,25 @@ class PatchingScreenViewModel( workManager.enqueueUniqueWork("patching", ExistingWorkPolicy.KEEP, patcherWorker) liveData.observeForever(observer) app.registerReceiver(installBroadcastReceiver, IntentFilter().apply { - addAction(InstallService.APP_INSTALL_ACTION) - addAction(UninstallService.APP_UNINSTALL_ACTION) - addAction(PatcherWorker.PATCH_LOG) + arrayOf( + InstallService.APP_INSTALL_ACTION, + UninstallService.APP_UNINSTALL_ACTION, + PatcherWorker.PATCH_LOG + ).forEach { + addAction(it) + } }) } - fun installApk(apk: File) { - PM.installApp(apk, app) - log(PatchLog.Info("Installing...")) + fun dismissDialog() { + installFailure = false + } + + fun installApk() { + if (output != null) { + PM.installApp(File(output!!), app) + log(PatchLog.Info("Installing...")) + } else app.toast("Couldn't find APK file.") } fun postInstallStatus() { diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/SourceSelectorViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/SourceSelectorViewModel.kt index ea44ee7..e62178a 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/SourceSelectorViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/SourceSelectorViewModel.kt @@ -7,11 +7,11 @@ import androidx.lifecycle.ViewModel import app.revanced.manager.patcher.PatcherUtils import app.revanced.manager.util.tag import io.sentry.Sentry -import java.io.File import java.nio.file.Files import java.nio.file.StandardCopyOption -class SourceSelectorViewModel(val app: Application, val patcherUtils: PatcherUtils) : ViewModel() { +class SourceSelectorViewModel(val app: Application, private val patcherUtils: PatcherUtils) : + ViewModel() { fun loadBundle(uri: Uri) { try { val patchesFile = app.cacheDir.resolve("patches.jar") @@ -20,10 +20,7 @@ class SourceSelectorViewModel(val app: Application, val patcherUtils: PatcherUti patchesFile.toPath(), StandardCopyOption.REPLACE_EXISTING ) - patchesFile.absolutePath.also { - patcherUtils.patchBundleFile = it - patcherUtils.loadPatchBundle(it) - } + patcherUtils.loadPatchBundle(patchesFile.absolutePath) } catch (e: Exception) { Log.e(tag, "Failed to load bundle", e) Sentry.captureException(e) diff --git a/app/src/main/java/app/revanced/manager/util/Util.kt b/app/src/main/java/app/revanced/manager/util/Util.kt index 80ea9d2..85eb962 100644 --- a/app/src/main/java/app/revanced/manager/util/Util.kt +++ b/app/src/main/java/app/revanced/manager/util/Util.kt @@ -4,6 +4,7 @@ import android.content.Context import android.content.Intent import android.content.pm.PackageManager.NameNotFoundException import android.graphics.drawable.Drawable +import android.widget.Toast import androidx.core.net.toUri @@ -19,4 +20,8 @@ fun Context.loadIcon(string: String): Drawable? { } catch (e: NameNotFoundException) { null } -} \ No newline at end of file +} + +fun Context.toast(string: String, duration: Int = Toast.LENGTH_SHORT) { + Toast.makeText(this, string, duration).show() +}