diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 44cb3a2..dbbd290 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") } diff --git a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt index 4856273..8492352 100644 --- a/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt +++ b/app/src/main/java/app/revanced/manager/domain/manager/PreferencesManager.kt @@ -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) diff --git a/app/src/main/java/app/revanced/manager/service/ShizkuService.kt b/app/src/main/java/app/revanced/manager/service/ShizkuService.kt index e9214ce..9edffa5 100644 --- a/app/src/main/java/app/revanced/manager/service/ShizkuService.kt +++ b/app/src/main/java/app/revanced/manager/service/ShizkuService.kt @@ -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() } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt index 2f1d6a2..e48e3ae 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/DashboardScreen.kt @@ -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, diff --git a/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt index ab1a5e4..5ebec04 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/InstalledAppsScreen.kt @@ -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 + ) + } } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt index 15aceb1..d930c01 100644 --- a/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt +++ b/app/src/main/java/app/revanced/manager/ui/screen/settings/AdvancedSettingsScreen.kt @@ -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)) } - } - ) + }) } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt index 4c88c85..c6dbdd4 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/AdvancedSettingsViewModel.kt @@ -36,6 +36,6 @@ class AdvancedSettingsViewModel( } fun setInstaller(installer: PreferencesManager.InstallerManager) = viewModelScope.launch { - prefs.installer.update(installer) + prefs.defaultInstaller.update(installer) } } \ No newline at end of file diff --git a/app/src/main/java/app/revanced/manager/ui/viewmodel/InstallerViewModel.kt b/app/src/main/java/app/revanced/manager/ui/viewmodel/InstallerViewModel.kt index 5b1878d..2c7a30e 100644 --- a/app/src/main/java/app/revanced/manager/ui/viewmodel/InstallerViewModel.kt +++ b/app/src/main/java/app/revanced/manager/ui/viewmodel/InstallerViewModel.kt @@ -64,7 +64,7 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon var installedPackageName by mutableStateOf(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)