mirror of
https://github.com/revanced/revanced-manager-compose.git
synced 2025-04-30 06:14:25 +02:00
fix: don't let user select shizuku installer if permission is not granted
This commit is contained in:
parent
c5d859f990
commit
9c13c66a81
@ -148,6 +148,5 @@ dependencies {
|
||||
// Shizuku
|
||||
implementation("dev.rikka.shizuku:api:13.1.2")
|
||||
implementation("dev.rikka.shizuku:provider:13.1.2")
|
||||
implementation("dev.rikka.tools.refine:runtime:4.3.0")
|
||||
compileOnly("dev.rikka.hidden:stub:4.2.0")
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ class PreferencesManager(
|
||||
val keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT)
|
||||
|
||||
val preferSplits = booleanPreference("prefer_splits", false)
|
||||
val installer = enumPreference("installer", InstallerManager.DEFAULT)
|
||||
val defaultInstaller = enumPreference("installer", InstallerManager.DEFAULT)
|
||||
|
||||
val showAutoUpdatesDialog = booleanPreference("show_auto_updates_dialog", true)
|
||||
val managerAutoUpdates = booleanPreference("manager_auto_updates", false)
|
||||
|
@ -1,53 +1,17 @@
|
||||
package app.revanced.manager.service
|
||||
|
||||
import android.content.Intent
|
||||
import android.content.pm.IPackageManager
|
||||
import android.content.pm.IPackageInstaller
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageInstallerHidden
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.IBinder
|
||||
import android.os.IInterface
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.core.content.FileProvider
|
||||
import app.revanced.manager.rvmApp
|
||||
import dev.rikka.tools.refine.Refine
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.shizuku.ShizukuBinderWrapper
|
||||
import rikka.shizuku.SystemServiceHelper
|
||||
import java.io.File
|
||||
|
||||
object ShizukuApi {
|
||||
|
||||
private fun IBinder.wrap() = ShizukuBinderWrapper(this)
|
||||
private fun IInterface.asShizukuBinder() = this.asBinder().wrap()
|
||||
|
||||
private val iPackageManager: IPackageManager by lazy {
|
||||
IPackageManager.Stub.asInterface(SystemServiceHelper.getSystemService("package").wrap())
|
||||
}
|
||||
|
||||
private val iPackageInstaller: IPackageInstaller by lazy {
|
||||
IPackageInstaller.Stub.asInterface(iPackageManager.packageInstaller.asShizukuBinder())
|
||||
}
|
||||
|
||||
private val packageInstaller: PackageInstaller by lazy {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
Refine.unsafeCast(
|
||||
PackageInstallerHidden(
|
||||
iPackageInstaller,
|
||||
"com.android.shell",
|
||||
null,
|
||||
0
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Refine.unsafeCast(PackageInstallerHidden(iPackageInstaller, "com.android.shell", 0))
|
||||
}
|
||||
}
|
||||
|
||||
var isBinderAvailable = false
|
||||
var isPermissionGranted by mutableStateOf(false)
|
||||
|
||||
@ -71,4 +35,7 @@ object ShizukuApi {
|
||||
rvmApp.startActivity(intent)
|
||||
}
|
||||
|
||||
fun isShizukuPermissionGranted() = isBinderAvailable && isPermissionGranted
|
||||
|
||||
fun isShizukuInstalled() = Shizuku.pingBinder()
|
||||
}
|
@ -39,7 +39,6 @@ import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.bundle.ImportBundleDialog
|
||||
import app.revanced.manager.ui.viewmodel.DashboardViewModel
|
||||
import app.revanced.manager.util.toast
|
||||
import app.revanced.manager.ui.component.ShizukuCard
|
||||
import kotlinx.coroutines.launch
|
||||
import org.koin.androidx.compose.getViewModel
|
||||
|
||||
@ -142,7 +141,6 @@ fun DashboardScreen(
|
||||
)
|
||||
}
|
||||
}
|
||||
ShizukuCard()
|
||||
HorizontalPager(
|
||||
pageCount = pages.size,
|
||||
state = pagerState,
|
||||
|
@ -1,6 +1,7 @@
|
||||
package app.revanced.manager.ui.screen
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
@ -9,13 +10,21 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.service.ShizukuApi
|
||||
import app.revanced.manager.ui.component.ShizukuCard
|
||||
|
||||
@Composable
|
||||
fun InstalledAppsScreen() {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_patched_apps_found),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
) {
|
||||
if (ShizukuApi.isShizukuInstalled()) ShizukuCard()
|
||||
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
Text(
|
||||
text = stringResource(R.string.no_patched_apps_found),
|
||||
style = MaterialTheme.typography.titleLarge
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
@ -1,7 +1,9 @@
|
||||
package app.revanced.manager.ui.screen.settings
|
||||
|
||||
import android.app.ActivityManager
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
@ -41,6 +43,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.getSystemService
|
||||
import app.revanced.manager.R
|
||||
import app.revanced.manager.domain.manager.PreferencesManager
|
||||
import app.revanced.manager.service.ShizukuApi
|
||||
import app.revanced.manager.ui.component.AppTopBar
|
||||
import app.revanced.manager.ui.component.GroupHeader
|
||||
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
|
||||
@ -50,8 +53,7 @@ import org.koin.compose.koinInject
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AdvancedSettingsScreen(
|
||||
onBackClick: () -> Unit,
|
||||
vm: AdvancedSettingsViewModel = getViewModel()
|
||||
onBackClick: () -> Unit, vm: AdvancedSettingsViewModel = getViewModel()
|
||||
) {
|
||||
val prefs = vm.prefs
|
||||
val context = LocalContext.current
|
||||
@ -66,20 +68,15 @@ fun AdvancedSettingsScreen(
|
||||
}
|
||||
|
||||
if (showInstallerPicker) {
|
||||
InstallerPicker(
|
||||
onDismiss = { showInstallerPicker = false },
|
||||
onConfirm = { vm.setInstaller(it) }
|
||||
)
|
||||
InstallerPicker(onDismiss = { showInstallerPicker = false },
|
||||
onConfirm = { vm.setInstaller(it) })
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.advanced),
|
||||
onBackClick = onBackClick
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Scaffold(topBar = {
|
||||
AppTopBar(
|
||||
title = stringResource(R.string.advanced), onBackClick = onBackClick
|
||||
)
|
||||
}) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@ -95,65 +92,46 @@ fun AdvancedSettingsScreen(
|
||||
it?.let(vm::setApiUrl)
|
||||
}
|
||||
}
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.api_url)) },
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.api_url)) },
|
||||
supportingContent = { Text(apiUrl) },
|
||||
modifier = Modifier.clickable {
|
||||
showApiUrlDialog = true
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
GroupHeader(stringResource(R.string.patch_bundles_section))
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.patch_bundles_redownload)) },
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.patch_bundles_redownload)) },
|
||||
modifier = Modifier.clickable {
|
||||
vm.redownloadBundles()
|
||||
}
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.patch_bundles_reset)) },
|
||||
})
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.patch_bundles_reset)) },
|
||||
modifier = Modifier.clickable {
|
||||
vm.resetBundles()
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
val installer by prefs.installer.getAsState()
|
||||
val installer by prefs.defaultInstaller.getAsState()
|
||||
GroupHeader(stringResource(R.string.installer))
|
||||
ListItem(
|
||||
modifier = Modifier.clickable { showInstallerPicker = true },
|
||||
ListItem(modifier = Modifier.clickable { showInstallerPicker = true },
|
||||
headlineContent = { Text(stringResource(R.string.installer)) },
|
||||
supportingContent = { Text(stringResource(R.string.installer_description)) },
|
||||
trailingContent = {
|
||||
FilledTonalButton(
|
||||
colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
),
|
||||
onClick = {
|
||||
showInstallerPicker = true
|
||||
}
|
||||
) {
|
||||
FilledTonalButton(colors = ButtonDefaults.filledTonalButtonColors(
|
||||
containerColor = MaterialTheme.colorScheme.primaryContainer,
|
||||
), onClick = {
|
||||
showInstallerPicker = true
|
||||
}) {
|
||||
Text(stringResource(installer.displayName))
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
GroupHeader(stringResource(R.string.device))
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.device_model)) },
|
||||
supportingContent = { Text(Build.MODEL) }
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.device_android_version)) },
|
||||
supportingContent = { Text(Build.VERSION.RELEASE) }
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.device_architectures)) },
|
||||
supportingContent = { Text(Build.SUPPORTED_ABIS.joinToString(", ")) }
|
||||
)
|
||||
ListItem(
|
||||
headlineContent = { Text(stringResource(R.string.device_memory_limit)) },
|
||||
supportingContent = { Text(memoryLimit) }
|
||||
)
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.device_model)) },
|
||||
supportingContent = { Text(Build.MODEL) })
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.device_android_version)) },
|
||||
supportingContent = { Text(Build.VERSION.RELEASE) })
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.device_architectures)) },
|
||||
supportingContent = { Text(Build.SUPPORTED_ABIS.joinToString(", ")) })
|
||||
ListItem(headlineContent = { Text(stringResource(R.string.device_memory_limit)) },
|
||||
supportingContent = { Text(memoryLimit) })
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -162,54 +140,43 @@ fun AdvancedSettingsScreen(
|
||||
private fun APIUrlDialog(currentUrl: String, onSubmit: (String?) -> Unit) {
|
||||
var url by rememberSaveable(currentUrl) { mutableStateOf(currentUrl) }
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = { onSubmit(null) },
|
||||
confirmButton = {
|
||||
TextButton(
|
||||
onClick = {
|
||||
onSubmit(url)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(R.string.api_url_dialog_save))
|
||||
}
|
||||
},
|
||||
dismissButton = {
|
||||
TextButton(onClick = { onSubmit(null) }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
},
|
||||
icon = {
|
||||
Icon(Icons.Outlined.Http, null)
|
||||
},
|
||||
title = {
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_title),
|
||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
},
|
||||
text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_warning),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
OutlinedTextField(
|
||||
value = url,
|
||||
onValueChange = { url = it },
|
||||
label = { Text(stringResource(R.string.api_url)) }
|
||||
)
|
||||
}
|
||||
AlertDialog(onDismissRequest = { onSubmit(null) }, confirmButton = {
|
||||
TextButton(onClick = {
|
||||
onSubmit(url)
|
||||
}) {
|
||||
Text(stringResource(R.string.api_url_dialog_save))
|
||||
}
|
||||
)
|
||||
}, dismissButton = {
|
||||
TextButton(onClick = { onSubmit(null) }) {
|
||||
Text(stringResource(R.string.cancel))
|
||||
}
|
||||
}, icon = {
|
||||
Icon(Icons.Outlined.Http, null)
|
||||
}, title = {
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_title),
|
||||
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
|
||||
color = MaterialTheme.colorScheme.onSurface,
|
||||
)
|
||||
}, text = {
|
||||
Column(
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_description),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.api_url_dialog_warning),
|
||||
style = MaterialTheme.typography.bodyMedium,
|
||||
color = MaterialTheme.colorScheme.error
|
||||
)
|
||||
OutlinedTextField(value = url,
|
||||
onValueChange = { url = it },
|
||||
label = { Text(stringResource(R.string.api_url)) })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ -218,10 +185,10 @@ private fun InstallerPicker(
|
||||
onConfirm: (PreferencesManager.InstallerManager) -> Unit,
|
||||
prefs: PreferencesManager = koinInject()
|
||||
) {
|
||||
var selectedInstaller by rememberSaveable { mutableStateOf(prefs.installer.getBlocking()) }
|
||||
var selectedInstaller by rememberSaveable { mutableStateOf(prefs.defaultInstaller.getBlocking()) }
|
||||
val context: Context = LocalContext.current
|
||||
|
||||
AlertDialog(
|
||||
onDismissRequest = onDismiss,
|
||||
AlertDialog(onDismissRequest = onDismiss,
|
||||
title = { Text(stringResource(R.string.installer)) },
|
||||
text = {
|
||||
Column {
|
||||
@ -232,8 +199,7 @@ private fun InstallerPicker(
|
||||
.clickable { selectedInstaller = it },
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
RadioButton(
|
||||
selected = selectedInstaller == it,
|
||||
RadioButton(selected = selectedInstaller == it,
|
||||
onClick = { selectedInstaller = it })
|
||||
Text(stringResource(it.displayName))
|
||||
}
|
||||
@ -242,11 +208,16 @@ private fun InstallerPicker(
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = {
|
||||
if (selectedInstaller == PreferencesManager.InstallerManager.SHIZUKU && !ShizukuApi.isShizukuPermissionGranted()) {
|
||||
Toast.makeText(
|
||||
context, R.string.shizuku_unavailable, Toast.LENGTH_SHORT
|
||||
).show()
|
||||
return@Button
|
||||
}
|
||||
onConfirm(selectedInstaller)
|
||||
onDismiss()
|
||||
}) {
|
||||
Text(stringResource(R.string.apply))
|
||||
}
|
||||
}
|
||||
)
|
||||
})
|
||||
}
|
@ -36,6 +36,6 @@ class AdvancedSettingsViewModel(
|
||||
}
|
||||
|
||||
fun setInstaller(installer: PreferencesManager.InstallerManager) = viewModelScope.launch {
|
||||
prefs.installer.update(installer)
|
||||
prefs.defaultInstaller.update(installer)
|
||||
}
|
||||
}
|
@ -64,7 +64,7 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
|
||||
var installedPackageName by mutableStateOf<String?>(null)
|
||||
private set
|
||||
val appButtonText by derivedStateOf { if (installedPackageName == null) R.string.install_app else R.string.open_app }
|
||||
private val selectedInstaller by derivedStateOf { prefs.installer.getBlocking() }
|
||||
private val selectedInstaller by derivedStateOf { prefs.defaultInstaller.getBlocking() }
|
||||
|
||||
private val workManager = WorkManager.getInstance(app)
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user