mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 05:54:26 +02:00
feat(patcher): Improve installation (#2185)
This commit is contained in:
parent
d201bdc422
commit
f6f72387b9
@ -9,7 +9,7 @@ import kotlinx.parcelize.Parcelize
|
|||||||
|
|
||||||
enum class InstallType(val stringResource: Int) {
|
enum class InstallType(val stringResource: Int) {
|
||||||
DEFAULT(R.string.default_install),
|
DEFAULT(R.string.default_install),
|
||||||
ROOT(R.string.root_install)
|
MOUNT(R.string.mount_install)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
|
@ -43,7 +43,7 @@ class RootInstaller(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return withTimeoutOrNull(Duration.ofSeconds(120L)) {
|
return withTimeoutOrNull(Duration.ofSeconds(20L)) {
|
||||||
remoteFS.await()
|
remoteFS.await()
|
||||||
} ?: throw RootServiceException()
|
} ?: throw RootServiceException()
|
||||||
}
|
}
|
||||||
@ -58,6 +58,10 @@ class RootInstaller(
|
|||||||
|
|
||||||
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
|
fun hasRootAccess() = Shell.isAppGrantedRoot() ?: false
|
||||||
|
|
||||||
|
fun isDeviceRooted() = System.getenv("PATH")?.split(":")?.any { path ->
|
||||||
|
File(path, "su").canExecute()
|
||||||
|
} ?: false
|
||||||
|
|
||||||
suspend fun isAppInstalled(packageName: String) =
|
suspend fun isAppInstalled(packageName: String) =
|
||||||
awaitRemoteFS().getFile("$modulesPath/$packageName-revanced").exists()
|
awaitRemoteFS().getFile("$modulesPath/$packageName-revanced").exists()
|
||||||
|
|
||||||
|
@ -135,7 +135,7 @@ class PatcherWorker(
|
|||||||
return try {
|
return try {
|
||||||
if (args.input is SelectedApp.Installed) {
|
if (args.input is SelectedApp.Installed) {
|
||||||
installedAppRepository.get(args.packageName)?.let {
|
installedAppRepository.get(args.packageName)?.let {
|
||||||
if (it.installType == InstallType.ROOT) {
|
if (it.installType == InstallType.MOUNT) {
|
||||||
rootInstaller.unmount(args.packageName)
|
rootInstaller.unmount(args.packageName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -7,6 +7,7 @@ import androidx.compose.material3.Button
|
|||||||
import androidx.compose.material3.ListItem
|
import androidx.compose.material3.ListItem
|
||||||
import androidx.compose.material3.RadioButton
|
import androidx.compose.material3.RadioButton
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
@ -27,7 +28,7 @@ fun InstallPickerDialog(
|
|||||||
AlertDialog(
|
AlertDialog(
|
||||||
onDismissRequest = onDismiss,
|
onDismissRequest = onDismiss,
|
||||||
dismissButton = {
|
dismissButton = {
|
||||||
Button(onClick = onDismiss) {
|
TextButton(onClick = onDismiss) {
|
||||||
Text(stringResource(R.string.cancel))
|
Text(stringResource(R.string.cancel))
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
@ -81,7 +81,7 @@ fun InstalledAppInfoScreen(
|
|||||||
AppInfo(viewModel.appInfo) {
|
AppInfo(viewModel.appInfo) {
|
||||||
Text(viewModel.installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
|
Text(viewModel.installedApp.version, color = MaterialTheme.colorScheme.onSurfaceVariant, style = MaterialTheme.typography.bodyMedium)
|
||||||
|
|
||||||
if (viewModel.installedApp.installType == InstallType.ROOT) {
|
if (viewModel.installedApp.installType == InstallType.MOUNT) {
|
||||||
Text(
|
Text(
|
||||||
text = if (viewModel.isMounted) {
|
text = if (viewModel.isMounted) {
|
||||||
stringResource(R.string.mounted)
|
stringResource(R.string.mounted)
|
||||||
@ -112,7 +112,7 @@ fun InstalledAppInfoScreen(
|
|||||||
onClick = viewModel::uninstall
|
onClick = viewModel::uninstall
|
||||||
)
|
)
|
||||||
|
|
||||||
InstallType.ROOT -> {
|
InstallType.MOUNT -> {
|
||||||
SegmentedButton(
|
SegmentedButton(
|
||||||
icon = Icons.Outlined.SettingsBackupRestore,
|
icon = Icons.Outlined.SettingsBackupRestore,
|
||||||
text = stringResource(R.string.unpatch),
|
text = stringResource(R.string.unpatch),
|
||||||
@ -138,7 +138,7 @@ fun InstalledAppInfoScreen(
|
|||||||
onPatchClick(viewModel.installedApp.originalPackageName, it)
|
onPatchClick(viewModel.installedApp.originalPackageName, it)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
enabled = viewModel.installedApp.installType != InstallType.ROOT || viewModel.rootInstaller.hasRootAccess()
|
enabled = viewModel.installedApp.installType != InstallType.MOUNT || viewModel.rootInstaller.hasRootAccess()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -38,6 +38,7 @@ import androidx.compose.ui.res.stringResource
|
|||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
|
import app.revanced.manager.data.room.apps.installed.InstallType
|
||||||
import app.revanced.manager.ui.component.AppScaffold
|
import app.revanced.manager.ui.component.AppScaffold
|
||||||
import app.revanced.manager.ui.component.AppTopBar
|
import app.revanced.manager.ui.component.AppTopBar
|
||||||
import app.revanced.manager.ui.component.InstallerStatusDialog
|
import app.revanced.manager.ui.component.InstallerStatusDialog
|
||||||
@ -139,7 +140,8 @@ fun PatcherScreen(
|
|||||||
},
|
},
|
||||||
onClick = {
|
onClick = {
|
||||||
if (vm.installedPackageName == null)
|
if (vm.installedPackageName == null)
|
||||||
showInstallPicker = true
|
if (vm.isDeviceRooted()) showInstallPicker = true
|
||||||
|
else vm.install(InstallType.DEFAULT)
|
||||||
else vm.open()
|
else vm.open()
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
@ -113,8 +113,8 @@ fun VersionSelectorScreen(
|
|||||||
onClick = { viewModel.select(it) },
|
onClick = { viewModel.select(it) },
|
||||||
patchCount = supportedVersions[it.version],
|
patchCount = supportedVersions[it.version],
|
||||||
enabled =
|
enabled =
|
||||||
!(installedApp?.installType == InstallType.ROOT && !viewModel.rootInstaller.hasRootAccess()),
|
!(installedApp?.installType == InstallType.MOUNT && !viewModel.rootInstaller.hasRootAccess()),
|
||||||
alreadyPatched = installedApp != null && installedApp.installType != InstallType.ROOT
|
alreadyPatched = installedApp != null && installedApp.installType != InstallType.MOUNT
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -78,7 +78,7 @@ class InstalledAppInfoViewModel(
|
|||||||
when (installedApp.installType) {
|
when (installedApp.installType) {
|
||||||
InstallType.DEFAULT -> pm.uninstallPackage(installedApp.currentPackageName)
|
InstallType.DEFAULT -> pm.uninstallPackage(installedApp.currentPackageName)
|
||||||
|
|
||||||
InstallType.ROOT -> viewModelScope.launch {
|
InstallType.MOUNT -> viewModelScope.launch {
|
||||||
rootInstaller.uninstall(installedApp.currentPackageName)
|
rootInstaller.uninstall(installedApp.currentPackageName)
|
||||||
installedAppRepository.delete(installedApp)
|
installedAppRepository.delete(installedApp)
|
||||||
onBackClick()
|
onBackClick()
|
||||||
|
@ -30,7 +30,7 @@ class InstalledAppsViewModel(
|
|||||||
packageInfoMap[installedApp.currentPackageName] = withContext(Dispatchers.IO) {
|
packageInfoMap[installedApp.currentPackageName] = withContext(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
if (
|
if (
|
||||||
installedApp.installType == InstallType.ROOT && !rootInstaller.isAppInstalled(installedApp.currentPackageName)
|
installedApp.installType == InstallType.MOUNT && !rootInstaller.isAppInstalled(installedApp.currentPackageName)
|
||||||
) {
|
) {
|
||||||
installedAppsRepository.delete(installedApp)
|
installedAppsRepository.delete(installedApp)
|
||||||
return@withContext null
|
return@withContext null
|
||||||
@ -39,7 +39,7 @@ class InstalledAppsViewModel(
|
|||||||
|
|
||||||
val packageInfo = pm.getPackageInfo(installedApp.currentPackageName)
|
val packageInfo = pm.getPackageInfo(installedApp.currentPackageName)
|
||||||
|
|
||||||
if (packageInfo == null && installedApp.installType != InstallType.ROOT) {
|
if (packageInfo == null && installedApp.installType != InstallType.MOUNT) {
|
||||||
installedAppsRepository.delete(installedApp)
|
installedAppsRepository.delete(installedApp)
|
||||||
return@withContext null
|
return@withContext null
|
||||||
}
|
}
|
||||||
|
@ -77,7 +77,7 @@ class PatcherViewModel(
|
|||||||
|
|
||||||
override fun install() {
|
override fun install() {
|
||||||
// Since this is a package installer status dialog,
|
// Since this is a package installer status dialog,
|
||||||
// InstallType.ROOT is never used here.
|
// InstallType.MOUNT is never used here.
|
||||||
install(InstallType.DEFAULT)
|
install(InstallType.DEFAULT)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -230,7 +230,7 @@ class PatcherViewModel(
|
|||||||
app.unregisterReceiver(installerBroadcastReceiver)
|
app.unregisterReceiver(installerBroadcastReceiver)
|
||||||
workManager.cancelWorkById(patcherWorkerId)
|
workManager.cancelWorkById(patcherWorkerId)
|
||||||
|
|
||||||
if (input.selectedApp is SelectedApp.Installed && installedApp?.installType == InstallType.ROOT) {
|
if (input.selectedApp is SelectedApp.Installed && installedApp?.installType == InstallType.MOUNT) {
|
||||||
GlobalScope.launch(Dispatchers.Main) {
|
GlobalScope.launch(Dispatchers.Main) {
|
||||||
uiSafe(app, R.string.failed_to_mount, "Failed to mount") {
|
uiSafe(app, R.string.failed_to_mount, "Failed to mount") {
|
||||||
withTimeout(Duration.ofMinutes(1L)) {
|
withTimeout(Duration.ofMinutes(1L)) {
|
||||||
@ -243,6 +243,8 @@ class PatcherViewModel(
|
|||||||
tempDir.deleteRecursively()
|
tempDir.deleteRecursively()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isDeviceRooted() = rootInstaller.isDeviceRooted()
|
||||||
|
|
||||||
fun export(uri: Uri?) = viewModelScope.launch {
|
fun export(uri: Uri?) = viewModelScope.launch {
|
||||||
uri?.let {
|
uri?.let {
|
||||||
withContext(Dispatchers.IO) {
|
withContext(Dispatchers.IO) {
|
||||||
@ -301,7 +303,7 @@ class PatcherViewModel(
|
|||||||
pmInstallStarted = true
|
pmInstallStarted = true
|
||||||
}
|
}
|
||||||
|
|
||||||
InstallType.ROOT -> {
|
InstallType.MOUNT -> {
|
||||||
try {
|
try {
|
||||||
// Check for base APK, first check if the app is already installed
|
// Check for base APK, first check if the app is already installed
|
||||||
if (existingPackageInfo == null) {
|
if (existingPackageInfo == null) {
|
||||||
@ -332,7 +334,7 @@ class PatcherViewModel(
|
|||||||
packageName,
|
packageName,
|
||||||
packageName,
|
packageName,
|
||||||
input.selectedApp.version,
|
input.selectedApp.version,
|
||||||
InstallType.ROOT,
|
InstallType.MOUNT,
|
||||||
input.selectedPatches
|
input.selectedPatches
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -223,7 +223,7 @@
|
|||||||
<string name="applied_patches">Applied patches</string>
|
<string name="applied_patches">Applied patches</string>
|
||||||
<string name="view_applied_patches">View applied patches</string>
|
<string name="view_applied_patches">View applied patches</string>
|
||||||
<string name="default_install">Default</string>
|
<string name="default_install">Default</string>
|
||||||
<string name="root_install">Root</string>
|
<string name="mount_install">Mount</string>
|
||||||
<string name="mounted">Mounted</string>
|
<string name="mounted">Mounted</string>
|
||||||
<string name="not_mounted">Not mounted</string>
|
<string name="not_mounted">Not mounted</string>
|
||||||
<string name="mount">Mount</string>
|
<string name="mount">Mount</string>
|
||||||
|
Loading…
x
Reference in New Issue
Block a user