From 8f1db51bcb937b23d495c070dc10614a53e0e397 Mon Sep 17 00:00:00 2001 From: renwj Date: Thu, 23 Mar 2023 21:21:09 +0800 Subject: [PATCH] =?UTF-8?q?[2.15.0]=20=E4=BC=98=E5=8C=96=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E7=8A=B6=E6=80=81=E7=9B=91=E6=8E=A7=E9=80=BB=E8=BE=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/main/AndroidManifest.xml | 9 +- .../eagle/core/utilcode/util/ApkInstaller.kt | 241 +++++++++--------- 2 files changed, 128 insertions(+), 122 deletions(-) diff --git a/core/mogo-core-utils/src/main/AndroidManifest.xml b/core/mogo-core-utils/src/main/AndroidManifest.xml index 6f47c81c15..7e643df2d9 100644 --- a/core/mogo-core-utils/src/main/AndroidManifest.xml +++ b/core/mogo-core-utils/src/main/AndroidManifest.xml @@ -37,11 +37,14 @@ - + + - + \ No newline at end of file diff --git a/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/ApkInstaller.kt b/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/ApkInstaller.kt index b4ba64e395..d962921066 100644 --- a/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/ApkInstaller.kt +++ b/core/mogo-core-utils/src/main/java/com/mogo/eagle/core/utilcode/util/ApkInstaller.kt @@ -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 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) - } - } -} \ No newline at end of file +}