fix: don't let user select shizuku installer if permission is not granted

This commit is contained in:
Aunali321 2023-08-15 01:14:53 +05:30
parent c5d859f990
commit 9c13c66a81
8 changed files with 100 additions and 156 deletions

View File

@ -148,6 +148,5 @@ dependencies {
// Shizuku // Shizuku
implementation("dev.rikka.shizuku:api:13.1.2") implementation("dev.rikka.shizuku:api:13.1.2")
implementation("dev.rikka.shizuku:provider: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") compileOnly("dev.rikka.hidden:stub:4.2.0")
} }

View File

@ -27,7 +27,7 @@ class PreferencesManager(
val keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT) val keystorePass = stringPreference("keystore_pass", KeystoreManager.DEFAULT)
val preferSplits = booleanPreference("prefer_splits", false) 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 showAutoUpdatesDialog = booleanPreference("show_auto_updates_dialog", true)
val managerAutoUpdates = booleanPreference("manager_auto_updates", false) val managerAutoUpdates = booleanPreference("manager_auto_updates", false)

View File

@ -1,53 +1,17 @@
package app.revanced.manager.service package app.revanced.manager.service
import android.content.Intent 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.content.pm.PackageManager
import android.os.Build
import android.os.IBinder
import android.os.IInterface
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.core.content.FileProvider import androidx.core.content.FileProvider
import app.revanced.manager.rvmApp import app.revanced.manager.rvmApp
import dev.rikka.tools.refine.Refine
import rikka.shizuku.Shizuku import rikka.shizuku.Shizuku
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
import java.io.File import java.io.File
object ShizukuApi { 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 isBinderAvailable = false
var isPermissionGranted by mutableStateOf(false) var isPermissionGranted by mutableStateOf(false)
@ -71,4 +35,7 @@ object ShizukuApi {
rvmApp.startActivity(intent) rvmApp.startActivity(intent)
} }
fun isShizukuPermissionGranted() = isBinderAvailable && isPermissionGranted
fun isShizukuInstalled() = Shizuku.pingBinder()
} }

View File

@ -39,7 +39,6 @@ import app.revanced.manager.ui.component.AppTopBar
import app.revanced.manager.ui.component.bundle.ImportBundleDialog import app.revanced.manager.ui.component.bundle.ImportBundleDialog
import app.revanced.manager.ui.viewmodel.DashboardViewModel import app.revanced.manager.ui.viewmodel.DashboardViewModel
import app.revanced.manager.util.toast import app.revanced.manager.util.toast
import app.revanced.manager.ui.component.ShizukuCard
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import org.koin.androidx.compose.getViewModel import org.koin.androidx.compose.getViewModel
@ -142,7 +141,6 @@ fun DashboardScreen(
) )
} }
} }
ShizukuCard()
HorizontalPager( HorizontalPager(
pageCount = pages.size, pageCount = pages.size,
state = pagerState, state = pagerState,

View File

@ -1,6 +1,7 @@
package app.revanced.manager.ui.screen package app.revanced.manager.ui.screen
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
@ -9,13 +10,21 @@ 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
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.service.ShizukuApi
import app.revanced.manager.ui.component.ShizukuCard
@Composable @Composable
fun InstalledAppsScreen() { fun InstalledAppsScreen() {
Column(
modifier = Modifier.fillMaxSize(),
) {
if (ShizukuApi.isShizukuInstalled()) ShizukuCard()
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
Text( Text(
text = stringResource(R.string.no_patched_apps_found), text = stringResource(R.string.no_patched_apps_found),
style = MaterialTheme.typography.titleLarge style = MaterialTheme.typography.titleLarge
) )
} }
}
} }

View File

@ -1,7 +1,9 @@
package app.revanced.manager.ui.screen.settings package app.revanced.manager.ui.screen.settings
import android.app.ActivityManager import android.app.ActivityManager
import android.content.Context
import android.os.Build import android.os.Build
import android.widget.Toast
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
@ -41,6 +43,7 @@ import androidx.compose.ui.unit.dp
import androidx.core.content.getSystemService import androidx.core.content.getSystemService
import app.revanced.manager.R import app.revanced.manager.R
import app.revanced.manager.domain.manager.PreferencesManager 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.AppTopBar
import app.revanced.manager.ui.component.GroupHeader import app.revanced.manager.ui.component.GroupHeader
import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel import app.revanced.manager.ui.viewmodel.AdvancedSettingsViewModel
@ -50,8 +53,7 @@ import org.koin.compose.koinInject
@OptIn(ExperimentalMaterial3Api::class) @OptIn(ExperimentalMaterial3Api::class)
@Composable @Composable
fun AdvancedSettingsScreen( fun AdvancedSettingsScreen(
onBackClick: () -> Unit, onBackClick: () -> Unit, vm: AdvancedSettingsViewModel = getViewModel()
vm: AdvancedSettingsViewModel = getViewModel()
) { ) {
val prefs = vm.prefs val prefs = vm.prefs
val context = LocalContext.current val context = LocalContext.current
@ -66,20 +68,15 @@ fun AdvancedSettingsScreen(
} }
if (showInstallerPicker) { if (showInstallerPicker) {
InstallerPicker( InstallerPicker(onDismiss = { showInstallerPicker = false },
onDismiss = { showInstallerPicker = false }, onConfirm = { vm.setInstaller(it) })
onConfirm = { vm.setInstaller(it) }
)
} }
Scaffold( Scaffold(topBar = {
topBar = {
AppTopBar( AppTopBar(
title = stringResource(R.string.advanced), title = stringResource(R.string.advanced), onBackClick = onBackClick
onBackClick = onBackClick
) )
} }) { paddingValues ->
) { paddingValues ->
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@ -95,65 +92,46 @@ fun AdvancedSettingsScreen(
it?.let(vm::setApiUrl) it?.let(vm::setApiUrl)
} }
} }
ListItem( ListItem(headlineContent = { Text(stringResource(R.string.api_url)) },
headlineContent = { Text(stringResource(R.string.api_url)) },
supportingContent = { Text(apiUrl) }, supportingContent = { Text(apiUrl) },
modifier = Modifier.clickable { modifier = Modifier.clickable {
showApiUrlDialog = true showApiUrlDialog = true
} })
)
GroupHeader(stringResource(R.string.patch_bundles_section)) GroupHeader(stringResource(R.string.patch_bundles_section))
ListItem( ListItem(headlineContent = { Text(stringResource(R.string.patch_bundles_redownload)) },
headlineContent = { Text(stringResource(R.string.patch_bundles_redownload)) },
modifier = Modifier.clickable { modifier = Modifier.clickable {
vm.redownloadBundles() vm.redownloadBundles()
} })
) ListItem(headlineContent = { Text(stringResource(R.string.patch_bundles_reset)) },
ListItem(
headlineContent = { Text(stringResource(R.string.patch_bundles_reset)) },
modifier = Modifier.clickable { modifier = Modifier.clickable {
vm.resetBundles() vm.resetBundles()
} })
)
val installer by prefs.installer.getAsState() val installer by prefs.defaultInstaller.getAsState()
GroupHeader(stringResource(R.string.installer)) GroupHeader(stringResource(R.string.installer))
ListItem( ListItem(modifier = Modifier.clickable { showInstallerPicker = true },
modifier = Modifier.clickable { showInstallerPicker = true },
headlineContent = { Text(stringResource(R.string.installer)) }, headlineContent = { Text(stringResource(R.string.installer)) },
supportingContent = { Text(stringResource(R.string.installer_description)) }, supportingContent = { Text(stringResource(R.string.installer_description)) },
trailingContent = { trailingContent = {
FilledTonalButton( FilledTonalButton(colors = ButtonDefaults.filledTonalButtonColors(
colors = ButtonDefaults.filledTonalButtonColors(
containerColor = MaterialTheme.colorScheme.primaryContainer, containerColor = MaterialTheme.colorScheme.primaryContainer,
), ), onClick = {
onClick = {
showInstallerPicker = true showInstallerPicker = true
} }) {
) {
Text(stringResource(installer.displayName)) Text(stringResource(installer.displayName))
} }
} })
)
GroupHeader(stringResource(R.string.device)) GroupHeader(stringResource(R.string.device))
ListItem( ListItem(headlineContent = { Text(stringResource(R.string.device_model)) },
headlineContent = { Text(stringResource(R.string.device_model)) }, supportingContent = { Text(Build.MODEL) })
supportingContent = { Text(Build.MODEL) } ListItem(headlineContent = { Text(stringResource(R.string.device_android_version)) },
) supportingContent = { Text(Build.VERSION.RELEASE) })
ListItem( ListItem(headlineContent = { Text(stringResource(R.string.device_architectures)) },
headlineContent = { Text(stringResource(R.string.device_android_version)) }, supportingContent = { Text(Build.SUPPORTED_ABIS.joinToString(", ")) })
supportingContent = { Text(Build.VERSION.RELEASE) } ListItem(headlineContent = { Text(stringResource(R.string.device_memory_limit)) },
) supportingContent = { Text(memoryLimit) })
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,33 +140,25 @@ fun AdvancedSettingsScreen(
private fun APIUrlDialog(currentUrl: String, onSubmit: (String?) -> Unit) { private fun APIUrlDialog(currentUrl: String, onSubmit: (String?) -> Unit) {
var url by rememberSaveable(currentUrl) { mutableStateOf(currentUrl) } var url by rememberSaveable(currentUrl) { mutableStateOf(currentUrl) }
AlertDialog( AlertDialog(onDismissRequest = { onSubmit(null) }, confirmButton = {
onDismissRequest = { onSubmit(null) }, TextButton(onClick = {
confirmButton = {
TextButton(
onClick = {
onSubmit(url) onSubmit(url)
} }) {
) {
Text(stringResource(R.string.api_url_dialog_save)) Text(stringResource(R.string.api_url_dialog_save))
} }
}, }, dismissButton = {
dismissButton = {
TextButton(onClick = { onSubmit(null) }) { TextButton(onClick = { onSubmit(null) }) {
Text(stringResource(R.string.cancel)) Text(stringResource(R.string.cancel))
} }
}, }, icon = {
icon = {
Icon(Icons.Outlined.Http, null) Icon(Icons.Outlined.Http, null)
}, }, title = {
title = {
Text( Text(
text = stringResource(R.string.api_url_dialog_title), text = stringResource(R.string.api_url_dialog_title),
style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center), style = MaterialTheme.typography.headlineSmall.copy(textAlign = TextAlign.Center),
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
) )
}, }, text = {
text = {
Column( Column(
verticalArrangement = Arrangement.spacedBy(16.dp) verticalArrangement = Arrangement.spacedBy(16.dp)
) { ) {
@ -202,14 +172,11 @@ private fun APIUrlDialog(currentUrl: String, onSubmit: (String?) -> Unit) {
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.error color = MaterialTheme.colorScheme.error
) )
OutlinedTextField( OutlinedTextField(value = url,
value = url,
onValueChange = { url = it }, onValueChange = { url = it },
label = { Text(stringResource(R.string.api_url)) } label = { Text(stringResource(R.string.api_url)) })
)
} }
} })
)
} }
@Composable @Composable
@ -218,10 +185,10 @@ private fun InstallerPicker(
onConfirm: (PreferencesManager.InstallerManager) -> Unit, onConfirm: (PreferencesManager.InstallerManager) -> Unit,
prefs: PreferencesManager = koinInject() 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( AlertDialog(onDismissRequest = onDismiss,
onDismissRequest = onDismiss,
title = { Text(stringResource(R.string.installer)) }, title = { Text(stringResource(R.string.installer)) },
text = { text = {
Column { Column {
@ -232,8 +199,7 @@ private fun InstallerPicker(
.clickable { selectedInstaller = it }, .clickable { selectedInstaller = it },
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
RadioButton( RadioButton(selected = selectedInstaller == it,
selected = selectedInstaller == it,
onClick = { selectedInstaller = it }) onClick = { selectedInstaller = it })
Text(stringResource(it.displayName)) Text(stringResource(it.displayName))
} }
@ -242,11 +208,16 @@ private fun InstallerPicker(
}, },
confirmButton = { confirmButton = {
Button(onClick = { Button(onClick = {
if (selectedInstaller == PreferencesManager.InstallerManager.SHIZUKU && !ShizukuApi.isShizukuPermissionGranted()) {
Toast.makeText(
context, R.string.shizuku_unavailable, Toast.LENGTH_SHORT
).show()
return@Button
}
onConfirm(selectedInstaller) onConfirm(selectedInstaller)
onDismiss() onDismiss()
}) { }) {
Text(stringResource(R.string.apply)) Text(stringResource(R.string.apply))
} }
} })
)
} }

View File

@ -36,6 +36,6 @@ class AdvancedSettingsViewModel(
} }
fun setInstaller(installer: PreferencesManager.InstallerManager) = viewModelScope.launch { fun setInstaller(installer: PreferencesManager.InstallerManager) = viewModelScope.launch {
prefs.installer.update(installer) prefs.defaultInstaller.update(installer)
} }
} }

View File

@ -64,7 +64,7 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
var installedPackageName by mutableStateOf<String?>(null) var installedPackageName by mutableStateOf<String?>(null)
private set private set
val appButtonText by derivedStateOf { if (installedPackageName == null) R.string.install_app else R.string.open_app } 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) private val workManager = WorkManager.getInstance(app)