Files
assistant-android/app/src/main/java/com/gh/common/util/DownloadObserver.kt

326 lines
18 KiB
Kotlin
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

package com.gh.common.util
import android.os.Build
import android.preference.PreferenceManager
import com.gh.base.BaseActivity
import com.gh.common.constant.Constants
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.meta.MetaUtil
import com.gh.common.simulator.SimulatorDownloadManager
import com.gh.common.simulator.SimulatorGameManager
import com.gh.common.util.EnergyTaskHelper.postEnergyTask
import com.gh.common.xapk.XapkInstaller
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.R
import com.gh.gamecenter.SuggestionActivity
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.eventbus.EBShowDialog
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.AUTO_INSTALL_SP_KEY
import com.gh.gamecenter.suggest.SuggestType
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
import java.util.*
object DownloadObserver {
private val mApplication = HaloApp.getInstance().application
// 如果在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(HaloApp.getInstance().application)
tryCatchInRelease {
DownloadDataHelper.uploadDownloadEvent(downloadEntity)
}
if (DownloadStatus.hijack == downloadEntity.status) {
// 链接被劫持
processHijack(downloadEntity)
// val nameAndPlatform = (downloadEntity.name + ":"
// + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
// MtaHelper.onEvent("下载劫持",
// "游戏名字", nameAndPlatform,
// "网络状态", DeviceUtils.getNetwork(mApplication))
return
} else if (DownloadStatus.notfound == downloadEntity.status) {
// 404 Not Found
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
Utils.toast(mApplication, "该链接已失效!请联系管理员。")
// MtaHelper.onEventWithBasicDeviceInfo("下载失败弹窗",
// "游戏", downloadEntity.name,
// "平台", downloadEntity.platform)
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)
// MtaHelper.onEventWithBasicDeviceInfo("下载自动暂停",
// "游戏", downloadEntity.name,
// "平台", downloadEntity.platform)
debugOnly {
Utils.log("DownloadObserver", "下载自动暂停->" + downloadEntity.toJson())
}
}
} else if (DownloadStatus.redirected == downloadEntity.status) {
debugOnly { Utils.log("重定向完毕") }
DownloadDataHelper.uploadRedirectEvent(downloadEntity)
}
if (DownloadStatus.done == downloadEntity.status) {
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)
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 -> Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
} 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
val simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
val isInstalled = PackageUtils.isInstalledFromAllPackage(HaloApp.getInstance().application, simulator.apk?.packageName)
if (!isInstalled) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
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 = PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(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)) {
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) {
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误分析",
// "游戏名字", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
//
// MtaHelper.onEventWithBasicDeviceInfo("解析包错误_新",
// "游戏", downloadEntity.name + ":" + PlatformUtils.getInstance(mApplication).getPlatformName(downloadEntity.platform))
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) {
val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity
?: return@showSimulatorParseErrorDialog
DownloadManager.getInstance(currentActivity).cancel(downloadEntity.url, true, true)
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator, SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME)
}
}
}
}
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(mApplication).addObserver(dataWatcher)
}
// 统计下载完成事件
private fun statDoneEvent(downloadEntity: DownloadEntity) {
var type: ExposureUtils.DownloadType
val platform = PlatformUtils.getInstance(HaloApp.getInstance().application).getPlatformName(downloadEntity.platform)
val kv1 = HashMap<String, Any>()
kv1["版本"] = platform
kv1["状态"] = "下载完成"
kv1["用户机型"] = Build.MODEL
kv1["设备JNFJ"] = MetaUtil.getBase64EncodedIMEI()
kv1["网络状态"] = DeviceUtils.getNetwork(HaloApp.getInstance().application)
kv1["光环助手版本"] = BuildConfig.VERSION_NAME
if (downloadEntity.isUpdate) {
type = ExposureUtils.DownloadType.UPDATE
if (downloadEntity.isPlugin) {
type = ExposureUtils.DownloadType.PLUGIN_UPDATE
}
DataUtils.onEvent(mApplication, "游戏更新", downloadEntity.name, kv1)
} else {
type = ExposureUtils.DownloadType.DOWNLOAD
}
// val kv2 = HashMap<String, Any>()
// kv2["版本"] = downloadEntity.platform
// kv2["状态"] = "下载完成"
// kv2["位置"] = downloadEntity.entrance ?: "null"
// kv2["游戏分平台"] = downloadEntity.name + "-" + platform
// kv2["光环助手版本"] = BuildConfig.VERSION_NAME
// DataUtils.onEvent(mApplication, "游戏下载位置", downloadEntity.name, kv2)
if (downloadEntity.isPluggable) {
val kv3 = HashMap<String, Any>()
kv3["下载"] = "下载完成"
kv3["版本"] = downloadEntity.platform
kv3["位置"] = downloadEntity.entrance ?: "null"
type = ExposureUtils.DownloadType.PLUGIN_DOWNLOAD
DataUtils.onEvent(mApplication, "插件化", downloadEntity.name, kv3)
// MtaHelper.onEvent(
// "插件化_新",
// "位置", downloadEntity.entrance,
// "游戏", downloadEntity.name + "-" + downloadEntity.platform,
// "操作", "下载完成",
// "网络状态", DeviceUtils.getNetwork(HaloApp.getInstance().application))
}
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(mApplication).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(mApplication).api.postDownload(body)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(Response())
}
}