Merge branch 'feature-GHZS-2771-patch-3' into 'release'
fix: 支持xapk格式的apks文件解压-0706测试 See merge request halo/android/assistant-android!1193
This commit is contained in:
@ -830,8 +830,9 @@
|
||||
</intent-filter>
|
||||
</receiver>
|
||||
|
||||
<receiver
|
||||
<activity
|
||||
android:name="com.gh.common.xapk.XapkInstallReceiver"
|
||||
android:theme="@style/Theme.Transparent"
|
||||
android:exported="false" />
|
||||
|
||||
<receiver
|
||||
|
||||
@ -4,9 +4,12 @@ import android.app.Activity
|
||||
import android.app.PendingIntent
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageInstaller.SessionCallback
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.content.FileProvider
|
||||
import com.gh.common.dialog.InstallPermissionDialogFragment
|
||||
@ -18,6 +21,7 @@ import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.DialogHelper
|
||||
import com.gh.gamecenter.common.utils.getExtension
|
||||
import com.gh.gamecenter.common.utils.getMetaExtra
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
@ -161,14 +165,13 @@ object PackageInstaller {
|
||||
}
|
||||
|
||||
val flags = if (Build.VERSION.SDK_INT >= 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()
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@ -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>(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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -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<String, DownloadEntity>())
|
||||
|
||||
private val mInstallingPath = Collections.synchronizedList(mutableListOf<String>())
|
||||
|
||||
private val mPendingSessionIdMap = Collections.synchronizedMap(HashMap<String, Int>())
|
||||
private val mPendingSessionInfoMap = HashMap<String, XapkPendingSessionInfo>()
|
||||
|
||||
// 按并行解压
|
||||
@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<XapkPendingSessionInfo>()
|
||||
|
||||
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<String>()
|
||||
|
||||
private val installerList = mutableListOf<IPackageInstaller>()
|
||||
|
||||
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) {
|
||||
|
||||
@ -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
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user