mirror of
https://github.com/revanced/revanced-manager-compose-old.git
synced 2025-04-30 14:34:29 +02:00
feat: select patch bundle from storage
Co-authored-by: Canny <canny1913@outlook.com>
This commit is contained in:
parent
7b19a4fb83
commit
69df08e0ed
5
.idea/jarRepositories.xml
generated
5
.idea/jarRepositories.xml
generated
@ -26,5 +26,10 @@
|
|||||||
<option name="name" value="Google" />
|
<option name="name" value="Google" />
|
||||||
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
<option name="url" value="https://dl.google.com/dl/android/maven2/" />
|
||||||
</remote-repository>
|
</remote-repository>
|
||||||
|
<remote-repository>
|
||||||
|
<option name="id" value="maven2" />
|
||||||
|
<option name="name" value="maven2" />
|
||||||
|
<option name="url" value="https://maven.pkg.github.com/revanced/revanced-patcher" />
|
||||||
|
</remote-repository>
|
||||||
</component>
|
</component>
|
||||||
</project>
|
</project>
|
@ -1,5 +1,6 @@
|
|||||||
package app.revanced.manager.ui.screen.subscreens
|
package app.revanced.manager.ui.screen.subscreens
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.lazy.LazyColumn
|
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.material.icons.outlined.Settings
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
import androidx.compose.runtime.saveable.rememberSaveable
|
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
@ -30,6 +30,7 @@ import app.revanced.patcher.extensions.PatchExtensions.version
|
|||||||
import com.xinto.taxi.BackstackNavigator
|
import com.xinto.taxi.BackstackNavigator
|
||||||
import org.koin.androidx.compose.getViewModel
|
import org.koin.androidx.compose.getViewModel
|
||||||
|
|
||||||
|
@SuppressLint("UnrememberedMutableState")
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun PatchesSelectorSubscreen(
|
fun PatchesSelectorSubscreen(
|
||||||
@ -39,7 +40,6 @@ fun PatchesSelectorSubscreen(
|
|||||||
val patches = pvm.getFilteredPatchesAndCheckOptions()
|
val patches = pvm.getFilteredPatchesAndCheckOptions()
|
||||||
var query by mutableStateOf("")
|
var query by mutableStateOf("")
|
||||||
|
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
topBar = {
|
topBar = {
|
||||||
MediumTopAppBar(
|
MediumTopAppBar(
|
||||||
@ -68,7 +68,7 @@ fun PatchesSelectorSubscreen(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
},
|
}
|
||||||
) { paddingValues ->
|
) { paddingValues ->
|
||||||
Column(
|
Column(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@ -76,60 +76,68 @@ fun PatchesSelectorSubscreen(
|
|||||||
) {
|
) {
|
||||||
when (patchesState) {
|
when (patchesState) {
|
||||||
is Resource.Success -> {
|
is Resource.Success -> {
|
||||||
Box(
|
if (patches.isNotEmpty()) {
|
||||||
modifier = Modifier
|
Box(
|
||||||
.fillMaxWidth()
|
modifier = Modifier
|
||||||
.padding(8.dp, 4.dp),
|
.fillMaxWidth()
|
||||||
) {
|
.padding(8.dp, 4.dp),
|
||||||
Row(
|
|
||||||
modifier = Modifier.fillMaxWidth()
|
|
||||||
) {
|
) {
|
||||||
OutlinedTextField(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxWidth()
|
||||||
.fillMaxWidth()
|
) {
|
||||||
.padding(8.dp),
|
OutlinedTextField(
|
||||||
shape = RoundedCornerShape(12.dp),
|
modifier = Modifier
|
||||||
value = query,
|
.fillMaxWidth()
|
||||||
onValueChange = { newValue ->
|
.padding(8.dp),
|
||||||
query = newValue
|
shape = RoundedCornerShape(12.dp),
|
||||||
},
|
value = query,
|
||||||
leadingIcon = {
|
onValueChange = { newValue ->
|
||||||
Icon(Icons.Default.Search, "Search")
|
query = newValue
|
||||||
},
|
},
|
||||||
trailingIcon = {
|
leadingIcon = {
|
||||||
if (query.isNotEmpty()) {
|
Icon(Icons.Default.Search, "Search")
|
||||||
IconButton(onClick = {
|
},
|
||||||
query = ""
|
trailingIcon = {
|
||||||
}) {
|
if (query.isNotEmpty()) {
|
||||||
Icon(Icons.Default.Clear, "Clear")
|
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) {
|
LazyColumn(Modifier.padding(0.dp, 2.dp)) {
|
||||||
val patch = patches[it]
|
|
||||||
val name = patch.patch.patchName
|
if (query.isEmpty() || query.isBlank()) {
|
||||||
if (name.contains(query.lowercase())) {
|
items(count = patches.size) {
|
||||||
|
val patch = patches[it]
|
||||||
|
val name = patch.patch.patchName
|
||||||
PatchCard(patch, pvm.isPatchSelected(name)) {
|
PatchCard(patch, pvm.isPatchSelected(name)) {
|
||||||
pvm.selectPatch(name, !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)
|
else -> LoadingIndicator(null)
|
||||||
}
|
}
|
||||||
|
@ -1,38 +1,62 @@
|
|||||||
package app.revanced.manager.ui.screen.subscreens
|
package app.revanced.manager.ui.screen.subscreens
|
||||||
|
|
||||||
import androidx.compose.foundation.*
|
import android.widget.Toast
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
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.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
import androidx.compose.material.icons.filled.ArrowBack
|
import androidx.compose.material.icons.filled.ArrowBack
|
||||||
import androidx.compose.material.icons.filled.Settings
|
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
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.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
|
||||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
import app.revanced.manager.ui.component.SourceItem
|
import app.revanced.manager.ui.component.SourceItem
|
||||||
import app.revanced.manager.ui.navigation.AppDestination
|
import app.revanced.manager.ui.navigation.AppDestination
|
||||||
import app.revanced.manager.ui.theme.Typography
|
import app.revanced.manager.ui.viewmodel.PatcherScreenViewModel
|
||||||
import coil.compose.AsyncImage
|
|
||||||
import com.xinto.taxi.BackstackNavigator
|
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
|
@Composable
|
||||||
fun SourceSelectorSubscreen(
|
fun SourceSelectorSubscreen(
|
||||||
navigator: BackstackNavigator<AppDestination>
|
navigator: BackstackNavigator<AppDestination>,
|
||||||
|
pvm: PatcherScreenViewModel = getViewModel()
|
||||||
) {
|
) {
|
||||||
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
|
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior(
|
||||||
state = rememberTopAppBarState(),
|
state = rememberTopAppBarState(),
|
||||||
canScroll = { true }
|
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(
|
Scaffold(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
.nestedScroll(scrollBehavior.nestedScrollConnection),
|
||||||
@ -61,7 +85,7 @@ fun SourceSelectorSubscreen(
|
|||||||
) {
|
) {
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.clickable { /* TODO */ },
|
.clickable { filePicker.launch(arrayOf("application/java-archive")) },
|
||||||
headlineText = { Text(stringResource(R.string.select_bundle_from_storage)) },
|
headlineText = { Text(stringResource(R.string.select_bundle_from_storage)) },
|
||||||
leadingContent = {
|
leadingContent = {
|
||||||
Icon(
|
Icon(
|
||||||
|
@ -5,6 +5,7 @@ import android.content.pm.PackageInfo
|
|||||||
import android.content.pm.PackageManager
|
import android.content.pm.PackageManager
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import android.widget.Toast
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import app.revanced.manager.Variables.patches
|
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.Variables.selectedPatches
|
||||||
import app.revanced.manager.api.API
|
import app.revanced.manager.api.API
|
||||||
import app.revanced.manager.ui.Resource
|
import app.revanced.manager.ui.Resource
|
||||||
import app.revanced.manager.util.tag
|
|
||||||
import app.revanced.patcher.data.Context
|
import app.revanced.patcher.data.Context
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
import app.revanced.patcher.extensions.PatchExtensions.compatiblePackages
|
||||||
import app.revanced.patcher.extensions.PatchExtensions.options
|
import app.revanced.patcher.extensions.PatchExtensions.options
|
||||||
@ -24,7 +24,8 @@ import kotlinx.coroutines.launch
|
|||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
class PatcherScreenViewModel(private val app: Application, private val api: API) : ViewModel() {
|
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 {
|
init {
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
@ -66,13 +67,13 @@ class PatcherScreenViewModel(private val app: Application, private val api: API)
|
|||||||
|
|
||||||
|
|
||||||
fun getSelectedPackageInfo(): PackageInfo? {
|
fun getSelectedPackageInfo(): PackageInfo? {
|
||||||
if (selectedAppPackage.value.isPresent) {
|
return if (selectedAppPackage.value.isPresent) {
|
||||||
return app.packageManager.getPackageArchiveInfo(
|
app.packageManager.getPackageArchiveInfo(
|
||||||
selectedAppPackage.value.get().publicSourceDir,
|
selectedAppPackage.value.get().publicSourceDir,
|
||||||
PackageManager.GET_META_DATA
|
PackageManager.GET_META_DATA
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
return null
|
null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -95,21 +96,27 @@ class PatcherScreenViewModel(private val app: Application, private val api: API)
|
|||||||
patchBundleFile = file.absolutePath
|
patchBundleFile = file.absolutePath
|
||||||
loadPatches0()
|
loadPatches0()
|
||||||
} catch (e: Exception) {
|
} 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() {
|
fun loadPatches0() {
|
||||||
val patchClasses = PatchBundle.Dex(
|
try {
|
||||||
patchBundleFile, DexClassLoader(
|
val patchClasses = PatchBundle.Dex(
|
||||||
patchBundleFile,
|
patchBundleFile, DexClassLoader(
|
||||||
app.codeCacheDir.absolutePath,
|
patchBundleFile,
|
||||||
null,
|
app.codeCacheDir.absolutePath,
|
||||||
javaClass.classLoader
|
null,
|
||||||
)
|
javaClass.classLoader
|
||||||
).loadPatches()
|
)
|
||||||
patches.value = Resource.Success(patchClasses)
|
).loadPatches()
|
||||||
Log.d("ReVanced Manager", "Finished loading patches")
|
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<PatchClass> {
|
fun getFilteredPatchesAndCheckOptions(): List<PatchClass> {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user