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 a89c90e..401c46b 100644 --- a/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt +++ b/app/src/main/java/app/revanced/manager/di/ViewModelModule.kt @@ -11,4 +11,6 @@ val viewModelModule = module { viewModelOf(::PatchesSelectorViewModel) viewModelOf(::PatchingScreenViewModel) viewModelOf(::ContributorsViewModel) + viewModelOf(::PatcherScreenViewModel) + viewModelOf(::SourceSelectorViewModel) } \ 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 2249dc4..aaacc2c 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.Main) { + suspend fun downloadPatches() = withContext(Dispatchers.Default) { try { val asset = if (prefs.srcPatches!! == ghPatches) reVancedAPI.findAsset(ghPatches, ".jar") 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 bef6cd9..f624425 100644 --- a/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt +++ b/app/src/main/java/app/revanced/manager/patcher/PatcherUtils.kt @@ -8,6 +8,7 @@ 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.patchName @@ -19,12 +20,13 @@ import java.util.* class PatcherUtils(val app: Application) { val patches = mutableStateOf>>>>(Resource.Loading) + val filteredPatches = mutableStateListOf() val selectedAppPackage = mutableStateOf(Optional.empty()) val selectedPatches = mutableStateListOf() lateinit var patchBundleFile: String fun cleanup() { - patches.value = Resource.Success(emptyList()) + patches.value = Resource.Loading selectedAppPackage.value = Optional.empty() selectedPatches.clear() } 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 bc8bc44..5274104 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 @@ -1,6 +1,12 @@ package app.revanced.manager.ui.screen -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.Row +import androidx.compose.foundation.layout.Spacer +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.width import androidx.compose.material.icons.Icons import androidx.compose.material.icons.filled.Build import androidx.compose.material3.* @@ -11,14 +17,11 @@ import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.revanced.manager.R -import app.revanced.manager.network.api.ManagerAPI -import app.revanced.manager.patcher.PatcherUtils import app.revanced.manager.ui.Resource import app.revanced.manager.ui.component.AppIcon import app.revanced.manager.ui.component.FloatingActionButton import app.revanced.manager.ui.component.SplitAPKDialog -import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel -import org.koin.androidx.compose.get +import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class) @@ -28,27 +31,17 @@ fun PatcherScreen( onClickPatchSelector: () -> Unit, onClickPatch: () -> Unit, onClickSourceSelector: () -> Unit, - patcherUtils: PatcherUtils = get(), - psvm: PatchesSelectorViewModel = getViewModel(), - managerAPI: ManagerAPI = get() + vm: PatcherScreenViewModel = getViewModel(), ) { - val selectedAmount = patcherUtils.selectedPatches.size - val selectedAppPackage by patcherUtils.selectedAppPackage - val hasAppSelected = selectedAppPackage.isPresent - val patchesLoaded = patcherUtils.patches.value is Resource.Success var showDialog by remember { mutableStateOf(false) } - - LaunchedEffect(patchesLoaded) { - if (!patchesLoaded) { - managerAPI.downloadPatches() - } - } + val hasAppSelected by mutableStateOf(vm.selectedAppPackage.isPresent) + val patchesLoaded by mutableStateOf(vm.patchesLoaded is Resource.Success) Scaffold( floatingActionButton = { FloatingActionButton( - enabled = hasAppSelected && psvm.anyPatchSelected(), - onClick = { onClickPatch(); patcherUtils.loadPatchBundle() }, // TODO: replace this with something better - icon = { Icon(Icons.Default.Build, contentDescription = stringResource(R.string.patch)) }, + enabled = hasAppSelected && vm.selectedPatches.isNotEmpty(), + onClick = onClickPatch, + icon = { Icon(Icons.Default.Build, contentDescription = "Patch") }, text = { Text(stringResource(R.string.patch)) } ) }) { paddingValues -> @@ -89,18 +82,18 @@ fun PatcherScreen( modifier = Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically ) { - if (selectedAppPackage.isPresent) { + if (vm.selectedAppPackage.isPresent) { AppIcon( LocalContext.current.packageManager.getApplicationIcon( - selectedAppPackage.get().packageName + vm.selectedAppPackage.get().packageName ), contentDescription = null, size = 18 ) Spacer(Modifier.width(5.dp)) } Text( text = if (patchesLoaded) { - if (selectedAppPackage.isPresent) { - selectedAppPackage.get().packageName + if (vm.selectedAppPackage.isPresent) { + vm.selectedAppPackage.get().packageName } else { stringResource(R.string.card_application_not_selected) } @@ -128,8 +121,8 @@ fun PatcherScreen( Text( text = if (!hasAppSelected) { stringResource(R.string.select_an_application_first) - } else if (psvm.anyPatchSelected()) { - "$selectedAmount patches selected." + } else if (vm.selectedPatches.isNotEmpty()) { + "${vm.selectedPatches.size} patches selected." } else { stringResource(R.string.card_patches_body_patches) }, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt index 4553fc2..3fa6f4f 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/SettingsScreen.kt @@ -25,11 +25,11 @@ import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun SettingsScreen( - viewModel: SettingsViewModel = getViewModel(), + vm: SettingsViewModel = getViewModel(), onClickContributors: () -> Unit, onClickLicenses: () -> Unit, ) { - val prefs = viewModel.prefs + val prefs = vm.prefs Column( modifier = Modifier @@ -38,19 +38,19 @@ fun SettingsScreen( .verticalScroll(state = rememberScrollState()), verticalArrangement = Arrangement.spacedBy(12.dp) ) { - if (viewModel.showThemePicker) { + if (vm.showThemePicker) { ThemePicker( - onDismissRequest = viewModel::dismissThemePicker, - onConfirm = viewModel::setTheme + onDismissRequest = vm::dismissThemePicker, + onConfirm = vm::setTheme ) } GroupHeader(stringResource(R.string.appearance)) ListItem( - modifier = Modifier.clickable { viewModel.showThemePicker() }, + modifier = Modifier.clickable { vm.showThemePicker() }, headlineText = { Text(stringResource(R.string.theme)) }, leadingContent = { Icon(Icons.Default.Style, contentDescription = null) }, trailingContent = { - FilledTonalButton(onClick = { viewModel.showThemePicker() }) { + FilledTonalButton(onClick = { vm.showThemePicker() }) { Text(text = prefs.theme.displayName) } } @@ -85,7 +85,7 @@ fun SettingsScreen( } ) Divider() - SocialItem(R.string.github, R.drawable.ic_github, viewModel::openGitHub) + SocialItem(R.string.github, R.drawable.ic_github, vm::openGitHub) SocialItem(R.string.opensource_licenses, Icons.Default.LibraryBooks, onClickLicenses) SocialItem(R.string.screen_contributors_title, Icons.Default.Group, onClickContributors) } diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/AppSelectorSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/AppSelectorSubscreen.kt index 409af5a..74db691 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/AppSelectorSubscreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/AppSelectorSubscreen.kt @@ -1,7 +1,6 @@ package app.revanced.manager.ui.screen.subscreens import android.annotation.SuppressLint -import android.widget.Toast import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.result.contract.ActivityResultContracts import androidx.compose.foundation.clickable @@ -12,9 +11,7 @@ import androidx.compose.material.icons.filled.ArrowBack import androidx.compose.material.icons.filled.SdStorage import androidx.compose.material3.* import androidx.compose.runtime.Composable -import androidx.compose.runtime.mutableStateOf import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource import app.revanced.manager.R import app.revanced.manager.ui.component.AppIcon @@ -31,17 +28,12 @@ fun AppSelectorSubscreen( navigator: BackstackNavigator, vm: AppSelectorViewModel = getViewModel(), ) { - val context = LocalContext.current - - val filtered = mutableStateOf(vm.filteredApps.isNotEmpty()) val filePicker = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { it?.let { uri -> vm.setSelectedAppPackageFromFile(uri) navigator.pop() - return@rememberLauncherForActivityResult } - Toast.makeText(context, "Couldn't load APK file.", Toast.LENGTH_SHORT).show() } Scaffold( @@ -63,7 +55,7 @@ fun AppSelectorSubscreen( ) }, ) { paddingValues -> - if (filtered.value) { + if (vm.filteredApps.isNotEmpty()) { LazyColumn(modifier = Modifier.padding(paddingValues)) { items(count = vm.filteredApps.size) { int -> val app = vm.filteredApps[int] 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 3f20a41..65c2d6a 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 @@ -13,132 +13,122 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.revanced.manager.R -import app.revanced.manager.patcher.PatcherUtils -import app.revanced.manager.ui.Resource import app.revanced.manager.ui.component.LoadingIndicator -import app.revanced.manager.ui.component.PatchCard import app.revanced.manager.ui.navigation.AppDestination import app.revanced.manager.ui.viewmodel.PatchesSelectorViewModel import app.revanced.patcher.extensions.PatchExtensions.patchName import com.xinto.taxi.BackstackNavigator -import org.koin.androidx.compose.get +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch import org.koin.androidx.compose.getViewModel +import app.revanced.manager.ui.component.PatchCard @SuppressLint("UnrememberedMutableState") @OptIn(ExperimentalMaterial3Api::class) @Composable fun PatchesSelectorSubscreen( navigator: BackstackNavigator, - psvm: PatchesSelectorViewModel = getViewModel(), - patcherUtils: PatcherUtils = get() + vm: PatchesSelectorViewModel = getViewModel(), ) { - val patchesState by patcherUtils.patches - val patches = psvm.getFilteredPatches() + val patches = vm.filteredPatches var query by mutableStateOf("") - Scaffold( - topBar = { - MediumTopAppBar( - title = { - Text( - text = stringResource(R.string.card_patches_header), - style = MaterialTheme.typography.headlineLarge - ) - }, - navigationIcon = { - IconButton(onClick = navigator::pop) { - Icon( - imageVector = Icons.Default.ArrowBack, - contentDescription = null - ) - } - }, - actions = { - IconButton(onClick = { - psvm.selectAllPatches(patches, !psvm.anyPatchSelected()) - }) { - if (!psvm.anyPatchSelected()) Icon( - Icons.Default.SelectAll, - contentDescription = null - ) else Icon(Icons.Default.Deselect, contentDescription = null) - } - } - ) + LaunchedEffect(null) { + launch(Dispatchers.Default) { + vm.filterPatches() } - ) { paddingValues -> + } + Scaffold(topBar = { + MediumTopAppBar(title = { + Text( + text = stringResource(R.string.card_patches_header), + style = MaterialTheme.typography.headlineLarge + ) + }, navigationIcon = { + IconButton(onClick = navigator::pop) { + Icon( + imageVector = Icons.Default.ArrowBack, contentDescription = null + ) + } + }, 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) + } + }) + }) { paddingValues -> Column( - modifier = Modifier - .padding(paddingValues) + modifier = Modifier.padding(paddingValues) ) { - when (patchesState) { - is Resource.Success -> { - if (patches.isNotEmpty()) { - Box( - modifier = Modifier - .fillMaxWidth() - .padding(8.dp, 4.dp), + if (!vm.loading) { + if (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, stringResource(R.string.search)) - }, - trailingIcon = { - if (query.isNotEmpty()) { - IconButton(onClick = { - query = "" - }) { - Icon(Icons.Default.Clear, stringResource(R.string.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, psvm.isPatchSelected(name)) { - psvm.selectPatch(name, !psvm.isPatchSelected(name)) - } - } - } else { - items(count = patches.size) { - val patch = patches[it] - val name = patch.patch.patchName - if (name.contains(query.lowercase())) { - PatchCard(patch, psvm.isPatchSelected(name)) { - psvm.selectPatch(name, !psvm.isPatchSelected(name)) + 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") } } - } - } - } - } else { - Column( - Modifier.fillMaxSize(), - Arrangement.Center, - Alignment.CenterHorizontally - ) { - Text(stringResource(R.string.no_compatible_patches)) + }, + ) } } + 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)) + } + } + } + } + } + } else { + Column( + Modifier.fillMaxSize(), + Arrangement.Center, + Alignment.CenterHorizontally + ) { + Text(stringResource(R.string.no_compatible_patches)) + } } - else -> LoadingIndicator(null) - } + } else LoadingIndicator(null) } } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/SourceSelectorSubscreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/SourceSelectorSubscreen.kt index bc18bbf..11bf278 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/subscreens/SourceSelectorSubscreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/subscreens/SourceSelectorSubscreen.kt @@ -20,19 +20,17 @@ import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import app.revanced.manager.R -import app.revanced.manager.patcher.PatcherUtils import app.revanced.manager.ui.component.SourceItem import app.revanced.manager.ui.navigation.AppDestination +import app.revanced.manager.ui.viewmodel.SourceSelectorViewModel import com.xinto.taxi.BackstackNavigator -import org.koin.androidx.compose.get -import java.nio.file.Files -import java.nio.file.StandardCopyOption +import org.koin.androidx.compose.getViewModel @OptIn(ExperimentalMaterial3Api::class) @Composable fun SourceSelectorSubscreen( navigator: BackstackNavigator, - patcherUtils: PatcherUtils = get() + viewModel: SourceSelectorViewModel = getViewModel() ) { val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior( state = rememberTopAppBarState(), @@ -42,17 +40,7 @@ fun SourceSelectorSubscreen( val filePicker = rememberLauncherForActivityResult(ActivityResultContracts.OpenDocument()) { it?.let { uri -> - val patchesFile = context.cacheDir.resolve("patches.jar") - Files.copy( - context.contentResolver.openInputStream(uri), - patchesFile.toPath(), - StandardCopyOption.REPLACE_EXISTING - ) - patchesFile.absolutePath.also { - patcherUtils.patchBundleFile = it - patcherUtils.loadPatchBundle(it) - } - + viewModel.loadBundle(uri) navigator.pop() return@rememberLauncherForActivityResult } 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 0aa00fc..e6876b6 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 @@ -6,6 +6,7 @@ import android.content.pm.PackageManager import android.graphics.drawable.Drawable import android.net.Uri import android.util.Log +import androidx.compose.runtime.mutableStateListOf import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import app.revanced.manager.patcher.PatcherUtils @@ -23,8 +24,9 @@ class AppSelectorViewModel( val app: Application, patcherUtils: PatcherUtils ) : ViewModel() { - val filteredApps = mutableListOf() + val filteredApps = mutableStateListOf() val patches = patcherUtils.patches + private val filteredPatches = patcherUtils.filteredPatches private val selectedAppPackage = patcherUtils.selectedAppPackage private val selectedPatches = patcherUtils.selectedPatches @@ -32,7 +34,7 @@ class AppSelectorViewModel( viewModelScope.launch { filterApps() } } - private suspend fun filterApps() = withContext(Dispatchers.Main) { + private suspend fun filterApps() = withContext(Dispatchers.Default) { try { val (patches) = patches.value as Resource.Success patches.forEach patch@{ patch -> @@ -59,13 +61,16 @@ class AppSelectorViewModel( return app.packageManager.getApplicationLabel(info).toString() } - fun loadIcon(info: ApplicationInfo): Drawable? { - return info.loadIcon(app.packageManager) + fun loadIcon(info: ApplicationInfo): Drawable { + return app.packageManager.getApplicationIcon(info) } fun setSelectedAppPackage(appId: ApplicationInfo) { selectedAppPackage.value.ifPresent { s -> - if (s != appId) selectedPatches.clear() + if (s != appId) { + selectedPatches.clear() + filteredPatches.clear() + } } selectedAppPackage.value = Optional.of(appId) } 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 new file mode 100644 index 0000000..b572c17 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/PatcherScreenViewModel.kt @@ -0,0 +1,25 @@ +package app.revanced.manager.ui.viewmodel + +import androidx.compose.runtime.getValue +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import app.revanced.manager.network.api.ManagerAPI +import app.revanced.manager.patcher.PatcherUtils +import kotlinx.coroutines.Dispatchers +import kotlinx.coroutines.launch + +class PatcherScreenViewModel( + patcherUtils: PatcherUtils, + api: ManagerAPI +) : ViewModel() { + + init { + viewModelScope.launch(Dispatchers.IO) { + api.downloadPatches() + } + } + + val selectedPatches = patcherUtils.selectedPatches + val selectedAppPackage by patcherUtils.selectedAppPackage + val patchesLoaded 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 9d670de..c0722c2 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 @@ -1,6 +1,9 @@ package app.revanced.manager.ui.viewmodel import android.os.Parcelable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.setValue import androidx.lifecycle.ViewModel import app.revanced.manager.patcher.PatcherUtils import app.revanced.manager.ui.Resource @@ -13,16 +16,14 @@ import kotlinx.parcelize.Parcelize class PatchesSelectorViewModel( private val patcherUtils: PatcherUtils ) : ViewModel() { - private val selectedPatches = patcherUtils.selectedPatches + val filteredPatches = patcherUtils.filteredPatches + val selectedPatches = patcherUtils.selectedPatches + var loading by mutableStateOf(true) fun isPatchSelected(patchId: String): Boolean { return selectedPatches.contains(patchId) } - fun anyPatchSelected(): Boolean { - return !selectedPatches.isEmpty() - } - fun selectPatch(patchId: String, state: Boolean) { if (state) selectedPatches.add(patchId) else selectedPatches.remove(patchId) @@ -36,23 +37,26 @@ class PatchesSelectorViewModel( } } - fun getFilteredPatches(): List { - return buildList { - val selected = patcherUtils.getSelectedPackageInfo() ?: return@buildList - val (patches) = patcherUtils.patches.value as? Resource.Success ?: return@buildList - 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 } - add(PatchClass(patch, unsupported)) - } + 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)) } } } + loading = false } } 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 new file mode 100644 index 0000000..29a6e06 --- /dev/null +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/SourceSelectorViewModel.kt @@ -0,0 +1,32 @@ +package app.revanced.manager.ui.viewmodel + +import android.app.Application +import android.net.Uri +import android.util.Log +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() { + fun loadBundle(uri: Uri) { + try { + val patchesFile = app.cacheDir.resolve(File(uri.path!!).name) + Files.copy( + app.contentResolver.openInputStream(uri), + patchesFile.toPath(), + StandardCopyOption.REPLACE_EXISTING + ) + patchesFile.absolutePath.also { + patcherUtils.patchBundleFile = it + patcherUtils.loadPatchBundle(it) + } + } catch (e: Exception) { + Log.e(tag, "Failed to load bundle", e) + Sentry.captureException(e) + } + } +} \ No newline at end of file