mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2026-01-21 10:13:56 +00:00
Compare commits
1 Commits
v1.26.0-de
...
feat/root-
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
ff70a77afb |
@@ -1,10 +1,3 @@
|
|||||||
# app [1.26.0-dev.14](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.13...v1.26.0-dev.14) (2025-12-28)
|
|
||||||
|
|
||||||
|
|
||||||
### Bug Fixes
|
|
||||||
|
|
||||||
* Update selected patch count when SelectionState changes ([#2896](https://github.com/ReVanced/revanced-manager/issues/2896)) ([0d26df0](https://github.com/ReVanced/revanced-manager/commit/0d26df03f463195dae550240c7f652680763079c))
|
|
||||||
|
|
||||||
# app [1.26.0-dev.13](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.12...v1.26.0-dev.13) (2025-12-17)
|
# app [1.26.0-dev.13](https://github.com/ReVanced/revanced-manager/compare/v1.26.0-dev.12...v1.26.0-dev.13) (2025-12-17)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
version = 1.26.0-dev.14
|
version = 1.26.0-dev.13
|
||||||
|
|||||||
@@ -54,7 +54,11 @@ class RootInstaller(
|
|||||||
await()
|
await()
|
||||||
}
|
}
|
||||||
|
|
||||||
suspend fun execute(vararg commands: String) = getShell().newJob().add(*commands).exec()
|
suspend fun execute(vararg commands: String): Shell.Result {
|
||||||
|
val stdout = mutableListOf<String>()
|
||||||
|
val stderr = mutableListOf<String>()
|
||||||
|
return getShell().newJob().add(*commands).to(stdout, stderr).exec()
|
||||||
|
}
|
||||||
|
|
||||||
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
|
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
|
||||||
|
|
||||||
@@ -108,20 +112,15 @@ class RootInstaller(
|
|||||||
unmount(packageName)
|
unmount(packageName)
|
||||||
|
|
||||||
stockAPK?.let { stockApp ->
|
stockAPK?.let { stockApp ->
|
||||||
pm.getPackageInfo(packageName)?.let { packageInfo ->
|
// TODO: get user id programmatically
|
||||||
// TODO: get user id programmatically
|
execute("pm uninstall -k --user 0 $packageName")
|
||||||
if (pm.getVersionCode(packageInfo) <= pm.getVersionCode(
|
|
||||||
pm.getPackageInfo(patchedAPK)
|
|
||||||
?: error("Failed to get package info for patched app")
|
|
||||||
)
|
|
||||||
)
|
|
||||||
execute("pm uninstall -k --user 0 $packageName").assertSuccess("Failed to uninstall stock app")
|
|
||||||
}
|
|
||||||
|
|
||||||
execute("pm install \"${stockApp.absolutePath}\"").assertSuccess("Failed to install stock app")
|
execute("pm install -r -d --user 0 \"${stockApp.absolutePath}\"")
|
||||||
|
.assertSuccess("Failed to install stock app")
|
||||||
}
|
}
|
||||||
|
|
||||||
remoteFS.getFile(modulePath).mkdir()
|
remoteFS.getFile(modulePath).mkdirs()
|
||||||
|
.also { if (!it) throw Exception("Failed to create module directory") }
|
||||||
|
|
||||||
listOf(
|
listOf(
|
||||||
"service.sh",
|
"service.sh",
|
||||||
@@ -142,7 +141,6 @@ class RootInstaller(
|
|||||||
}
|
}
|
||||||
|
|
||||||
"$modulePath/$packageName.apk".let { apkPath ->
|
"$modulePath/$packageName.apk".let { apkPath ->
|
||||||
|
|
||||||
remoteFS.getFile(patchedAPK.absolutePath)
|
remoteFS.getFile(patchedAPK.absolutePath)
|
||||||
.also { if (!it.exists()) throw Exception("File doesn't exist") }
|
.also { if (!it.exists()) throw Exception("File doesn't exist") }
|
||||||
.newInputStream().use { inputStream ->
|
.newInputStream().use { inputStream ->
|
||||||
@@ -173,9 +171,43 @@ class RootInstaller(
|
|||||||
const val modulesPath = "/data/adb/modules"
|
const val modulesPath = "/data/adb/modules"
|
||||||
|
|
||||||
private fun Shell.Result.assertSuccess(errorMessage: String) {
|
private fun Shell.Result.assertSuccess(errorMessage: String) {
|
||||||
if (!isSuccess) throw Exception(errorMessage)
|
if (!isSuccess) {
|
||||||
|
throw ShellCommandException(
|
||||||
|
errorMessage,
|
||||||
|
code,
|
||||||
|
out,
|
||||||
|
err
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class ShellCommandException(
|
||||||
|
val userMessage: String,
|
||||||
|
val exitCode: Int,
|
||||||
|
val stdout: List<String>,
|
||||||
|
val stderr: List<String>
|
||||||
|
) : Exception(format(userMessage, exitCode, stdout, stderr)) {
|
||||||
|
companion object {
|
||||||
|
private fun format(message: String, exitCode: Int, stdout: List<String>, stderr: List<String>): String =
|
||||||
|
buildString {
|
||||||
|
appendLine(message)
|
||||||
|
appendLine("Exit code: $exitCode")
|
||||||
|
|
||||||
|
val output = stdout.filter { it.isNotBlank() }
|
||||||
|
val errors = stderr.filter { it.isNotBlank() }
|
||||||
|
|
||||||
|
if (output.isNotEmpty()) {
|
||||||
|
appendLine("stdout:")
|
||||||
|
output.forEach(::appendLine)
|
||||||
|
}
|
||||||
|
if (errors.isNotEmpty()) {
|
||||||
|
appendLine("stderr:")
|
||||||
|
errors.forEach(::appendLine)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
class RootServiceException : Exception("Root not available")
|
class RootServiceException : Exception("Root not available")
|
||||||
@@ -25,7 +25,6 @@ import androidx.compose.material3.TextButton
|
|||||||
import androidx.compose.material3.TopAppBarDefaults
|
import androidx.compose.material3.TopAppBarDefaults
|
||||||
import androidx.compose.material3.rememberTopAppBarState
|
import androidx.compose.material3.rememberTopAppBarState
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
@@ -77,12 +76,12 @@ fun SelectedAppInfoScreen(
|
|||||||
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
|
val bundles by vm.bundleInfoFlow.collectAsStateWithLifecycle(emptyList())
|
||||||
|
|
||||||
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
|
val allowIncompatiblePatches by vm.prefs.disablePatchVersionCompatCheck.getAsState()
|
||||||
val patches by remember {
|
val patches = remember(bundles, allowIncompatiblePatches) {
|
||||||
derivedStateOf {
|
vm.getPatches(bundles, allowIncompatiblePatches)
|
||||||
vm.getPatches(bundles, allowIncompatiblePatches)
|
}
|
||||||
}
|
val selectedPatchCount = remember(patches) {
|
||||||
|
patches.values.sumOf { it.size }
|
||||||
}
|
}
|
||||||
val selectedPatchCount = patches.values.sumOf { it.size }
|
|
||||||
|
|
||||||
val launcher = rememberLauncherForActivityResult(
|
val launcher = rememberLauncherForActivityResult(
|
||||||
contract = ActivityResultContracts.StartActivityForResult(),
|
contract = ActivityResultContracts.StartActivityForResult(),
|
||||||
|
|||||||
@@ -377,22 +377,22 @@ class PatcherViewModel(
|
|||||||
try {
|
try {
|
||||||
isInstalling = true
|
isInstalling = true
|
||||||
|
|
||||||
val currentPackageInfo = pm.getPackageInfo(outputFile)
|
|
||||||
?: throw Exception("Failed to load application info")
|
|
||||||
|
|
||||||
// If the app is currently installed
|
|
||||||
val existingPackageInfo = pm.getPackageInfo(currentPackageInfo.packageName)
|
|
||||||
if (existingPackageInfo != null) {
|
|
||||||
// Check if the app version is less than the installed version
|
|
||||||
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
|
||||||
// Exit if the selected app version is less than the installed version
|
|
||||||
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (installType) {
|
when (installType) {
|
||||||
InstallType.DEFAULT -> {
|
InstallType.DEFAULT -> {
|
||||||
|
val currentPackageInfo = pm.getPackageInfo(outputFile)
|
||||||
|
?: throw Exception("Failed to load application info")
|
||||||
|
|
||||||
|
// If the app is currently installed
|
||||||
|
val existingPackageInfo = pm.getPackageInfo(currentPackageInfo.packageName)
|
||||||
|
if (existingPackageInfo != null) {
|
||||||
|
// Check if the app version is less than the installed version
|
||||||
|
if (pm.getVersionCode(currentPackageInfo) < pm.getVersionCode(existingPackageInfo)) {
|
||||||
|
// Exit if the selected app version is less than the installed version
|
||||||
|
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_CONFLICT
|
||||||
|
return@launch
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Check if the app is mounted as root
|
// Check if the app is mounted as root
|
||||||
// If it is, unmount it first, silently
|
// If it is, unmount it first, silently
|
||||||
if (rootInstaller.hasRootAccess() && rootInstaller.isAppMounted(packageName)) {
|
if (rootInstaller.hasRootAccess() && rootInstaller.isAppMounted(packageName)) {
|
||||||
@@ -412,16 +412,6 @@ class PatcherViewModel(
|
|||||||
packageInfo.label()
|
packageInfo.label()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Check for base APK, first check if the app is already installed
|
|
||||||
if (existingPackageInfo == null) {
|
|
||||||
// If the app is not installed, check if the output file is a base apk
|
|
||||||
if (currentPackageInfo.splitNames.isNotEmpty()) {
|
|
||||||
// Exit if there is no base APK package
|
|
||||||
packageInstallerStatus = PackageInstaller.STATUS_FAILURE_INVALID
|
|
||||||
return@launch
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val inputVersion = input.selectedApp.version
|
val inputVersion = input.selectedApp.version
|
||||||
?: inputFile?.let(pm::getPackageInfo)?.versionName
|
?: inputFile?.let(pm::getPackageInfo)?.versionName
|
||||||
?: throw Exception("Failed to determine input APK version")
|
?: throw Exception("Failed to determine input APK version")
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import android.os.Parcelable
|
|||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.activity.result.ActivityResult
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.annotation.StringRes
|
import androidx.annotation.StringRes
|
||||||
|
import androidx.compose.runtime.MutableState
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@@ -128,6 +129,8 @@ class SelectedAppInfoViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
var options: Options by savedStateHandle.saveable {
|
var options: Options by savedStateHandle.saveable {
|
||||||
|
val state = mutableStateOf<Options>(emptyMap())
|
||||||
|
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (!persistConfiguration) return@launch // TODO: save options for patched apps.
|
if (!persistConfiguration) return@launch // TODO: save options for patched apps.
|
||||||
val bundlePatches = bundleInfoFlow.first()
|
val bundlePatches = bundleInfoFlow.first()
|
||||||
@@ -138,7 +141,7 @@ class SelectedAppInfoViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mutableStateOf(emptyMap())
|
state
|
||||||
}
|
}
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@@ -146,6 +149,8 @@ class SelectedAppInfoViewModel(
|
|||||||
if (input.patches != null)
|
if (input.patches != null)
|
||||||
return@saveable mutableStateOf(SelectionState.Customized(input.patches))
|
return@saveable mutableStateOf(SelectionState.Customized(input.patches))
|
||||||
|
|
||||||
|
val selection: MutableState<SelectionState> = mutableStateOf(SelectionState.Default)
|
||||||
|
|
||||||
// Try to get the previous selection if customization is enabled.
|
// Try to get the previous selection if customization is enabled.
|
||||||
viewModelScope.launch {
|
viewModelScope.launch {
|
||||||
if (!prefs.disableSelectionWarning.get()) return@launch
|
if (!prefs.disableSelectionWarning.get()) return@launch
|
||||||
@@ -155,7 +160,7 @@ class SelectedAppInfoViewModel(
|
|||||||
selectionState = SelectionState.Customized(previous)
|
selectionState = SelectionState.Customized(previous)
|
||||||
}
|
}
|
||||||
|
|
||||||
mutableStateOf(SelectionState.Default)
|
selection
|
||||||
}
|
}
|
||||||
|
|
||||||
var showSourceSelector by mutableStateOf(false)
|
var showSourceSelector by mutableStateOf(false)
|
||||||
@@ -306,7 +311,7 @@ class SelectedAppInfoViewModel(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum class Error(@param:StringRes val resourceId: Int) {
|
enum class Error(@StringRes val resourceId: Int) {
|
||||||
NoPlugins(R.string.downloader_no_plugins_available)
|
NoPlugins(R.string.downloader_no_plugins_available)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user