feat: add Shizuku installation support

This commit is contained in:
Aunali321 2023-08-14 20:45:14 +05:30
parent 0520ff23c7
commit 6ca2268a34
9 changed files with 209 additions and 3 deletions

View File

@ -144,4 +144,10 @@ dependencies {
// Markdown to HTML
implementation(libs.markdown)
// 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

@ -60,5 +60,21 @@
android:value="androidx.startup"
tools:node="remove" />
</provider>
<provider
android:name="rikka.shizuku.ShizukuProvider"
android:authorities="app.revanced.manager.shizuku"
android:enabled="true"
android:exported="true"
android:multiprocess="false"
android:permission="android.permission.INTERACT_ACROSS_USERS_FULL" />
<provider
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}.provider"
android:exported="false"
android:grantUriPermissions="true">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths"/>
</provider>
</application>
</manifest>

View File

@ -8,10 +8,12 @@ import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
import app.revanced.manager.service.ShizukuApi
import org.koin.android.ext.koin.androidContext
import org.koin.android.ext.koin.androidLogger
import org.koin.androidx.workmanager.koin.workManagerFactory
import org.koin.core.context.startKoin
lateinit var rvmApp: ManagerApplication
class ManagerApplication : Application() {
private val scope = MainScope()
@ -20,6 +22,9 @@ class ManagerApplication : Application() {
override fun onCreate() {
super.onCreate()
rvmApp = this
ShizukuApi.init()
startKoin {
androidContext(this@ManagerApplication)
androidLogger()

View File

@ -0,0 +1,74 @@
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)
fun init() {
Shizuku.addBinderReceivedListenerSticky {
isBinderAvailable = true
isPermissionGranted = Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
}
Shizuku.addBinderDeadListener {
isBinderAvailable = false
isPermissionGranted = false
}
}
fun installPackage(file: File) {
val intent = Intent(Intent.ACTION_VIEW, FileProvider.getUriForFile(rvmApp, "app.revanced.manager.provider", file)
).apply {
addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
rvmApp.startActivity(intent)
}
}

View File

@ -0,0 +1,94 @@
package app.revanced.manager.ui.component
import android.content.pm.PackageManager
import android.util.Log
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Warning
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp
import app.revanced.manager.R
import app.revanced.manager.service.ShizukuApi
import rikka.shizuku.Shizuku
private val listener: (Int, Int) -> Unit = { _, grantResult ->
ShizukuApi.isPermissionGranted = grantResult == PackageManager.PERMISSION_GRANTED
}
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun ShizukuCard() {
LaunchedEffect(Unit) {
Shizuku.addRequestPermissionResultListener(listener)
}
DisposableEffect(Unit) {
onDispose {
Shizuku.removeRequestPermissionResultListener(listener)
}
}
AnimatedVisibility(visible = !ShizukuApi.isPermissionGranted) {
Card(
colors = CardDefaults.cardColors(
MaterialTheme.colorScheme.errorContainer
),
onClick = {
if (ShizukuApi.isBinderAvailable && !ShizukuApi.isPermissionGranted) {
Log.e("ShizukuCard", "Requesting permission")
Shizuku.requestPermission(114514)
}
},
modifier = Modifier
.fillMaxWidth()
.padding(16.dp)
.clip(RoundedCornerShape(24.dp))
.background(MaterialTheme.colorScheme.tertiaryContainer),
) {
Row(
modifier = Modifier
.fillMaxWidth()
.padding(16.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(16.dp)
) {
Icon(
imageVector = Icons.Default.Warning,
contentDescription = null,
tint = MaterialTheme.colorScheme.onErrorContainer,
modifier = Modifier.size(24.dp)
)
Column() {
Text(
text = stringResource(R.string.shizuku_unavailable),
style = MaterialTheme.typography.titleMedium
)
Text(
text = stringResource(R.string.home_shizuku_warning),
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onErrorContainer
)
}
}
}
}
}

View File

@ -39,6 +39,7 @@ 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
@ -141,7 +142,7 @@ fun DashboardScreen(
)
}
}
ShizukuCard()
HorizontalPager(
pageCount = pages.size,
state = pagerState,

View File

@ -32,6 +32,7 @@ import app.revanced.manager.util.tag
import app.revanced.manager.util.toast
import app.revanced.patcher.logging.Logger
import kotlinx.collections.immutable.ImmutableList
import app.revanced.manager.service.ShizukuApi
import kotlinx.collections.immutable.toImmutableList
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableStateFlow
@ -123,7 +124,7 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
}
}
}
init {
app.registerReceiver(installBroadcastReceiver, IntentFilter().apply {
addAction(InstallService.APP_INSTALL_ACTION)
@ -188,7 +189,9 @@ class InstallerViewModel(input: Destination.Installer) : ViewModel(), KoinCompon
isInstalling = true
try {
if (!signApk()) return@launch
pm.installApp(listOf(signedFile))
//
// pm.installApp(listOf(signedFile))
ShizukuApi.installPackage(signedFile)
} finally {
isInstalling = false
}

View File

@ -230,4 +230,7 @@
<string name="save">Save</string>
<string name="update">Update</string>
<string name="installing_message">Tap on <b>Update</b> when prompted. \n ReVanced Manager will close when updating.</string>
<string name="shizuku_available">Shizuku service available</string>
<string name="shizuku_unavailable">Shizuku service not connected</string>
<string name="home_shizuku_warning">Some functions unavailable</string>
</resources>

View File

@ -0,0 +1,4 @@
<?xml version="1.0" encoding="utf-8"?>
<paths xmlns:android="http://schemas.android.com/apk/res/android">
<cache-path name="cache" path="/"/>
</paths>