[2.15.0] 优化安装状态监控逻辑
This commit is contained in:
@@ -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>
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user