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
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")
}

View File

@ -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)

View File

@ -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()
}

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.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,

View File

@ -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
)
}
}
}

View File

@ -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))
}
}
)
})
}

View File

@ -36,6 +36,6 @@ class AdvancedSettingsViewModel(
}
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)
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)