diff --git a/.idea/jarRepositories.xml b/.idea/jarRepositories.xml
index cfc391f..453c83d 100644
--- a/.idea/jarRepositories.xml
+++ b/.idea/jarRepositories.xml
@@ -26,5 +26,10 @@
+
+
+
+
+
\ 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 34090ab..a6f6440 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
@@ -1,5 +1,6 @@
package app.revanced.manager.ui.screen.subscreens
+import android.annotation.SuppressLint
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.LazyColumn
@@ -9,7 +10,6 @@ import androidx.compose.material.icons.filled.*
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.*
-import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
@@ -30,6 +30,7 @@ import app.revanced.patcher.extensions.PatchExtensions.version
import com.xinto.taxi.BackstackNavigator
import org.koin.androidx.compose.getViewModel
+@SuppressLint("UnrememberedMutableState")
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun PatchesSelectorSubscreen(
@@ -39,7 +40,6 @@ fun PatchesSelectorSubscreen(
val patches = pvm.getFilteredPatchesAndCheckOptions()
var query by mutableStateOf("")
-
Scaffold(
topBar = {
MediumTopAppBar(
@@ -68,7 +68,7 @@ fun PatchesSelectorSubscreen(
}
}
)
- },
+ }
) { paddingValues ->
Column(
modifier = Modifier
@@ -76,60 +76,68 @@ fun PatchesSelectorSubscreen(
) {
when (patchesState) {
is Resource.Success -> {
- Box(
- modifier = Modifier
- .fillMaxWidth()
- .padding(8.dp, 4.dp),
- ) {
- Row(
- modifier = Modifier.fillMaxWidth()
+ if (patches.isNotEmpty()) {
+ Box(
+ modifier = Modifier
+ .fillMaxWidth()
+ .padding(8.dp, 4.dp),
) {
- 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")
+ 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, pvm.isPatchSelected(name)) {
- pvm.selectPatch(name, !pvm.isPatchSelected(name))
- }
+ },
+ )
}
- } else {
- items(count = patches.size) {
- val patch = patches[it]
- val name = patch.patch.patchName
- if (name.contains(query.lowercase())) {
+ }
+ 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, pvm.isPatchSelected(name)) {
pvm.selectPatch(name, !pvm.isPatchSelected(name))
}
}
+ } else {
+ items(count = patches.size) {
+ val patch = patches[it]
+ val name = patch.patch.patchName
+ if (name.contains(query.lowercase())) {
+ PatchCard(patch, pvm.isPatchSelected(name)) {
+ pvm.selectPatch(name, !pvm.isPatchSelected(name))
+ }
+ }
+ }
}
}
}
+ else {
+ Column(Modifier.fillMaxSize(), Arrangement.Center, Alignment.CenterHorizontally) {
+ Text(text = "No compatible patches found.")
+ }
+
+ }
}
else -> LoadingIndicator(null)
}
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 0b05c49..6ccbe8a 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
@@ -1,38 +1,62 @@
package app.revanced.manager.ui.screen.subscreens
-import androidx.compose.foundation.*
-import androidx.compose.foundation.layout.*
-import androidx.compose.foundation.shape.RoundedCornerShape
+import android.widget.Toast
+import androidx.activity.compose.rememberLauncherForActivityResult
+import androidx.activity.result.contract.ActivityResultContracts
+import androidx.compose.foundation.clickable
+import androidx.compose.foundation.layout.Column
+import androidx.compose.foundation.layout.padding
+import androidx.compose.foundation.rememberScrollState
+import androidx.compose.foundation.verticalScroll
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.ArrowBack
-import androidx.compose.material.icons.filled.Settings
import androidx.compose.material3.*
import androidx.compose.runtime.Composable
-import androidx.compose.ui.Alignment
-import androidx.compose.ui.Alignment.Companion.CenterVertically
import androidx.compose.ui.Modifier
-import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.nestedscroll.nestedScroll
+import androidx.compose.ui.platform.LocalContext
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.ui.component.SourceItem
import app.revanced.manager.ui.navigation.AppDestination
-import app.revanced.manager.ui.theme.Typography
-import coil.compose.AsyncImage
+import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
import com.xinto.taxi.BackstackNavigator
+import org.koin.androidx.compose.getViewModel
+import java.nio.file.Files
+import java.nio.file.StandardCopyOption
-@OptIn(ExperimentalMaterial3Api::class, ExperimentalFoundationApi::class)
+@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun SourceSelectorSubscreen(
- navigator: BackstackNavigator
+ navigator: BackstackNavigator,
+ pvm: PatcherScreenViewModel = getViewModel()
) {
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
state = rememberTopAppBarState(),
canScroll = { true }
)
+ val context = LocalContext.current
+
+ 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
+ )
+ pvm.patchBundleFile = patchesFile.absolutePath
+ pvm.loadPatches0()
+ navigator.pop()
+ return@rememberLauncherForActivityResult
+ }
+ Toast.makeText(context, "Couldn't load local patch bundle.", Toast.LENGTH_SHORT).show()
+ }
+
+
Scaffold(
modifier = Modifier
.nestedScroll(scrollBehavior.nestedScrollConnection),
@@ -61,7 +85,7 @@ fun SourceSelectorSubscreen(
) {
ListItem(
modifier = Modifier
- .clickable { /* TODO */ },
+ .clickable { filePicker.launch(arrayOf("application/java-archive")) },
headlineText = { Text(stringResource(R.string.select_bundle_from_storage)) },
leadingContent = {
Icon(
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 3dc3359..e20ce88 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
@@ -5,6 +5,7 @@ import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Parcelable
import android.util.Log
+import android.widget.Toast
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import app.revanced.manager.Variables.patches
@@ -12,7 +13,6 @@ import app.revanced.manager.Variables.selectedAppPackage
import app.revanced.manager.Variables.selectedPatches
import app.revanced.manager.api.API
import app.revanced.manager.ui.Resource
-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.options
@@ -24,7 +24,8 @@ import kotlinx.coroutines.launch
import kotlinx.parcelize.Parcelize
class PatcherScreenViewModel(private val app: Application, private val api: API) : ViewModel() {
- private lateinit var patchBundleFile: String
+ lateinit var patchBundleFile: String
+ private val tag = "ReVanced Manager"
init {
viewModelScope.launch {
@@ -66,13 +67,13 @@ class PatcherScreenViewModel(private val app: Application, private val api: API)
fun getSelectedPackageInfo(): PackageInfo? {
- if (selectedAppPackage.value.isPresent) {
- return app.packageManager.getPackageArchiveInfo(
+ return if (selectedAppPackage.value.isPresent) {
+ app.packageManager.getPackageArchiveInfo(
selectedAppPackage.value.get().publicSourceDir,
PackageManager.GET_META_DATA
)
} else {
- return null
+ null
}
}
@@ -95,21 +96,27 @@ class PatcherScreenViewModel(private val app: Application, private val api: API)
patchBundleFile = file.absolutePath
loadPatches0()
} catch (e: Exception) {
- Log.e("ReVancedManager", "An error occurred while loading patches", e)
+ Log.e(tag, "An error occurred while loading patches", e)
}
}
fun loadPatches0() {
- val patchClasses = PatchBundle.Dex(
- patchBundleFile, DexClassLoader(
- patchBundleFile,
- app.codeCacheDir.absolutePath,
- null,
- javaClass.classLoader
- )
- ).loadPatches()
- patches.value = Resource.Success(patchClasses)
- Log.d("ReVanced Manager", "Finished loading patches")
+ try {
+ val patchClasses = PatchBundle.Dex(
+ patchBundleFile, DexClassLoader(
+ patchBundleFile,
+ app.codeCacheDir.absolutePath,
+ null,
+ javaClass.classLoader
+ )
+ ).loadPatches()
+ patches.value = Resource.Success(patchClasses)
+ } catch (e: Exception) {
+ Toast.makeText(app, "Failed to load patch bundle.", Toast.LENGTH_LONG).show()
+ Log.e(tag, "Failed to load patch bundle.", e)
+ return
+ }
+ Toast.makeText(app, "Successfully loaded patch bundle.", Toast.LENGTH_LONG).show()
}
fun getFilteredPatchesAndCheckOptions(): List {