[2.15.0] 优化安装状态监控逻辑

This commit is contained in:
renwj
2023-03-23 21:21:09 +08:00
parent 95296ab54c
commit 8f1db51bcb
2 changed files with 128 additions and 122 deletions

View File

@@ -37,11 +37,14 @@
</provider>
<receiver android:name=".NetworkUtils$NetworkChangedReceiver" />
<receiver android:name=".AppInstallReceiver"
android:exported="true">
<activity android:name=".InstallApkSessionApi"
android:exported="true"
android:theme="@style/ActivityTranslucent"
android:launchMode="singleTask">
<intent-filter>
<action android:name="com.mogo.launcher.f.action.SESSION_API_PACKAGE_INSTALLED" />
</intent-filter>
</receiver>
</activity>
</application>
</manifest>

View File

@@ -1,32 +1,20 @@
package com.mogo.eagle.core.utilcode.util
import android.annotation.*
import android.app.PendingIntent
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentSender
import android.content.pm.PackageInstaller
import android.content.pm.PackageInstaller.Session
import android.content.pm.PackageInstaller.SessionCallback
import android.content.pm.PackageInstaller.SessionParams
import android.app.*
import android.content.*
import android.content.pm.PackageInstaller.*
import android.os.*
import android.util.Log
import androidx.lifecycle.ProcessLifecycleOwner
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.*
import java.io.File
import java.io.FileInputStream
import java.io.InputStream
import java.lang.ref.WeakReference
import java.util.concurrent.ConcurrentHashMap
import android.util.*
import android.widget.*
import androidx.appcompat.app.*
import com.mogo.eagle.core.utilcode.util.ApkInstaller.Companion.INSTALL_CODE_INVALID
import java.io.*
class ApkInstaller {
companion object {
private const val TAG = "ApkInstaller"
const val INSTALL_CODE_INVALID = -2
@JvmStatic
@@ -46,118 +34,133 @@ class ApkInstaller {
block?.invoke(INSTALL_CODE_INVALID, msg)
return
}
installApp(context, FileInputStream(apkFile), block)
}
@JvmStatic
fun installApp(context: Context, input: InputStream, block: ((Int, String) -> Unit)?) {
ProcessLifecycleOwner.get().lifecycleScope.launch(Dispatchers.IO) {
var session: Session? = null
try {
val installer = context.packageManager.packageInstaller
installer.registerSessionCallback(object : SessionCallback() {
override fun onCreated(sessionId: Int) {
Log.d(TAG, "onCreate: sessionId -> $sessionId")
}
override fun onBadgingChanged(sessionId: Int) {
Log.d(TAG, "onBadgingChanged: sessionId -> $sessionId")
}
override fun onActiveChanged(sessionId: Int, active: Boolean) {
Log.d(TAG, "onActiveChanged: sessionId -> $sessionId, active: $active")
}
override fun onProgressChanged(sessionId: Int, progress: Float) {
Log.d(TAG, "onProgressChanged: sessionId -> $sessionId, process: $progress")
}
override fun onFinished(sessionId: Int, success: Boolean) {
Log.d(TAG, "onFinished: sessionId -> $sessionId, success: $success")
}
}, Handler(Looper.getMainLooper()))
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
session = installer.openSession(sessionId)
val output = session.openWrite("install.apk", 0, -1)
output.use { o ->
input.copyTo(o)
}
withContext(Dispatchers.Main) {
session.commit(createIntent(context, sessionId))
}
block?.also {
AppInstallReceiver.listeners[sessionId] = WeakReference(it)
}
} catch (t: Throwable) {
block?.invoke(-2, t.message ?: "未知异常")
session?.close()
}
}
}
@SuppressLint("UnspecifiedImmutableFlag")
private fun createIntent(context: Context, sessionId: Int): IntentSender {
val intent = Intent(context, AppInstallReceiver::class.java)
intent.action = AppInstallReceiver.INTENT_ACTION_SESSION_INSTALL
intent.putExtra(AppInstallReceiver.INTENT_EXTRA_SESSION_ID, sessionId)
return PendingIntent.getBroadcast(context, sessionId, intent, PendingIntent.FLAG_UPDATE_CURRENT).intentSender
val intent = Intent(context, InstallApkSessionApi::class.java)
intent.putExtra("APK_FILE_PATH", apkFile.absolutePath)
block?.also { InstallApkSessionApi.listener = it }
context.startActivity(intent)
}
}
}
class AppInstallReceiver: BroadcastReceiver() {
class InstallApkSessionApi: AppCompatActivity() {
companion object {
const val TAG = "AppInstallReceiver"
const val INTENT_ACTION_SESSION_INSTALL = "com.mogo.launcher.f.action.SESSION_API_PACKAGE_INSTALLED"
const val INTENT_EXTRA_SESSION_ID = "intent.extra.session_id"
val listeners by lazy { ConcurrentHashMap<Int, WeakReference<((Int, String) -> Unit)>>() }
@Volatile
var listener: ((Int, String) -> Unit)? = null
}
override fun onReceive(context: Context?, intent: Intent?) {
val c = context ?: return
val i = intent ?: return
val a = i.action ?: return
if (a == INTENT_ACTION_SESSION_INSTALL) {
val e = i.extras ?: return
val sessionId = e.getInt(INTENT_EXTRA_SESSION_ID, 0)
Log.d(TAG, "action -> $a, sessionId: $sessionId")
when(val status = e.getInt(PackageInstaller.EXTRA_STATUS)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val confirm = e.get(Intent.EXTRA_INTENT) as? Intent
if (confirm != null) {
Log.d(TAG, "confirm -> status:$status, sessionID: $sessionId")
val activityInfo = c.packageManager.resolveActivity(confirm, 0)?.activityInfo
if (activityInfo != null) {
c.startActivity(confirm)
} else {
notifyListeners(status, "action: ${confirm.action} 不存在")
}
} else {
notifyListeners(status, "需要用户确认的应用程序不存在")
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
Log.d(TAG, "--- InstallApkSessionApi -- onCreate --")
val apkPath = intent.extras?.getString("APK_FILE_PATH")
if (apkPath.isNullOrEmpty()) {
Log.e(TAG, "--- InstallApkSessionApi --:apk path is null or empty.")
finish()
return
}
val file = File(apkPath)
if (!file.exists()) {
Log.e(TAG, "--- InstallApkSessionApi --:apk is not exist.")
finish()
return
}
var session: Session? = null
val input = FileInputStream(file)
try {
val installer = packageManager.packageInstaller
installer.registerSessionCallback(object : SessionCallback() {
override fun onCreated(sessionId: Int) {
Log.d(TAG, "onCreate: sessionId -> $sessionId")
}
PackageInstaller.STATUS_FAILURE_ABORTED,
PackageInstaller.STATUS_FAILURE,
PackageInstaller.STATUS_FAILURE_BLOCKED,
PackageInstaller.STATUS_FAILURE_CONFLICT,
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
PackageInstaller.STATUS_FAILURE_INVALID,
PackageInstaller.STATUS_FAILURE_STORAGE -> {
Log.d(TAG, "failed -> status:$status, sessionID: $sessionId")
notifyListeners(status, "安装失败: $status")
override fun onBadgingChanged(sessionId: Int) {
Log.d(TAG, "onBadgingChanged: sessionId -> $sessionId")
}
override fun onActiveChanged(sessionId: Int, active: Boolean) {
Log.d(TAG, "onActiveChanged: sessionId -> $sessionId, active: $active")
}
override fun onProgressChanged(sessionId: Int, progress: Float) {
Log.d(TAG, "onProgressChanged: sessionId -> $sessionId, process: $progress")
}
override fun onFinished(sessionId: Int, success: Boolean) {
Log.d(TAG, "onFinished: sessionId -> $sessionId, success: $success")
}
}, Handler(Looper.getMainLooper()))
val params = SessionParams(SessionParams.MODE_FULL_INSTALL)
val sessionId = installer.createSession(params)
session = installer.openSession(sessionId)
val output = session.openWrite("install.apk", 0, -1)
output.use { o ->
input.copyTo(o)
}
val intent = Intent(this, InstallApkSessionApi::class.java)
intent.action = INTENT_ACTION_SESSION_INSTALL
val pendingIntent = PendingIntent.getActivity(this, 0, intent, 0)
session.commit(pendingIntent.intentSender)
} catch (t: IOException) {
Log.e(TAG, "--- InstallApkSessionApi --:exception 1:${t.message}")
listener?.invoke(INSTALL_CODE_INVALID, t.message ?: "未知异常")
session?.close()
} catch (t: RuntimeException) {
Log.e(TAG, "--- InstallApkSessionApi --:exception 2:${t.message}")
listener?.invoke(INSTALL_CODE_INVALID, t.message ?: "未知异常2")
session?.abandon()
} finally {
try {
input.close()
} catch (t: Throwable) {
t.printStackTrace()
}
}
}
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
Log.i(TAG, "--- InstallApkSessionApi -- onNewIntent --")
val i = intent ?: return
val extras = i.extras
if (INTENT_ACTION_SESSION_INSTALL == i.action) {
Log.i(TAG, "--- InstallApkSessionApi -- action: ${ i.action } --")
val status = extras?.getInt(EXTRA_STATUS)
Log.i(TAG, "--- InstallApkSessionApi -- action: ${ i.action } - status: $status --")
val message = extras?.getString(EXTRA_STATUS_MESSAGE)
when (status) {
STATUS_PENDING_USER_ACTION -> {
Log.i(TAG, "--- InstallApkSessionApi -- confirm --")
// This test app isn't privileged, so the user has to confirm the install.
val confirmIntent = extras[Intent.EXTRA_INTENT] as? Intent
startActivity(confirmIntent)
}
STATUS_SUCCESS -> {
Log.i(TAG, "--- InstallApkSessionApi -- install success --")
Toast.makeText(this, "Install succeeded!", Toast.LENGTH_SHORT).show()
listener?.invoke(STATUS_SUCCESS, "安装成功")
finish()
listener = null
}
STATUS_FAILURE,
STATUS_FAILURE_ABORTED,
STATUS_FAILURE_BLOCKED,
STATUS_FAILURE_CONFLICT,
STATUS_FAILURE_INCOMPATIBLE,
STATUS_FAILURE_INVALID,
STATUS_FAILURE_STORAGE -> {
Toast.makeText(this, "Install failed! $status, $message", Toast.LENGTH_SHORT).show()
listener?.invoke(status, "安装失败")
finish()
listener = null
}
else -> {
Log.d(TAG, "other status:$status, sessionID: $sessionId")
notifyListeners(status, "未知状态")
status?.also { listener?.invoke(it, "未知状态") }
Toast.makeText(this, "Unrecognized status received from installer: $status", Toast.LENGTH_SHORT).show()
finish()
listener = null
}
}
}
}
private fun notifyListeners(code: Int, reason: String) {
listeners.values.forEach {
it.get()?.invoke(code, reason)
}
}
}
}