diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index ca02cb9529..609e51e83a 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -830,8 +830,9 @@
-
= Build.VERSION_CODES.S) {
- PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT
+ PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_CANCEL_CURRENT
} else {
- PendingIntent.FLAG_UPDATE_CURRENT
+ PendingIntent.FLAG_CANCEL_CURRENT
}
- val pendingIntent = PendingIntent.getBroadcast(context, 0, intent, flags)
+ val pendingIntent = PendingIntent.getActivity(context, sessionId, intent, flags)
// 提交数据流并执行安装
session.commit(pendingIntent.intentSender)
- session.close()
}
/**
diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt b/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt
index a9cea6e637..2f90ebb282 100644
--- a/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt
+++ b/app/src/main/java/com/gh/common/xapk/XapkInstallReceiver.kt
@@ -1,31 +1,35 @@
package com.gh.common.xapk
-import android.content.BroadcastReceiver
+import android.app.Activity
import android.content.Context
import android.content.Intent
import android.content.pm.PackageInstaller
-import android.content.pm.PackageInstaller.SessionInfo
import android.os.Build
+import android.os.Bundle
import androidx.annotation.RequiresApi
@RequiresApi(Build.VERSION_CODES.LOLLIPOP)
-class XapkInstallReceiver : BroadcastReceiver() {
+class XapkInstallReceiver : Activity() {
companion object {
const val KEY_PACKAGE_PATH = "package_path"
}
- override fun onReceive(context: Context, intent: Intent) {
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ overridePendingTransition(0, 0)
+ handleIntent(this, intent)
+
+ }
+
+ private fun handleIntent(context: Context, intent: Intent) {
when (intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -999)) {
PackageInstaller.STATUS_PENDING_USER_ACTION -> {
val installIntent = intent.getParcelableExtra(Intent.EXTRA_INTENT)
if (installIntent != null) {
- val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH)
- val installSessionId = intent.getIntExtra(PackageInstaller.EXTRA_SESSION_ID, -1)
- XapkInstaller.onPendingUserAction(installPackagePath!!, installSessionId)
- installIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
- installIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TASK)
context.startActivity(installIntent)
+ updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_PENDING_USER_ACTION)
+ finish()
}
}
PackageInstaller.STATUS_FAILURE,
@@ -35,11 +39,21 @@ class XapkInstallReceiver : BroadcastReceiver() {
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE,
PackageInstaller.STATUS_FAILURE_INVALID,
PackageInstaller.STATUS_FAILURE_STORAGE -> {
- val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH)
- if (!installPackagePath.isNullOrEmpty()) {
- XapkInstaller.onInstallCanceled(installPackagePath)
- }
+ updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_INSTALL_CANCELED)
+ finish()
+ }
+ else -> {
+ updatePendingSessionInfoStatus(intent, XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS)
+ finish()
}
}
}
+
+ private fun updatePendingSessionInfoStatus(intent: Intent, status: Int) {
+ val installPackagePath = intent.getStringExtra(KEY_PACKAGE_PATH)
+ if (!installPackagePath.isNullOrEmpty()) {
+ val pendingSessionInfo = XapkInstaller.getPendingSessionInfo(installPackagePath)
+ pendingSessionInfo?.updateStatus(status)
+ }
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt
index 149a015421..256eafa937 100644
--- a/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt
+++ b/app/src/main/java/com/gh/common/xapk/XapkInstaller.kt
@@ -3,7 +3,6 @@ package com.gh.common.xapk
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
-import android.util.Log
import com.gh.common.util.*
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
@@ -61,9 +60,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
private val mDownloadEntityMap = Collections.synchronizedMap(HashMap())
- private val mInstallingPath = Collections.synchronizedList(mutableListOf())
-
- private val mPendingSessionIdMap = Collections.synchronizedMap(HashMap())
+ private val mPendingSessionInfoMap = HashMap()
// 按并行解压
@JvmStatic
@@ -127,16 +124,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
Utils.log("unzip", "onSuccess->${downloadEntity.path}")
}
- if (!mInstallingPath.contains(downloadEntity.path)) {
- mInstallingPath.add(downloadEntity.path)
- }
-
- AppExecutor.ioExecutor.execute {
- NDataChanger.notifyDataChanged(downloadEntity)
- }
-
- XapkInstallerLooper.add(downloadEntity.path, installer)
- XapkInstallerLooper.install(mContext)
+ installer.install(mContext)
}
override fun onError(apk: XApkFile, exception: Throwable) {
@@ -180,11 +168,9 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
}
}
- fun isInstalling(packagePath: String): Boolean = mInstallingPath.contains(packagePath)
+ fun getPendingSessionInfo(packagePath: String): XapkPendingSessionInfo? = mPendingSessionInfoMap[packagePath]
- fun onPendingUserAction(packagePath: String, sessionId: Int) {
- mPendingSessionIdMap[packagePath] = sessionId
- }
+ fun isInstalling(packagePath: String): Boolean = mPendingSessionInfoMap.containsKey(packagePath)
/**
* 通知XAPK安装完成
@@ -192,15 +178,11 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
fun onInstalled(packagePath: String) {
val downloadEntity = mDownloadEntityMap.remove(packagePath) ?: return
- mPendingSessionIdMap.remove(packagePath)
- mInstallingPath.remove(packagePath)
+ mPendingSessionInfoMap.remove(packagePath)
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "100.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.INSTALLED.name
- XapkInstallerLooper.remove(downloadEntity.path)
- XapkInstallerLooper.install(mContext)
-
AppExecutor.ioExecutor.execute {
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
@@ -212,16 +194,11 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
val downloadEntity = mDownloadEntityMap.remove(packagePath) ?: return
- mPendingSessionIdMap.remove(packagePath)
-
- mInstallingPath.remove(packagePath)
+ mPendingSessionInfoMap.remove(packagePath)
downloadEntity.meta[XAPK_UNZIP_PERCENT] = "0.0"
downloadEntity.meta[XAPK_UNZIP_STATUS] = XapkUnzipStatus.CANCEL.name
- XapkInstallerLooper.remove(downloadEntity.path)
- XapkInstallerLooper.install(mContext)
-
AppExecutor.ioExecutor.execute {
NDataChanger.notifyDataChanged(downloadEntity)
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
@@ -234,31 +211,46 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
* 1. 用户触碰安装弹窗(原生Android系统)区域外导致安装取消
*/
fun updateCurrentInstallStatus() {
- if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || mPendingSessionIdMap.isEmpty()) {
+ if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP || mPendingSessionInfoMap.isEmpty()) {
return
}
- val installer = mContext.packageManager.packageInstaller
+ val updateList = mutableListOf()
- for (pendingSessionEntry in mPendingSessionIdMap) {
- val sessionInfo = installer.getSessionInfo(pendingSessionEntry.value)
+ for (pendingSessionInfoEntry in mPendingSessionInfoMap) {
- // 1. 用户点击了取消按钮时,sessionInfo为空
- // 2. 用户点击了确定按钮时,sessionInfo的progress大于0.8
- if (sessionInfo == null || sessionInfo.progress > 0.8F) {
- continue
+ val pendingSessionInfo = pendingSessionInfoEntry.value
+
+ if (pendingSessionInfo.getStatus() == XapkPendingSessionInfo.STATUS_PENDING_USER_ACTION) {// 用户触摸安装弹窗外部区域取消安装后,更新安装状态
+ val installer = mContext.packageManager.packageInstaller
+ val sessionId = pendingSessionInfo.sessionId
+ if (sessionId != -1) {
+ val sessionInfo = installer.getSessionInfo(sessionId)
+ // 表示用户点击了安装弹窗外部区域
+ if (sessionInfo != null && sessionInfo.progress <= 0.8F) {
+ AppExecutor.ioExecutor.execute {
+ installer.abandonSession(sessionInfo.sessionId)
+ }
+ pendingSessionInfo.updateStatus(XapkPendingSessionInfo.STATUS_INSTALL_CANCELED)
+ }
+ }
}
- AppExecutor.ioExecutor.execute {
- installer.abandonSession(sessionInfo.sessionId)
+ val installStatus = pendingSessionInfo.getStatus()
+ if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_CANCELED
+ || installStatus == XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS
+ ) {
+ updateList.add(pendingSessionInfo)
}
+ }
- val downloadEntity = mDownloadEntityMap[pendingSessionEntry.key] ?: return
-
- if (!PackageUtils.isInstalled(mContext, downloadEntity.packageName)) {
- onInstallCanceled(downloadEntity.path)
- } else {
+ for (pendingSessionInfo in updateList) {
+ val downloadEntity = mDownloadEntityMap[pendingSessionInfo.path] ?: continue
+ val installStatus = pendingSessionInfo.getStatus()
+ if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_SUCCESS) {
onInstalled(downloadEntity.path)
+ } else if (installStatus == XapkPendingSessionInfo.STATUS_INSTALL_CANCELED) {
+ onInstallCanceled(downloadEntity.path)
}
}
}
@@ -284,10 +276,13 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
private val xApkFile: XApkFile,
private val sessionId: Int,
) : IPackageInstaller {
+
override fun install(context: Context) {
val downloadEntity = mDownloadEntityMap[xApkFile.file.path] ?: return
val applicationContext = context.applicationContext
+ mPendingSessionInfoMap[downloadEntity.path] = XapkPendingSessionInfo(downloadEntity.path, sessionId)
AppExecutor.ioExecutor.execute {// 有可能卡顿造成anr
+ NDataChanger.notifyDataChanged(downloadEntity)
PackageInstaller.installMultiple(applicationContext, downloadEntity.path, sessionId)
}
}
@@ -297,6 +292,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
private val xApkFile: XApkFile,
private val file: File
) : IPackageInstaller {
+
override fun install(context: Context) {
val downloadEntity = mDownloadEntityMap[xApkFile.file.path] ?: return
PackageInstaller.install(
@@ -307,42 +303,6 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
)
}
}
-
- /**
- * XAPK安装每次只能执行一个安装流程,因此添加一个安装队列
- */
- private object XapkInstallerLooper {
-
- private val packagePathList = mutableListOf()
-
- private val installerList = mutableListOf()
-
- fun add(packagePath: String, installer: IPackageInstaller) {
- if (packagePathList.contains(packagePath)) {
- return
- }
- packagePathList.add(packagePath)
- installerList.add(installer)
- }
-
- fun install(context: Context) {
- if (installerList.isEmpty()) {
- return
- }
- installerList.first().install(context)
- }
-
- fun remove(packagePath: String) {
- if (!packagePathList.contains(packagePath)) {
- return
- }
- val index = packagePathList.indexOf(packagePath)
- if (index != -1) {
- packagePathList.removeAt(index)
- installerList.removeAt(index)
- }
- }
- }
}
enum class XapkUnzipStatus(status: String) {
diff --git a/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt b/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt
new file mode 100644
index 0000000000..5815d9a0a6
--- /dev/null
+++ b/app/src/main/java/com/gh/common/xapk/XapkPendingSessionInfo.kt
@@ -0,0 +1,21 @@
+package com.gh.common.xapk
+
+class XapkPendingSessionInfo(
+ val path: String,
+ val sessionId: Int = -1
+) {
+ companion object {
+ const val STATUS_INIT = -1
+ const val STATUS_PENDING_USER_ACTION = 0
+ const val STATUS_INSTALL_SUCCESS = 1
+ const val STATUS_INSTALL_CANCELED = 2
+ }
+
+ private var status = STATUS_INIT
+
+ internal fun updateStatus(status: Int) {
+ this.status = status
+ }
+
+ fun getStatus(): Int = status
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/download/PackageObserver.kt b/app/src/main/java/com/gh/download/PackageObserver.kt
index 5be59056d2..6a1fbf05fc 100644
--- a/app/src/main/java/com/gh/download/PackageObserver.kt
+++ b/app/src/main/java/com/gh/download/PackageObserver.kt
@@ -111,8 +111,7 @@ object PackageObserver {
runOnIoThread { FileUtils.deleteFile(mDownloadEntity.path) }
}
- if (mDownloadEntity.format == Constants.XAPK_FORMAT
- || mDownloadEntity.format == Constants.XAPK_APKS_FORMAT) {
+ if (mDownloadEntity.format == Constants.XAPK_FORMAT) {
XapkInstaller.onInstalled(mDownloadEntity.path)
}