Skip to content
This repository has been archived by the owner on Jul 7, 2024. It is now read-only.

fix: request Shizuku permissions only when necessary #64

Merged
merged 3 commits into from
Jan 15, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,18 +1,25 @@
package dev.beefers.vendetta.manager.installer.shizuku

import android.content.Context
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.installer.Installer
import dev.beefers.vendetta.manager.utils.showToast
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import rikka.shizuku.Shizuku
import java.io.File
import java.util.UUID

class ShizukuInstaller(private val context: Context) : Installer {

companion object {
private val SESSION_ID_REGEX = Regex("(?<=\\[).+?(?=])")
}

override suspend fun installApks(silent: Boolean, vararg apks: File) {
if (!ShizukuPermissions.waitShizukuPermissions()) {
withContext(Dispatchers.Main) {
context.showToast(R.string.msg_shizuku_denied, short = false)
}

throw Error("Failed to install due to missing Shizuku permissions")
}

val tempDir = File("/data/local/tmp")
val movedApks = mutableListOf<File>()

Expand All @@ -36,6 +43,7 @@ class ShizukuInstaller(private val context: Context) : Installer {
}

private fun executeShellCommand(command: String): String {
@Suppress("DEPRECATION")
val process = Shizuku.newProcess(arrayOf("sh", "-c", command), null, null)

val errorStr = process.errorStream.bufferedReader().use { it.readText().trim() }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package dev.beefers.vendetta.manager.installer.shizuku

import android.content.pm.PackageManager
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.first
import kotlinx.coroutines.launch
import rikka.shizuku.Shizuku
import rikka.shizuku.Shizuku.OnRequestPermissionResultListener

@OptIn(DelicateCoroutinesApi::class)
object ShizukuPermissions {

private const val REQUEST_CODE = 1

private val _permissionsGranted = MutableSharedFlow<Boolean>(replay = 0)
private lateinit var permissionResultListener: OnRequestPermissionResultListener

fun requestShizukuPermissions() {
if (!Shizuku.pingBinder()) {
GlobalScope.launch { _permissionsGranted.emit(false) }
return
}
if (Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED) {
GlobalScope.launch { _permissionsGranted.emit(true) }
return
}

Shizuku.addRequestPermissionResultListener(permissionResultListener)
Shizuku.requestPermission(REQUEST_CODE)
}

suspend fun waitShizukuPermissions(): Boolean {
requestShizukuPermissions()
return _permissionsGranted.first()
}

init {
permissionResultListener = OnRequestPermissionResultListener { requestCode, grantResult ->
if (requestCode != REQUEST_CODE) return@OnRequestPermissionResultListener

Shizuku.removeRequestPermissionResultListener(permissionResultListener)

GlobalScope.launch {
_permissionsGranted.emit(grantResult == PackageManager.PERMISSION_GRANTED)
}
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,35 +8,23 @@ import androidx.activity.compose.setContent
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.core.app.ActivityCompat
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import androidx.lifecycle.lifecycleScope
import cafe.adriel.voyager.navigator.Navigator
import cafe.adriel.voyager.transitions.SlideTransition
import dev.beefers.vendetta.manager.domain.manager.InstallMethod
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.installer.shizuku.ShizukuPermissions
import dev.beefers.vendetta.manager.ui.screen.installer.InstallerScreen
import dev.beefers.vendetta.manager.ui.screen.main.MainScreen
import dev.beefers.vendetta.manager.ui.theme.VendettaManagerTheme
import dev.beefers.vendetta.manager.utils.DiscordVersion
import dev.beefers.vendetta.manager.utils.Intents
import rikka.shizuku.Shizuku
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject

class MainActivity : ComponentActivity() {

class MainActivity : ComponentActivity(), Shizuku.OnRequestPermissionResultListener {

private val acRequestCode = 1
private val REQUEST_PERMISSION_RESULT_LISTENER = this::onRequestPermissionResult

override fun onRequestPermissionResult(requestCode: Int, grantResult: Int) {
if (grantResult != PackageManager.PERMISSION_GRANTED) {
checkAndRequestPermission()
}
}

private fun checkAndRequestPermission() {
if (Shizuku.pingBinder()) {
Shizuku.addRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
if (Shizuku.checkSelfPermission() != PackageManager.PERMISSION_GRANTED) {
Shizuku.requestPermission(acRequestCode)
}
}
}
private val preferences: PreferenceManager by inject()

@OptIn(ExperimentalAnimationApi::class)
override fun onCreate(savedInstanceState: Bundle?) {
Expand All @@ -52,7 +40,13 @@ class MainActivity : ComponentActivity(), Shizuku.OnRequestPermissionResultListe
)
}

checkAndRequestPermission()
if (preferences.installMethod == InstallMethod.SHIZUKU) {
lifecycleScope.launch {
if (!ShizukuPermissions.waitShizukuPermissions()) {
preferences.installMethod = InstallMethod.DEFAULT
Copy link
Contributor Author

@rushiiMachine rushiiMachine Jan 15, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You should've probably shown a toast here otherwise it's changing a setting without telling the user.
Something like "Failed to obtain Shizuku permissions, reverting to PackageManager"

}
}
}

val screen = if (intent.action == Intents.Actions.INSTALL && version != null) {
InstallerScreen(DiscordVersion.fromVersionCode(version)!!)
Expand All @@ -69,9 +63,4 @@ class MainActivity : ComponentActivity(), Shizuku.OnRequestPermissionResultListe
}
}

override fun onDestroy() {
Shizuku.removeRequestPermissionResultListener(REQUEST_PERMISSION_RESULT_LISTENER)
super.onDestroy()
}

}
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
package dev.beefers.vendetta.manager.ui.screen.settings

import android.content.pm.PackageManager
import android.os.Build
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.rememberScrollState
Expand All @@ -12,7 +11,6 @@ import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
Expand Down Expand Up @@ -41,7 +39,6 @@ import dev.beefers.vendetta.manager.utils.ManagerTab
import dev.beefers.vendetta.manager.utils.TabOptions
import dev.beefers.vendetta.manager.utils.navigate
import org.koin.androidx.compose.get
import rikka.shizuku.Shizuku
import java.io.File

class SettingsScreen : ManagerTab {
Expand All @@ -58,17 +55,6 @@ class SettingsScreen : ManagerTab {
val prefs: PreferenceManager = get()
val installManager: InstallManager = get()
val ctx = LocalContext.current
var shizukuAvailable by remember { mutableStateOf(false) }

LaunchedEffect(Unit) {
val shizukuAlive = Shizuku.pingBinder()
val shizukuPermissionsGranted = if (shizukuAlive) {
Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
} else {
false
}
shizukuAvailable = shizukuAlive && shizukuPermissionsGranted
}

Column(
modifier = Modifier.verticalScroll(rememberScrollState())
Expand Down Expand Up @@ -148,10 +134,7 @@ class SettingsScreen : ManagerTab {
labelFactory = {
ctx.getString(it.labelRes)
},
disabled = !shizukuAvailable,
onPrefChange = {
prefs.installMethod = it
}
onPrefChange = viewModel::setInstallMethod,
)
SettingsSwitch(
label = stringResource(R.string.settings_auto_clear_cache),
Expand Down Expand Up @@ -241,4 +224,5 @@ class SettingsScreen : ManagerTab {
)
}
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,20 @@ import androidx.work.ExistingPeriodicWorkPolicy
import androidx.work.PeriodicWorkRequestBuilder
import androidx.work.WorkManager
import cafe.adriel.voyager.core.model.ScreenModel
import cafe.adriel.voyager.core.model.coroutineScope
import dev.beefers.vendetta.manager.R
import dev.beefers.vendetta.manager.domain.manager.InstallMethod
import dev.beefers.vendetta.manager.domain.manager.PreferenceManager
import dev.beefers.vendetta.manager.domain.manager.UpdateCheckerDuration
import dev.beefers.vendetta.manager.installer.shizuku.ShizukuPermissions
import dev.beefers.vendetta.manager.updatechecker.worker.UpdateWorker
import dev.beefers.vendetta.manager.utils.showToast
import kotlinx.coroutines.launch
import java.io.File

class SettingsViewModel(
private val context: Context
private val context: Context,
private val prefs: PreferenceManager,
) : ScreenModel {
private val cacheDir = context.externalCacheDir ?: File(Environment.getExternalStorageDirectory(), Environment.DIRECTORY_DOWNLOADS).resolve("VendettaManager").also { it.mkdirs() }

Expand All @@ -37,4 +43,18 @@ class SettingsViewModel(
}
}

fun setInstallMethod(method: InstallMethod) {
when (method) {
InstallMethod.SHIZUKU -> coroutineScope.launch {
if (ShizukuPermissions.waitShizukuPermissions()) {
prefs.installMethod = InstallMethod.SHIZUKU
} else {
context.showToast(R.string.msg_shizuku_denied)
}
}

else -> prefs.installMethod = method
}
}

}
3 changes: 2 additions & 1 deletion app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
<string name="msg_downgrade">Cannot downgrade, try uninstalling first</string>
<string name="msg_unlocked">You are now a developer</string>
<string name="msg_permission_grant">In order for Vendetta Manager to function, file permissions are required. Since shared data is stored in ~/Vendetta, permissions are required in order to access it.</string>
<string name="msg_shizuku_denied">Failed to obtain Shizuku permissions</string>
<string name="msg_change_mirror">Would you like to try again using a download mirror?</string>
<string name="msg_invalid_apk">APK was corrupted, try clearing cache then reinstalling</string>

Expand Down Expand Up @@ -139,6 +140,6 @@
<string name="version_target">Target: %1$s</string>
<string name="version_current">Current: %1$s</string>
<string name="install_method">Install method</string>
<string name="default_installer">Default (recommended)</string>
<string name="default_installer">PackageManager (recommended)</string>
<string name="shizuku_installer">Shizuku</string>
</resources>
Loading