Files
assistant-android/app/src/main/java/com/gh/vspace/VArchiveHelper.kt
2024-03-25 08:54:57 +08:00

402 lines
14 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.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteDiskIOException
import android.os.*
import androidx.annotation.WorkerThread
import com.blankj.utilcode.util.LogUtils
import com.gh.download.simple.DownloadMessageHandler
import com.gh.download.simple.SimpleDownloadManager
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.ArchiveEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.db.VArchiveEntity
import com.gh.vspace.db.VGameDatabase
import com.halo.assistant.HaloApp
import com.lg.download.DownloadStatus
import com.lg.download.httpclient.DefaultHttpClient
import com.lg.ndownload.DownloadConfigBuilder
import com.lg.vspace.VirtualAppManager
import com.lg.vspace.archive.data.Const
import com.lg.vspace.archive.ui.ArchiveManager
import com.lg.vspace.archive.ui.interfaces.ArchiveProgressPageListener
import com.lightgame.utils.Utils
import com.lody.virtual.client.core.VirtualCore
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import java.io.File
object VArchiveHelper {
val vArchiveDao by lazy { VGameDatabase.instance.vArchiveDao() }
private var mLatestArchiveFile: File? = null // 最近一个用于保存的存档文件
private var mSaveArchiveListener: ((VArchiveEntity) -> Unit)? = null
private var mApplyArchiveListener: ((String, Boolean) -> Unit)? = null
private var mVArchiveEntityList: ArrayList<VArchiveEntity> = arrayListOf()
private var mVArchiveMd5Set: HashSet<String> = hashSetOf()
private var mLatestDownloadingArchiveEntity: ArchiveEntity? = null // 最近一个正在下载的存档实例
// 本地存档存放的位置
val archivePath by lazy { HaloApp.getInstance().filesDir.absolutePath + File.separator }
private var messenger: Messenger? = null
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
Const.MSG_USE_ARCHIVE -> {
msg.data?.let {
onApplyGameArchiveFinished(
packageName = it.getString(Const.EXTRA_PACKAGE_NAME, ""),
isSuccess = it.getBoolean(Const.EXTRA_IS_SUCCESS, false)
)
}
}
Const.MSG_GENERATE_ARCHIVE -> {
msg.data?.let {
onSaveGameArchiveFinished(
packageName = it.getString(Const.EXTRA_PACKAGE_NAME, ""),
isSuccess = it.getBoolean(Const.EXTRA_IS_SUCCESS, false)
)
}
}
}
messenger = null
}
}
fun init() {
DownloadMessageHandler.registerGlobalStatusChangedListener { simpleDownloadEntity, downloadStatus ->
when (downloadStatus) {
DownloadStatus.COMPLETED -> {
mLatestDownloadingArchiveEntity?.let {
Utils.log(VHelper.LOG_TAG, "新的存档 ${mLatestDownloadingArchiveEntity?.id} 下载完成")
if (it.id == simpleDownloadEntity.id) {
val vArchiveEntity = VArchiveEntity(
id = it.id,
gameId = it.gameId,
descContent = it.desc,
name = it.name,
md5 = it.md5,
type = 1,
filePath = simpleDownloadEntity.dirPath + simpleDownloadEntity.fileName,
gameVersion = it.gameVersion,
time = it.time.update
)
Utils.log(VHelper.LOG_TAG, "新的存档 ${it.id} 保存至数据库中")
runOnIoThread {
vArchiveDao.insert(vArchiveEntity)
refreshArchiveSnapshot()
}
syncDownloadArchive(vArchiveEntity)
}
mLatestDownloadingArchiveEntity = null
}
}
else -> {
// do nothing
}
}
}
runOnIoThread {
refreshArchiveSnapshot()
}
}
/**
* 手动更新存档相关的内存快照
*/
@WorkerThread
fun refreshArchiveSnapshot() {
try {
mVArchiveEntityList = ArrayList(vArchiveDao.getAll())
} catch (e: SQLiteDiskIOException) {
ToastUtils.toast("磁盘出现异常,请稍后再试")
e.printStackTrace()
return
}
// 更新已拥有的存档数据
val archiveMd5Set = hashSetOf<String>()
for (vArchiveEntity in mVArchiveEntityList) {
archiveMd5Set.add(vArchiveEntity.md5)
}
mVArchiveMd5Set = archiveMd5Set
}
fun getVArchiveEntityList(): ArrayList<VArchiveEntity> {
return mVArchiveEntityList
}
@SuppressLint("CheckResult")
private fun syncDownloadArchive(archiveEntity: VArchiveEntity) {
RetrofitManager.getInstance().newApi.syncGameArchive(archiveEntity.gameId, archiveEntity.id)
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<ResponseBody>() {
override fun onSuccess(data: ResponseBody) {
archiveEntity.isLocal = VArchiveEntity.UPLOADED
vArchiveDao.update(archiveEntity)
}
})
}
/**
* 下载新的存档
*/
@SuppressLint("CheckResult")
fun downloadArchive(archive: ArchiveEntity) {
Utils.log(VHelper.LOG_TAG, "下载新的存档 ${archive.id}")
mLatestDownloadingArchiveEntity = archive
// 因为没有下载管理,所以每次下载前都需要清理旧的下载,避免无法下载
SimpleDownloadManager.cancel(archive.id)
SimpleDownloadManager.download(
DownloadConfigBuilder()
.setUniqueId(archive.id)
.setFileName(archive.id + ".zip")
.setUrl(archive.url)
.setPathToStore(archivePath)
.setHttpClient(DefaultHttpClient())
.setDownloadThreadSize(2)
.setDownloadListener(DownloadMessageHandler)
.setDownloadExecutor(AppExecutor.downloadExecutor)
.build()
)
}
/**
* 创建游戏存档
* @param packageName 游戏包名
* @param config 该游戏对应的配置 (不是 url是 config 的内容)
* @param archiveFile 游戏存档需要保存的 file (用 MD5 哈希当前时间戳来当文件名)
*/
fun saveGameArchive(
context: Context,
packageName: String,
config: String,
archiveFile: File,
saveArchiveListener: ((VArchiveEntity) -> Unit)? = null
) {
mSaveArchiveListener = saveArchiveListener
// 记录最近一次创建的存档文件
mLatestArchiveFile = archiveFile
if (VHelper.isLegacyInstalled(packageName)) {
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
context.startActivity(intent)
} else {
if (VirtualCore.get().isRunInExtProcess(packageName)) {
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
prepareExtIntent(intent)
context.startActivity(intent)
} else {
ArchiveManager.generatePackage(context, packageName, config, archiveFile,
object : ArchiveProgressPageListener {
override fun onCancel() {
}
override fun completed(isSuccess: Boolean) {
onSaveGameArchiveFinished(packageName, isSuccess)
}
})
}
}
}
/**
* 使用游戏存档
* @param packageName 游戏包名
* @param archiveFile 游戏存档的 file
*/
fun applyGameArchive(
context: Context,
packageName: String,
config: String,
archiveFile: File,
applyArchiveListener: ((String, Boolean) -> Unit)? = null
) {
mApplyArchiveListener = applyArchiveListener
if (VHelper.isLegacyInstalled(packageName)) {
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
context.startActivity(intent)
} else {
if (VirtualCore.get().isRunInExtProcess(packageName)) {
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
prepareExtIntent(intent)
context.startActivity(intent)
} else {
ArchiveManager.useArchive(context, packageName, archiveFile, config,
object : ArchiveProgressPageListener {
override fun onCancel() {
}
override fun completed(isSuccess: Boolean) {
LogUtils.d("存档使用 completed : $isSuccess")
if (applyArchiveListener != null) {
applyArchiveListener(packageName, isSuccess)
}
}
})
}
}
}
private fun prepareExtIntent(intent: Intent) {
intent.setClassName(
com.lg.vspace.BuildConfig.EXT_PACKAGE_NAME,
"com.lg.vspace.addon.launcher.RemoteGuideActivity"
)
intent.putExtra("callback", Bundle().also {
messenger = Messenger(handler)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
it.putBinder("messenger", messenger!!.binder)
}
})
}
/**
* 创建游戏存档结束回调
* @param packageName 游戏包名
* @param isSuccess 保存存档是否成功
*/
fun onSaveGameArchiveFinished(packageName: String, isSuccess: Boolean) {
if (isSuccess) {
val vGameEntity = VHelper.getVGameSnapshot(packageName = packageName)
runOnIoThread {
val fileMd5 = MD5Utils.calculateMD5(mLatestArchiveFile) ?: return@runOnIoThread
val vArchiveEntity = VArchiveEntity(
id = fileMd5,
gameId = vGameEntity?.downloadEntity?.gameId ?: "",
md5 = fileMd5,
filePath = mLatestArchiveFile?.absolutePath ?: "",
time = System.currentTimeMillis(),
type = 0,
gameVersion = vGameEntity?.downloadEntity?.versionName ?: "",
)
vArchiveDao.insert(vArchiveEntity)
refreshArchiveSnapshot()
runOnUiThread {
mSaveArchiveListener?.invoke(vArchiveEntity)
}
mLatestArchiveFile = null
}
}
}
/**
* 使用游戏存档结束回调
* @param packageName 游戏包名
* @param isSuccess 应用存档是否成功
*/
fun onApplyGameArchiveFinished(packageName: String, isSuccess: Boolean) {
mApplyArchiveListener?.invoke(packageName, isSuccess)
}
/**
* 本地是否已经拥有该 md5 对应的存档
*/
fun isArchiveDownloaded(md5: String): Boolean = mVArchiveMd5Set.contains(md5)
/**
* 根据 md5 获取对应的存档文件 (file)
*/
fun getArchiveFile(md5: String): File? {
for (vArchiveEntity in mVArchiveEntityList) {
if (md5 == vArchiveEntity.md5) {
return File(vArchiveEntity.filePath)
}
}
return null
}
/**
* 根据 md5 获取对应的存档文件路径
*/
fun getArchiveFilePath(md5: String): String {
for (vArchiveEntity in mVArchiveEntityList) {
if (md5 == vArchiveEntity.md5) {
return vArchiveEntity.filePath
}
}
return "";
}
/**
* 根据 md5 删除对应的存档文件
*/
fun deleteArchive(md5: String) {
for (vArchiveEntity in mVArchiveEntityList) {
if (md5 == vArchiveEntity.md5) {
runOnIoThread {
vArchiveDao.delete(vArchiveEntity)
refreshArchiveSnapshot()
tryWithDefaultCatch {
File(vArchiveEntity.filePath).delete()
}
}
break
}
}
}
/**
* 清空存档文件
*/
@WorkerThread
fun clearAllArchives() {
vArchiveDao.clearAllArchives()
}
/**
* 批量更新存档文件
*/
@WorkerThread
fun updateArchives(archiveEntityList: ArrayList<VArchiveEntity>) {
vArchiveDao.update(archiveEntityList)
}
/**
* 设置创建游戏存档成功结果监听
*/
fun setSaveArchiveListener(listener: (VArchiveEntity) -> Unit) {
mSaveArchiveListener = listener
}
/**
* 移除创建游戏存档成功结果监听
*/
fun clearSaveArchiveListener() {
mSaveArchiveListener = null
}
/**
* 设置使用游戏存档成功结果监听
*/
fun setApplyArchiveListener(listener: (String, Boolean) -> Unit) {
mApplyArchiveListener = listener
}
/**
* 移除使用游戏存档成功结果监听
*/
fun clearApplyArchiveListener() {
mApplyArchiveListener = null
}
}