Phase 5: PackageInstaller hand-off for verified updates
Browse files
app/src/main/java/io/github/abrarul/modularai/update/Installer.kt
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
| 1 |
+
package io.github.abrarul.modularai.update
|
| 2 |
+
|
| 3 |
+
import android.app.PendingIntent
|
| 4 |
+
import android.content.BroadcastReceiver
|
| 5 |
+
import android.content.Context
|
| 6 |
+
import android.content.Intent
|
| 7 |
+
import android.content.pm.PackageInstaller
|
| 8 |
+
|
| 9 |
+
object Installer {
|
| 10 |
+
/** Streams [apk] into a PackageInstaller session; Android shows its own confirm dialog. */
|
| 11 |
+
fun install(ctx: Context, apk: java.io.File) {
|
| 12 |
+
val pi = ctx.packageManager.packageInstaller
|
| 13 |
+
val id = pi.createSession(PackageInstaller.SessionParams(PackageInstaller.SessionParams.MODE_FULL_INSTALL))
|
| 14 |
+
pi.openSession(id).use { s ->
|
| 15 |
+
s.openWrite("update.apk", 0, apk.length()).use { o ->
|
| 16 |
+
apk.inputStream().use { it.copyTo(o) }
|
| 17 |
+
s.fsync(o)
|
| 18 |
+
}
|
| 19 |
+
val cb = PendingIntent.getBroadcast(
|
| 20 |
+
ctx, id, Intent(ctx, InstallReceiver::class.java).setPackage(ctx.packageName),
|
| 21 |
+
PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
|
| 22 |
+
)
|
| 23 |
+
s.commit(cb.intentSender)
|
| 24 |
+
}
|
| 25 |
+
}
|
| 26 |
+
}
|
| 27 |
+
|
| 28 |
+
class InstallReceiver : BroadcastReceiver() {
|
| 29 |
+
override fun onReceive(ctx: Context, i: Intent) {
|
| 30 |
+
if (i.getIntExtra(PackageInstaller.EXTRA_STATUS, -1) == PackageInstaller.STATUS_PENDING_USER_ACTION) {
|
| 31 |
+
@Suppress("DEPRECATION")
|
| 32 |
+
val confirm = i.getParcelableExtra<Intent>(Intent.EXTRA_INTENT)
|
| 33 |
+
confirm?.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)?.let(ctx::startActivity)
|
| 34 |
+
}
|
| 35 |
+
}
|
| 36 |
+
}
|