implement nonroot uninstallation

This commit is contained in:
X1nto 2021-11-23 18:28:04 +04:00
parent 19f8219c3c
commit 7141df8af8
7 changed files with 107 additions and 40 deletions

View File

@ -41,9 +41,8 @@
android:theme="@style/Theme.MaterialComponents.NoActionBar"
android:label="@string/app_name"/>
<service android:name="com.xinto.apkhelper.services.PackageManagerService" />
<service android:name=".core.installer.service.AppInstallService" />
<service android:name=".core.installer.service.AppUninstallService" />
</application>

View File

@ -12,7 +12,9 @@ class AppInstallService : Service() {
flags: Int,
startId: Int
): Int {
when (val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
val extraStatus = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)
val extraStatusMessage = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
when (extraStatus) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
startActivity(
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT).apply {
@ -22,12 +24,9 @@ class AppInstallService : Service() {
}
else -> {
sendBroadcast(Intent().apply {
action = APP_INSTALL_STATUS
putExtra(EXTRA_INSTALL_STATUS, status)
putExtra(
EXTRA_INSTALL_EXTRA,
intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
)
action = APP_INSTALL_ACTION
putExtra(EXTRA_INSTALL_STATUS, extraStatus)
putExtra(EXTRA_INSTALL_EXTRA, extraStatusMessage)
})
}
}
@ -38,7 +37,7 @@ class AppInstallService : Service() {
override fun onBind(intent: Intent?): IBinder? = null
companion object {
const val APP_INSTALL_STATUS = "APP_INSTALL_STATUS"
const val APP_INSTALL_ACTION = "APP_INSTALL_ACTION"
const val EXTRA_INSTALL_STATUS = "EXTRA_INSTALL_STATUS"
const val EXTRA_INSTALL_EXTRA = "EXTRA_INSTALL_EXTRA"

View File

@ -0,0 +1,39 @@
package com.vanced.manager.core.installer.service
import android.app.Service
import android.content.Intent
import android.content.pm.PackageInstaller
import android.os.IBinder
class AppUninstallService : Service() {
override fun onStartCommand(
intent: Intent,
flags: Int,
startId: Int
): Int {
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
startActivity(
intent.getParcelableExtra<Intent>(Intent.EXTRA_INTENT).apply {
this?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
}
)
}
else -> {
sendBroadcast(Intent().apply {
action = APP_UNINSTALL_ACTION
})
}
}
stopSelf()
return START_NOT_STICKY
}
override fun onBind(intent: Intent?): IBinder? = null
companion object {
const val APP_UNINSTALL_ACTION = "APP_UNINSTALL_ACTION"
}
}

View File

@ -7,6 +7,7 @@ import android.content.pm.PackageInstaller
import android.content.pm.PackageManager
import android.os.Build
import com.vanced.manager.core.installer.service.AppInstallService
import com.vanced.manager.core.installer.service.AppUninstallService
import java.io.File
import java.io.FileInputStream
@ -17,7 +18,7 @@ fun installApp(apk: File, context: Context) {
val session =
packageInstaller.openSession(packageInstaller.createSession(sessionParams))
writeApkToSession(apk, session)
session.commit(getIntentSender(context))
session.commit(context.installIntentSender)
session.close()
}
@ -28,10 +29,28 @@ fun installSplitApp(apks: Array<File>, context: Context) {
for (apk in apks) {
writeApkToSession(apk, session)
}
session.commit(getIntentSender(context))
session.commit(context.installIntentSender)
session.close()
}
fun uninstallPackage(pkg: String, context: Context) {
val packageInstaller = context.packageManager.packageInstaller
packageInstaller.uninstall(pkg, context.uninstallIntentSender)
}
private fun writeApkToSession(
apk: File,
session: PackageInstaller.Session
) {
val inputStream = FileInputStream(apk)
val outputStream = session.openWrite(apk.name, 0, apk.length())
inputStream.copyTo(outputStream, byteArraySize)
session.fsync(outputStream)
inputStream.close()
outputStream.flush()
outputStream.close()
}
private val intentFlags
get() = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S)
PendingIntent.FLAG_MUTABLE
@ -47,27 +66,18 @@ private val sessionParams
}
}
private fun getIntentSender(context: Context) =
PendingIntent.getService(
context,
private val Context.installIntentSender
get() = PendingIntent.getService(
this,
0,
Intent(context, AppInstallService::class.java),
Intent(this, AppInstallService::class.java),
intentFlags
).intentSender
private fun writeApkToSession(
apk: File,
session: PackageInstaller.Session
) {
val inputStream = FileInputStream(apk)
val outputStream = session.openWrite(apk.name, 0, apk.length())
val buffer = ByteArray(byteArraySize)
var length: Int
while (inputStream.read(buffer).also { length = it } > 0) {
outputStream.write(buffer, 0, length)
}
session.fsync(outputStream)
inputStream.close()
outputStream.flush()
outputStream.close()
}
private val Context.uninstallIntentSender
get() = PendingIntent.getService(
this,
0,
Intent(this, AppUninstallService::class.java),
intentFlags
).intentSender

View File

@ -17,6 +17,7 @@ import com.github.zsoltk.compose.backpress.LocalBackPressHandler
import com.github.zsoltk.compose.router.Router
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.vanced.manager.core.installer.service.AppInstallService
import com.vanced.manager.core.installer.service.AppUninstallService
import com.vanced.manager.ui.component.color.managerSurfaceColor
import com.vanced.manager.ui.screens.*
import com.vanced.manager.ui.theme.ManagerTheme
@ -36,12 +37,18 @@ class MainActivity : ComponentActivity() {
private val installBroadcastReceiver = object : BroadcastReceiver() {
override fun onReceive(context: Context?, intent: Intent?) {
if (intent?.action != AppInstallService.APP_INSTALL_STATUS) return
installViewModel.postInstallStatus(
pmStatus = intent.getIntExtra(AppInstallService.EXTRA_INSTALL_STATUS, -999),
extra = intent.getStringExtra(AppInstallService.EXTRA_INSTALL_EXTRA)!!,
)
when (intent?.action) {
AppInstallService.APP_INSTALL_ACTION -> {
installViewModel.postInstallStatus(
pmStatus = intent.getIntExtra(AppInstallService.EXTRA_INSTALL_STATUS, -999),
extra = intent.getStringExtra(AppInstallService.EXTRA_INSTALL_EXTRA)!!,
)
mainViewModel.fetch()
}
AppUninstallService.APP_UNINSTALL_ACTION -> {
mainViewModel.fetch()
}
}
}
}
@ -151,7 +158,8 @@ class MainActivity : ComponentActivity() {
registerReceiver(
installBroadcastReceiver,
IntentFilter().apply {
addAction(AppInstallService.APP_INSTALL_STATUS)
addAction(AppInstallService.APP_INSTALL_ACTION)
addAction(AppUninstallService.APP_UNINSTALL_ACTION)
}
)
}

View File

@ -136,7 +136,11 @@ fun HomeLayout(
app.installationOptions
)
},
onAppUninstallClick = { /*TODO*/ },
onAppUninstallClick = {
viewModel.uninstallApp(
appPackage = app.packageName
)
},
onAppLaunchClick = {
viewModel.launchApp(
appName = app.name,

View File

@ -10,6 +10,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.viewModelScope
import com.vanced.manager.core.installer.util.uninstallPackage
import com.vanced.manager.core.preferences.holder.managerVariantPref
import com.vanced.manager.core.preferences.holder.musicEnabled
import com.vanced.manager.core.preferences.holder.vancedEnabled
@ -96,6 +97,13 @@ class MainViewModel(
}
}
//TODO implement root uninstallation
fun uninstallApp(
appPackage: String,
) {
uninstallPackage(appPackage, app)
}
init {
fetch()
}