423 lines
21 KiB
Kotlin
423 lines
21 KiB
Kotlin
package com.gh.common.util
|
||
|
||
import android.preference.PreferenceManager
|
||
import com.gh.common.constant.Config
|
||
import com.gh.common.exposure.ExposureUtils
|
||
import com.gh.common.simulator.SimulatorDownloadManager
|
||
import com.gh.common.simulator.SimulatorGameManager
|
||
import com.gh.common.xapk.XapkInstaller
|
||
import com.gh.download.DownloadDataHelper
|
||
import com.gh.download.DownloadManager
|
||
import com.gh.gamecenter.R
|
||
import com.gh.gamecenter.SuggestionActivity
|
||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||
import com.gh.gamecenter.common.callback.ConfirmListener
|
||
import com.gh.gamecenter.common.constant.Constants
|
||
import com.gh.gamecenter.common.eventbus.EBShowDialog
|
||
import com.gh.gamecenter.common.retrofit.Response
|
||
import com.gh.gamecenter.common.utils.*
|
||
import com.gh.gamecenter.core.utils.*
|
||
import com.gh.gamecenter.energy.EnergyBridge
|
||
import com.gh.gamecenter.entity.GameEntity
|
||
import com.gh.gamecenter.entity.SimpleGameEntity
|
||
import com.gh.gamecenter.entity.SimulatorEntity
|
||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||
import com.gh.gamecenter.setting.view.GameDownloadSettingFragment
|
||
import com.gh.gamecenter.suggest.SuggestType
|
||
import com.gh.vspace.VHelper
|
||
import com.halo.assistant.HaloApp
|
||
import com.lightgame.download.DataWatcher
|
||
import com.lightgame.download.DownloadEntity
|
||
import com.lightgame.download.DownloadStatus
|
||
import com.lightgame.download.FileUtils
|
||
import com.lightgame.utils.AppManager
|
||
import com.lightgame.utils.Utils
|
||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||
import io.reactivex.schedulers.Schedulers
|
||
import okhttp3.MediaType
|
||
import okhttp3.RequestBody
|
||
import org.greenrobot.eventbus.EventBus
|
||
import org.json.JSONObject
|
||
|
||
object DownloadObserver {
|
||
|
||
private val mApplication = HaloApp.getInstance().application
|
||
|
||
// 简单 debounce 因为内存更新 downloadEntity 对象造成的触发双重下载完成事件
|
||
// TODO 修复因为更改内存对象造成的双重下载完成事件问题,具体触发代码见 DownloadDao.updateSnapshotList
|
||
private var mDoneDebouncePair: Pair<String, Long>? = null
|
||
|
||
private const val TEA_EVENT_DOWNLOAD_COMPLETE = "game_addiction"
|
||
|
||
// 如果在WIFI状态下,下载自动暂停,则再重试一遍
|
||
@JvmStatic
|
||
fun initObserver() {
|
||
val dataWatcher = object : DataWatcher() {
|
||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||
// todo 如何处理xapk安装问题
|
||
val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
|
||
if (!xapkStatus.isNullOrEmpty()) return
|
||
|
||
val gameId = downloadEntity.gameId
|
||
val downloadManager = DownloadManager.getInstance()
|
||
|
||
tryCatchInRelease {
|
||
DownloadDataHelper.uploadDownloadEvent(downloadEntity)
|
||
}
|
||
|
||
if (DownloadStatus.hijack == downloadEntity.status) {
|
||
// 链接被劫持
|
||
processHijack(downloadEntity)
|
||
return
|
||
} else if (DownloadStatus.notfound == downloadEntity.status) {
|
||
// 404 Not Found
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
downloadManager.cancel(downloadEntity.url)
|
||
Utils.toast(mApplication, "该链接已失效!请联系管理员。")
|
||
|
||
val currentActivity = AppManager.getInstance().currentActivity() ?: return
|
||
|
||
DialogHelper.showDialog(currentActivity, "下载失败", "下载链接已失效,建议提交反馈", "立即反馈", "取消", {
|
||
SuggestionActivity.startSuggestionActivity(
|
||
currentActivity,
|
||
SuggestType.gameQuestion, "notfound",
|
||
StringUtils.buildString(downloadEntity.name, ",问题反馈:下载链接失效"),
|
||
SimpleGameEntity(gameId, downloadEntity.name, "")
|
||
)
|
||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||
return
|
||
} else if (DownloadStatus.neterror == downloadEntity.status || DownloadStatus.timeout == downloadEntity.status) {
|
||
if (downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD].isNullOrEmpty()
|
||
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)
|
||
) {
|
||
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = downloadEntity.progress.toString()
|
||
downloadManager.updateDownloadEntity(downloadEntity)
|
||
downloadManager.resumeDownload(downloadEntity.url)
|
||
debugOnly {
|
||
Utils.log("DownloadObserver", "下载重试->" + downloadEntity.toJson())
|
||
}
|
||
} else {
|
||
Utils.toast(mApplication, "网络不稳定,下载任务已暂停")
|
||
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
|
||
|
||
debugOnly {
|
||
Utils.log("DownloadObserver", "下载自动暂停->" + downloadEntity.toJson())
|
||
}
|
||
}
|
||
} else if (DownloadStatus.redirected == downloadEntity.status) {
|
||
debugOnly { Utils.log("重定向完毕") }
|
||
DownloadDataHelper.uploadRedirectEvent(downloadEntity)
|
||
} else if (DownloadStatus.unqualified == downloadEntity.status) {
|
||
// 未成年
|
||
RealNameHelper.showRealNameUnqualifiedDialog(downloadEntity)
|
||
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
downloadManager.cancel(downloadEntity.url)
|
||
} else if (DownloadStatus.unavailable == downloadEntity.status) {
|
||
// 未接入防沉迷系统
|
||
val currentActivity = AppManager.getInstance().currentActivity()
|
||
|
||
if (currentActivity != null) {
|
||
DialogHelper.showDialog(
|
||
context = currentActivity,
|
||
title = "温馨提示",
|
||
content = "该游戏未接入防沉迷系统,暂不支持下载",
|
||
confirmText = "知道了",
|
||
cancelText = ""
|
||
)
|
||
} else {
|
||
ToastUtils.toast("该游戏未接入防沉迷系统,暂不支持下载")
|
||
}
|
||
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
downloadManager.cancel(downloadEntity.url)
|
||
} else if (DownloadStatus.uncertificated == downloadEntity.status) {
|
||
// 未实名
|
||
RealNameHelper.showRealNameUncertificatedDialog(downloadEntity)
|
||
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
downloadManager.cancel(downloadEntity.url)
|
||
} else if (DownloadStatus.banned == downloadEntity.status) {
|
||
ToastUtils.showToast("网络异常")
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
downloadManager.cancel(downloadEntity.url)
|
||
}
|
||
|
||
if (DownloadStatus.done == downloadEntity.status) {
|
||
if (mDoneDebouncePair?.first != downloadEntity.url) {
|
||
mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis())
|
||
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
|
||
} else {
|
||
if (mDoneDebouncePair?.second == 0L
|
||
|| System.currentTimeMillis() - (mDoneDebouncePair?.second ?: 0) > 500
|
||
) {
|
||
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
|
||
}
|
||
}
|
||
}
|
||
|
||
if (downloadEntity.status == DownloadStatus.done) {
|
||
EventBus.getDefault().post(EBDownloadStatus("done", "", "", "", downloadEntity.packageName, ""))
|
||
}
|
||
|
||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||
|
||
// 如果已下载大小发生变化,表示成功恢复下载,则重置重试标记
|
||
if (downloadEntity.status == DownloadStatus.downloading &&
|
||
downloadEntity.progress.toString() != downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD]
|
||
) {
|
||
downloadEntity.meta[Constants.MARK_RETRY_DOWNLOAD] = ""
|
||
downloadManager.updateDownloadEntity(downloadEntity)
|
||
}
|
||
}
|
||
}
|
||
|
||
// 添加观察者
|
||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||
|
||
}
|
||
|
||
private fun performDownloadCompleteAction(
|
||
downloadEntity: DownloadEntity,
|
||
gameId: String,
|
||
downloadManager: DownloadManager
|
||
) {
|
||
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
|
||
statDoneEvent(downloadEntity)
|
||
MtaHelper.onEvent("软件更新", "下载完成")
|
||
// 会有 ActivityNotFoundException 异常,catch 掉不管了
|
||
tryWithDefaultCatch {
|
||
if (Constants.SILENT_UPDATE != downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)) {
|
||
// TODO 在 Android 11 上没有授权安装未知应用的权限前第一次调用这个方法系统会杀掉我们的进程...
|
||
// 没能找到类似的解释,最接近的是这个 https://issuetracker.google.com/issues/154157387,但也只是点授权杀进程而已
|
||
PackageInstaller.install(mApplication, downloadEntity);
|
||
DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据
|
||
}
|
||
}
|
||
} else {
|
||
statDoneEvent(downloadEntity)
|
||
|
||
if (!SPUtils.getBoolean(TEA_EVENT_DOWNLOAD_COMPLETE)) {
|
||
HaloApp.getInstance().flavorProvider.logEvent(TEA_EVENT_DOWNLOAD_COMPLETE)
|
||
SPUtils.setBoolean(TEA_EVENT_DOWNLOAD_COMPLETE, true)
|
||
}
|
||
|
||
GameActivityDownloadHelper.clear()
|
||
|
||
EnergyBridge.postEnergyTask("download_game", downloadEntity.gameId, downloadEntity.packageName)
|
||
val platform = PlatformUtils.getInstance(mApplication)
|
||
.getPlatformName(downloadEntity.platform)
|
||
if (platform != null) {
|
||
when {
|
||
// TODO 插件化传 path 从 apk 中获取 packageName 的形式有可能出现拿不到 packageName 的情况
|
||
downloadEntity.isPluggable -> // 弹出插件化提示框
|
||
EventBus.getDefault().post(
|
||
EBShowDialog(
|
||
BaseActivity.PLUGGABLE,
|
||
downloadEntity.path
|
||
)
|
||
)
|
||
downloadEntity.isPlugin -> Utils.toast(
|
||
mApplication,
|
||
downloadEntity.name + " - " + platform + " - 下载完成"
|
||
)
|
||
else -> {
|
||
if (downloadEntity.isVGame()) {
|
||
VHelper.showFloatingWindow(downloadEntity.packageName)
|
||
} else {
|
||
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
|
||
}
|
||
}
|
||
}
|
||
} else {
|
||
if (downloadEntity.isVGame()) {
|
||
VHelper.showFloatingWindow(downloadEntity.packageName)
|
||
} else {
|
||
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
|
||
}
|
||
}
|
||
if (!downloadEntity.isPluggable) {
|
||
if (downloadEntity.isSimulatorGame()) {
|
||
val simulatorJson = downloadEntity.getMetaExtra(Constants.SIMULATOR)
|
||
val gameName = downloadEntity.getMetaExtra(Constants.GAME_NAME)
|
||
if (simulatorJson.isEmpty()) return
|
||
var simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
|
||
val isInstalled = PackageUtils.isInstalledFromAllPackage(
|
||
HaloApp.getInstance().application,
|
||
simulator.apk?.packageName
|
||
)
|
||
val isInstalledNewSimulator =
|
||
SimulatorGameManager.isNewSimulatorInstalled(HaloApp.getInstance().application)
|
||
val isInstalledOldSimulator =
|
||
SimulatorGameManager.isOldSimulatorInstalled(HaloApp.getInstance().application)
|
||
// if (!isInstalled && !isInstalledNewSimulator) {
|
||
val currentActivity = AppManager.getInstance().currentActivity()
|
||
?: return
|
||
val newSimulator = Config.getNewSimulatorEntitySetting()
|
||
if ((!isInstalledOldSimulator && newSimulator != null && newSimulator.active) || isInstalledNewSimulator) { //如果没有安装任一旧的模拟器 或者下载了新模拟器 则使用新版本模拟器
|
||
simulator = newSimulator ?: simulator
|
||
}
|
||
SimulatorDownloadManager.getInstance().showDownloadDialog(
|
||
currentActivity, simulator,
|
||
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null
|
||
)
|
||
// }
|
||
SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type)
|
||
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
|
||
} else {
|
||
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
|
||
// 是否是自动安装
|
||
val isAutoInstall = SPUtils.getBoolean(GameDownloadSettingFragment.AUTO_INSTALL_SP_KEY, true)
|
||
if (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall) {
|
||
if (FileUtils.isEmptyFile(downloadEntity.path)) {
|
||
Utils.toast(mApplication, R.string.install_failure_hint)
|
||
downloadManager.cancel(downloadEntity.url)
|
||
} else {
|
||
if (PackageUtils.isCanLaunchSetup(
|
||
mApplication,
|
||
downloadEntity.path
|
||
) || downloadType == Constants.SMOOTH_GAME
|
||
) {
|
||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||
tryWithDefaultCatch {
|
||
PackageInstaller.install(mApplication, downloadEntity, false)
|
||
}
|
||
} else {
|
||
// 弹出卸载提示框
|
||
if (downloadEntity.isPlugin) {
|
||
EventBus.getDefault().post(
|
||
EBShowDialog(
|
||
BaseActivity.PLUGGABLE,
|
||
downloadEntity.path
|
||
)
|
||
)
|
||
} else {
|
||
EventBus.getDefault().post(
|
||
EBShowDialog(
|
||
BaseActivity.SIGNATURE_CONFLICT,
|
||
downloadEntity.path
|
||
)
|
||
)
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
// 统计下载完成
|
||
uploadData(gameId, downloadEntity.platform)
|
||
}
|
||
|
||
// 下载过程分析统计
|
||
|
||
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
|
||
// 这里为了让它能用就不判断是否解析包错误了
|
||
if (PackageUtils.isDeviceUnableToHandleBigApkFile(downloadEntity.path)) {
|
||
return
|
||
}
|
||
|
||
val pm = mApplication.packageManager
|
||
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
|
||
if (packageInfo == null) {
|
||
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
|
||
if (downloadType == Constants.SIMULATOR_DOWNLOAD) {
|
||
val currentActivity = AppManager.getInstance().currentActivity()
|
||
?: return
|
||
DialogUtils.showSimulatorParseErrorDialog(
|
||
currentActivity,
|
||
downloadEntity.gameId,
|
||
downloadEntity.name,
|
||
object : ConfirmListener {
|
||
override fun onConfirm() {
|
||
val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity
|
||
?: return
|
||
DownloadManager.getInstance().cancel(downloadEntity.url, true, true, false)
|
||
SimulatorDownloadManager.getInstance()
|
||
.showDownloadDialog(
|
||
currentActivity,
|
||
simulator,
|
||
SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME
|
||
)
|
||
}
|
||
})
|
||
}
|
||
}
|
||
}
|
||
|
||
// 统计下载完成事件
|
||
private fun statDoneEvent(downloadEntity: DownloadEntity) {
|
||
var type: ExposureUtils.DownloadType
|
||
if (downloadEntity.isUpdate) {
|
||
if (downloadEntity.isVGame()) {
|
||
type = ExposureUtils.DownloadType.FUN_UPDATE
|
||
} else {
|
||
type = ExposureUtils.DownloadType.UPDATE
|
||
if (downloadEntity.isPlugin) {
|
||
type = ExposureUtils.DownloadType.PLUGIN_UPDATE
|
||
}
|
||
}
|
||
} else {
|
||
type = if (downloadEntity.isVGame()) {
|
||
ExposureUtils.DownloadType.FUN_DOWNLOAD
|
||
} else {
|
||
ExposureUtils.DownloadType.DOWNLOAD
|
||
}
|
||
}
|
||
|
||
if (downloadEntity.isPluggable) {
|
||
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
|
||
}
|
||
val isPlatformRecommend =
|
||
java.lang.Boolean.parseBoolean(downloadEntity.getMetaExtra(Constants.IS_PLATFORM_RECOMMEND))
|
||
ExposureUtils.logADownloadCompleteExposureEvent(
|
||
GameEntity(
|
||
id = downloadEntity.gameId,
|
||
mName = downloadEntity.name.removeSuffix(Constants.GAME_NAME_DECORATOR),
|
||
gameVersion = downloadEntity.versionName ?: "",
|
||
isPlatformRecommend = isPlatformRecommend
|
||
),
|
||
downloadEntity.platform,
|
||
downloadEntity.exposureTrace,
|
||
downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown",
|
||
downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown",
|
||
type
|
||
)
|
||
|
||
DataCollectionUtils.uploadDownload(mApplication, downloadEntity, "完成")
|
||
}
|
||
|
||
private fun processHijack(downloadEntity: DownloadEntity) {
|
||
// 删除任务
|
||
downloadEntity.status = DownloadStatus.cancel
|
||
DownloadManager.getInstance().cancel(downloadEntity.url)
|
||
// 弹出提示框
|
||
EventBus.getDefault().post(EBShowDialog(BaseActivity.DOWNLOAD_HIJACK))
|
||
// 记录链接被劫持
|
||
DataCollectionUtils.uploadHijack(mApplication, downloadEntity)
|
||
// 上传劫持log
|
||
DataLogUtils.uploadHijack(mApplication, downloadEntity)
|
||
}
|
||
|
||
// 统计下载
|
||
private fun uploadData(id: String, platform: String?) {
|
||
val params = HashMap<String, String>()
|
||
params["game"] = id
|
||
params["platform"] = platform ?: ""
|
||
val body = RequestBody.create(
|
||
MediaType.parse("application/json"),
|
||
JSONObject(params as Map<*, *>).toString()
|
||
)
|
||
RetrofitManager.getInstance().api.postDownload(body)
|
||
.subscribeOn(Schedulers.io())
|
||
.observeOn(AndroidSchedulers.mainThread())
|
||
.subscribe(Response())
|
||
}
|
||
|
||
} |