mirror of
https://github.com/Aviortheking/Neo-Store.git
synced 2025-04-23 19:32:16 +00:00
Rewrite DefaultInstaller to use PackageInstaller
This commit is contained in:
parent
7bd4785d80
commit
87f19ddc21
@ -9,6 +9,7 @@
|
||||
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.QUERY_ALL_PACKAGES" />
|
||||
<uses-permission android:name="android.permission.UPDATE_PACKAGES_WITHOUT_USER_ACTION" />
|
||||
|
||||
<application
|
||||
android:name="com.looker.droidify.MainApplication"
|
||||
@ -89,6 +90,10 @@
|
||||
|
||||
<receiver android:name="com.looker.droidify.service.DownloadService$Receiver" />
|
||||
|
||||
<service
|
||||
android:name="com.looker.droidify.installer.InstallerService"
|
||||
android:exported="false" />
|
||||
|
||||
<provider
|
||||
android:name="com.looker.droidify.content.Cache$Provider"
|
||||
android:authorities="${applicationId}.provider.cache"
|
||||
|
@ -1,8 +1,9 @@
|
||||
package com.looker.droidify.installer
|
||||
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.content.pm.PackageInstaller.SessionParams
|
||||
import com.looker.droidify.content.Cache
|
||||
import com.looker.droidify.utility.extension.android.Android
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
@ -25,38 +26,48 @@ class DefaultInstaller(context: Context) : BaseInstaller(context) {
|
||||
|
||||
override suspend fun uninstall(packageName: String) = mDefaultUninstaller(packageName)
|
||||
|
||||
private suspend fun mDefaultInstaller(cacheFile: File) {
|
||||
val (uri, flags) = if (Android.sdk(24)) {
|
||||
Pair(
|
||||
Cache.getReleaseUri(context, cacheFile.name),
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
)
|
||||
} else {
|
||||
Pair(Uri.fromFile(cacheFile), 0)
|
||||
private fun mDefaultInstaller(cacheFile: File) {
|
||||
val sessionInstaller = context.packageManager.packageInstaller
|
||||
val sessionParams =
|
||||
SessionParams(SessionParams.MODE_FULL_INSTALL)
|
||||
|
||||
if (Android.sdk(31)) {
|
||||
sessionParams.setRequireUserAction(SessionParams.USER_ACTION_NOT_REQUIRED)
|
||||
}
|
||||
// TODO Handle deprecation
|
||||
@Suppress("DEPRECATION")
|
||||
withContext(Dispatchers.IO) {
|
||||
context.startActivity(
|
||||
Intent(Intent.ACTION_INSTALL_PACKAGE)
|
||||
.setDataAndType(uri, "application/vnd.android.package-archive")
|
||||
.setFlags(flags)
|
||||
)
|
||||
|
||||
val id = sessionInstaller.createSession(sessionParams)
|
||||
|
||||
val session = sessionInstaller.openSession(id)
|
||||
|
||||
session.use { activeSession ->
|
||||
activeSession.openWrite("package", 0, cacheFile.length()).use { packageStream ->
|
||||
cacheFile.inputStream().use { fileStream ->
|
||||
fileStream.copyTo(packageStream)
|
||||
}
|
||||
}
|
||||
|
||||
val intent = Intent(context, InstallerService::class.java)
|
||||
|
||||
val flags = if (Android.sdk(31)) PendingIntent.FLAG_MUTABLE else 0
|
||||
|
||||
val pendingIntent = PendingIntent.getService(context, id, intent, flags)
|
||||
|
||||
session.commit(pendingIntent.intentSender)
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun mDefaultUninstaller(packageName: String) {
|
||||
val uri = Uri.fromParts("package", packageName, null)
|
||||
val intent = Intent()
|
||||
intent.data = uri
|
||||
@Suppress("DEPRECATION")
|
||||
if (Android.sdk(28)) {
|
||||
intent.action = Intent.ACTION_DELETE
|
||||
} else {
|
||||
intent.action = Intent.ACTION_UNINSTALL_PACKAGE
|
||||
intent.putExtra(Intent.EXTRA_RETURN_RESULT, true)
|
||||
}
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
withContext(Dispatchers.IO) { context.startActivity(intent) }
|
||||
val sessionInstaller = context.packageManager.packageInstaller
|
||||
|
||||
val intent = Intent(context, InstallerService::class.java)
|
||||
intent.putExtra(InstallerService.KEY_ACTION, InstallerService.ACTION_UNINSTALL)
|
||||
|
||||
val flags = if (Android.sdk(31)) PendingIntent.FLAG_MUTABLE else 0
|
||||
|
||||
val pendingIntent = PendingIntent.getService(context, -1, intent, flags)
|
||||
|
||||
withContext(Dispatchers.IO) {
|
||||
sessionInstaller.uninstall(packageName, pendingIntent.intentSender)
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,122 @@
|
||||
package com.looker.droidify.installer
|
||||
|
||||
import android.app.Service
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.IBinder
|
||||
import android.view.ContextThemeWrapper
|
||||
import androidx.core.app.NotificationCompat
|
||||
import com.looker.droidify.Common
|
||||
import com.looker.droidify.R
|
||||
import com.looker.droidify.utility.extension.android.notificationManager
|
||||
import com.looker.droidify.utility.extension.resources.getColorFromAttr
|
||||
|
||||
/**
|
||||
* Runs during or after a PackageInstaller session in order to handle completion, failure, or
|
||||
* interruptions requiring user intervention (e.g. "Install Unknown Apps" permission requests).
|
||||
*/
|
||||
class InstallerService : Service() {
|
||||
companion object {
|
||||
const val KEY_ACTION = "installerAction"
|
||||
const val ACTION_UNINSTALL = "uninstall"
|
||||
}
|
||||
|
||||
override fun onStartCommand(intent: Intent, flags: Int, startId: Int): Int {
|
||||
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
|
||||
|
||||
if (status == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
||||
// prompts user to enable unknown source
|
||||
val promptIntent: Intent? = intent.getParcelableExtra(Intent.EXTRA_INTENT)
|
||||
|
||||
promptIntent?.let {
|
||||
it.putExtra(Intent.EXTRA_NOT_UNKNOWN_SOURCE, true)
|
||||
it.putExtra(Intent.EXTRA_INSTALLER_PACKAGE_NAME, "com.android.vending")
|
||||
it.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
|
||||
startActivity(it)
|
||||
}
|
||||
} else {
|
||||
notifyStatus(intent)
|
||||
}
|
||||
|
||||
stopSelf()
|
||||
return START_NOT_STICKY
|
||||
}
|
||||
|
||||
/**
|
||||
* Notifies user of installer outcome.
|
||||
*/
|
||||
private fun notifyStatus(intent: Intent) {
|
||||
// unpack from intent
|
||||
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
|
||||
val name = intent.getStringExtra(PackageInstaller.EXTRA_PACKAGE_NAME)
|
||||
val message = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
||||
val installerAction = intent.getStringExtra(KEY_ACTION)
|
||||
|
||||
// get application name for notifications
|
||||
val appLabel = try {
|
||||
if (name != null) packageManager.getApplicationLabel(
|
||||
packageManager.getApplicationInfo(
|
||||
name,
|
||||
PackageManager.GET_META_DATA
|
||||
)
|
||||
) else null
|
||||
} catch (_: Exception) {
|
||||
null
|
||||
}
|
||||
|
||||
val notificationTag = "download-$name"
|
||||
|
||||
// start building
|
||||
val builder = NotificationCompat
|
||||
.Builder(this, Common.NOTIFICATION_CHANNEL_DOWNLOADING)
|
||||
.setAutoCancel(true)
|
||||
.setColor(
|
||||
ContextThemeWrapper(this, R.style.Theme_Main_Light)
|
||||
.getColorFromAttr(android.R.attr.colorAccent).defaultColor
|
||||
)
|
||||
|
||||
when (status) {
|
||||
PackageInstaller.STATUS_SUCCESS -> {
|
||||
if (installerAction == ACTION_UNINSTALL)
|
||||
// remove any notification for this app
|
||||
notificationManager.cancel(notificationTag, Common.NOTIFICATION_ID_DOWNLOADING)
|
||||
else {
|
||||
val notification = builder
|
||||
.setSmallIcon(android.R.drawable.stat_sys_download_done)
|
||||
.setContentTitle(getString(R.string.installed))
|
||||
.setContentText(appLabel)
|
||||
.build()
|
||||
notificationManager.notify(
|
||||
notificationTag,
|
||||
Common.NOTIFICATION_ID_DOWNLOADING,
|
||||
notification
|
||||
)
|
||||
}
|
||||
}
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> {
|
||||
// do nothing if user cancels
|
||||
}
|
||||
else -> {
|
||||
// problem occurred when installing/uninstalling package
|
||||
val notification = builder
|
||||
.setSmallIcon(android.R.drawable.stat_notify_error)
|
||||
.setContentTitle(getString(R.string.unknown_error_DESC))
|
||||
.setContentText(message)
|
||||
.build()
|
||||
notificationManager.notify(
|
||||
notificationTag,
|
||||
Common.NOTIFICATION_ID_DOWNLOADING,
|
||||
notification
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBind(intent: Intent?): IBinder? {
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user