Compare commits

...

90 Commits

Author SHA1 Message Date
eaa7cef7f6 fix: 最近在玩来源判断被覆盖的问题。 2024-06-28 17:00:05 +08:00
af65230ac4 chore: 一体化升级版本到5.35.6 2024-06-28 09:35:37 +08:00
25f44c3a3e refactor: sync vasdk 2024-06-28 09:33:00 +08:00
fa3955b193 feat: 畅玩添加神策事件 https://jira.shanqu.cc/browse/GHZSCY-5829 2024-06-28 09:31:33 +08:00
6f9c6eae60 feat: game_install_fail 新增code字段上报
https://jira.shanqu.cc/browse/GHZSCY-5801
2024-06-27 18:07:27 +08:00
0f4c06974d fix: 部分用户可能的数据不一致导致首次安装字段上报错误。https://jira.shanqu.cc/browse/GHZSCY-5838 2024-06-27 17:33:21 +08:00
5a583d202d feat: 启动畅玩事件添加页面来源字段 2024-06-27 15:02:44 +08:00
a47f6e3c39 refactor: sync vasdk 2024-06-21 13:59:19 +08:00
196a9cfd22 fix: 1.云存档使用上传判断错误。2.安装失败toast优化。 2024-06-19 11:29:51 +08:00
5e1a234435 chore: upgrade va version 2024-06-19 11:29:51 +08:00
745edfef64 fix: 部分游戏由于attachBaseContext多次调用导致的闪退 [https://jira.shanqu.cc/browse/GHZSCY-5615] 2024-06-19 11:29:51 +08:00
c02535524f fix: 游戏主进程才上报va_progress_began 和 va_progress_complete [https://jira.shanqu.cc/browse/GHZSCY-5613] 2024-06-19 11:29:51 +08:00
a7a2397916 fix: 非主进程过滤remote_server_failed类型的crash type [https://jira.shanqu.cc/browse/GHZSCY-5624] 2024-06-19 11:29:51 +08:00
07c54383f5 feat: 上报冷启动游戏进程失败https://jira.shanqu.cc/browse/GHZSCY-5718 2024-06-19 11:29:51 +08:00
937c483751 fix:细分安装失败的原因,修复安装失败能启动游戏 2024-06-19 11:29:51 +08:00
5554f6b0cd fix: 修复32位畅玩游戏助手被卸载的云存档交互崩溃 https://sentry.shanqu.cc/organizations/lightgame/issues/355607/?project=22 2024-06-19 11:29:51 +08:00
b222c67c1f fix:去掉英文 2024-06-19 11:29:51 +08:00
51cdce6210 fix: remove启动服务导致的崩溃,由于包渠道不会触发SensorLogService的业务逻辑,先不启动。 2024-06-19 11:29:51 +08:00
141ca256a3 fix: remove启动服务导致的崩溃,由于包渠道不会触发SensorLogService的业务逻辑,先不启动。 2024-06-19 11:29:51 +08:00
58c6b40f0c fix: 获取网络状态报错 & 资源覆盖导致深色主题UI异常 2024-06-19 11:29:51 +08:00
255099f00d fix:史宾格检测隐私政策前读取了系统属性 2024-06-19 11:29:51 +08:00
230d6175e1 refactor: sync id with 光环助手5.35.5 2024-06-19 11:29:51 +08:00
a64b7cba62 refactor: sync id with 光环助手5.35.5 2024-06-19 11:29:51 +08:00
cc84ce8d07 refactor: sync vasdk 2024-06-19 11:29:51 +08:00
694fc7f71e refactor: 取消assumenosideeffects注释 & 修改版本号 & update git submodule 2024-06-19 11:29:51 +08:00
251f05895d fix: 上传云存档弹窗时间1970年的问题 2024-06-19 11:29:51 +08:00
17047ebaa6 refactor: 删除插件文件,自动打包会下载 2024-06-19 11:29:51 +08:00
75c03fe868 refactor: sync va commit id & upload plugin files. 2024-06-19 11:29:51 +08:00
1145b85d2a refactor: sync commit id. 2024-06-19 11:29:50 +08:00
e20de2c245 refactor: 插件删除实名部分,实名在va实现 2024-06-19 11:29:50 +08:00
d2566a42ef refactor: upgrade va version 2024-06-19 11:29:50 +08:00
4bc8e072a4 feat: merge va 2024-06-19 11:29:50 +08:00
71efe3b69a Merge branch 'hotfix/v5.35.5-1055/pack-info-error' into 'release'
fix: 特殊包判断的异常问题

See merge request halo/android/assistant-android!1710
2024-06-17 16:07:54 +08:00
b8cbbc0301 fix: 特殊包判断的异常问题 2024-06-17 16:07:02 +08:00
1c04e966d9 Merge branch 'hotfix/v5.35.5-1055/download_dialog_crash' into 'release'
fix: 捕抓下载弹窗自动触发下载时的数组越界异常 https://sentry.shanqu.cc/organizations/lightgame/issues/365000/

See merge request halo/android/assistant-android!1709
2024-06-17 10:50:20 +08:00
ba0ad10f61 fix: 捕抓下载弹窗自动触发下载时的数组越界异常 https://sentry.shanqu.cc/organizations/lightgame/issues/365000/ 2024-06-17 10:49:47 +08:00
fd450f0d67 Merge branch 'hotfix/v5.35.5-1055/sentry_crashes' into 'release'
fix: 捕抓下载时获取回调 header 时的异常 https://sentry.shanqu.cc/organizations/lightgame/issues/371082

See merge request halo/android/assistant-android!1707
2024-06-14 16:31:47 +08:00
57984d160f fix: 捕抓下载时获取回调 header 时的异常 https://sentry.shanqu.cc/organizations/lightgame/issues/371082
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-06-14 15:08:00 +08:00
e19c091532 Merge branch 'hotfix/v5.35.5-1055/filter_crash' into 'release'
fix: 修复游戏屏蔽的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/380904/

See merge request halo/android/assistant-android!1706
2024-06-14 11:32:04 +08:00
5db6b3511f fix: 修复游戏屏蔽的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/380904/
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-06-14 10:44:29 +08:00
4a2f0896bb Merge branch 'hotfix/v5.35.5-1055/crashes' into 'release'
fix: 修复 5.35.5 线上闪退

See merge request halo/android/assistant-android!1693
2024-06-05 15:00:34 +08:00
51ccc532d5 fix: 修复安装数据上报闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/365934
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-06-05 14:48:54 +08:00
1bf977f3e4 fix: 修复安利墙点赞闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/379887/
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-06-05 14:48:54 +08:00
54ee8a9c69 chore: 版本更新至 5.35.5 2024-05-16 11:36:57 +08:00
2b44efd6b3 Merge branch 'hotfix/v5.35.4-1054/wrong_install_status' into 'release'
fix: 修复安装游戏回到光环时的安装状态更新问题

See merge request halo/android/assistant-android!1673
2024-05-16 11:32:46 +08:00
d62c8beb30 fix: 修复安装游戏回到光环时的安装状态更新问题 2024-05-16 11:22:04 +08:00
d46aa81dbe chore: 版本更新至 5.35.4 2024-05-15 16:30:39 +08:00
51c0bf27cf Merge branch 'hotfix/v5.35.3-1053/wrong_update_status' into 'release'
fix: 修复使用跳转落地页下载安装的游戏更新时下载按钮的更新问题;修复安装同包名同版本不同游戏 id 的游戏时下载按钮的更新问题

See merge request halo/android/assistant-android!1670
2024-05-15 16:29:48 +08:00
06b2d2b416 Merge branch 'hotfix/v5.35.3-1053/packagename_null_crash' into 'release'
fix: 修复部分位置跳转安装因为包名为空而产生的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/385367

See merge request halo/android/assistant-android!1671
2024-05-15 16:00:11 +08:00
af7580a6a6 fix: 修复部分位置跳转安装因为包名为空而产生的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/385367 2024-05-15 15:57:37 +08:00
1e4375ec8a fix: 修复使用跳转落地页下载安装的游戏更新时下载按钮的更新问题;修复安装同包名同版本不同游戏 id 的游戏时下载按钮的更新问题 2024-05-15 15:31:41 +08:00
196e719358 chore: 版本更新至 5.35.3 2024-05-14 17:13:10 +08:00
dd051b4d13 Merge branch 'hotfix/v5.35.2-1052/logout_crash' into 'release'
fix: 修复退出登录出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/385028/?project=22

See merge request halo/android/assistant-android!1665
2024-05-14 09:45:26 +08:00
e7f756555c fix: 修复退出登录出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/385028/?project=22 2024-05-14 09:39:57 +08:00
b8c4f1403b chore: 版本更新至 5.35.2 2024-05-10 11:18:37 +08:00
b88698c2a3 Merge branch 'hotfix/v5.35.1-1051/crashes' into 'release'
fix: 修复 5.35.1 线上闪退问题

See merge request halo/android/assistant-android!1662
2024-05-10 11:17:13 +08:00
c897d5ad0f fix: 修复多线程更新已安装列表时的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/384726 https://sentry.shanqu.cc/organizations/lightgame/issues/384682
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-10 10:53:41 +08:00
037f453a75 fix: 修复小米设备安装 APKS 格式 XAPK 包时候的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/384710
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-10 10:51:38 +08:00
ad3a3c1341 Merge branch 'hotfix/v5.35.1-1051/hot_launch_splash_ad' into 'release'
fix: 修复移除第三方广告SDK后错误展示热启动广告的问题

See merge request halo/android/assistant-android!1661
2024-05-09 17:45:06 +08:00
ba320f7740 fix: 修复移除第三方广告SDK后错误展示热启动广告的问题 2024-05-09 17:19:33 +08:00
f3dbc0b779 Merge branch 'hotfix/v5.35.1-1051/update_notify_issue' into 'release'
fix: 修复更新应用时遗漏模拟卸载监听导致的状态变更异常问题

See merge request halo/android/assistant-android!1660
2024-05-09 15:40:05 +08:00
0c518ac40e fix: 修复更新应用时遗漏模拟卸载监听导致的状态变更异常问题 2024-05-09 15:37:45 +08:00
bf57118900 Merge branch 'hotfix/v5.35.1-1051/init_crash' into 'release'
fix: 修复部分 downloadEntity versionName 而导致的启动闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/384612

See merge request halo/android/assistant-android!1659
2024-05-09 13:42:04 +08:00
b80a14f2b1 fix: 修复部分 downloadEntity versionName 而导致的启动闪退问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-09 11:48:43 +08:00
1450064640 chore: 版本更新至 5.35.1 2024-05-09 10:26:59 +08:00
90e19d5099 Merge branch 'feat/GHZSCY-5342' into 'release'
feat: 关于移除监听广播后的相关优化 https://jira.shanqu.cc/browse/GHZSCY-5342

See merge request halo/android/assistant-android!1657
2024-05-09 10:25:59 +08:00
383124dc36 feat: 关于移除监听广播后的相关优化(处理错误注释) https://jira.shanqu.cc/browse/GHZSCY-5342
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-09 10:21:28 +08:00
6d6ce2613a feat: 关于移除监听广播后的相关优化 https://jira.shanqu.cc/browse/GHZSCY-5342 2024-05-09 09:21:34 +08:00
8569264b82 feat: 合规调整-已安装列表权限-2024/4/22 https://jira.shanqu.cc/browse/GHZSCY-5250 2024-05-09 09:21:34 +08:00
59667abf09 feat: 合规调整-2024/4/30-后台静默监听行为 https://jira.shanqu.cc/browse/GHZSCY-5331 2024-05-09 09:21:34 +08:00
b651ef8617 Merge branch 'hotfix/v5.35.0-1050/GHZSCY-5329' into 'release'
fix: 优化 gid 的获取逻辑,避免部分接口请求时因为 gid 为空供导致异常 https://jira.shanqu.cc/browse/GHZSCY-5329

See merge request halo/android/assistant-android!1654
2024-05-08 11:14:46 +08:00
76e17eddd7 fix: 优化 gid 的获取逻辑,避免部分接口请求时因为 gid 为空供导致异常 https://jira.shanqu.cc/browse/GHZSCY-5329 2024-05-08 10:03:02 +08:00
f01e08aec9 Merge branch 'hotfix/v5.35.0-1050/crashes' into 'release'
fix: 修复线上闪退问题

See merge request halo/android/assistant-android!1652
2024-05-07 11:02:11 +08:00
faddf5d7b6 fix: 收到卸载广播时移除调用 MutableCollections.removeAll() 方法引起的数组越界异常 (MutableCollections.removeAll()线程不安全) https://sentry.shanqu.cc/organizations/lightgame/issues/242447
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-07 10:38:55 +08:00
81cf2f0ddc fix: 部分镜像游戏开关切换时由于不同接口更新不及时导致的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/378659
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-07 09:56:29 +08:00
198561d15a fix: 收到卸载广播时移除调用 MutableCollections.removeAll() 方法引起的数组越界异常 (MutableCollections.removeAll()线程不安全) https://sentry.shanqu.cc/organizations/lightgame/issues/242447
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-07 09:42:44 +08:00
1e721b699c fix: 修复存储空间不足时进行数据库删除操作引起的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/381076
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-05-06 17:58:50 +08:00
8f48dfd347 Merge branch 'fix-community_browsing_duration_error' into 'release'
fix: 使用ViewPager2导致CommunityBrowsingDuration埋点上报异常的问题

See merge request halo/android/assistant-android!1651
2024-04-30 15:04:50 +08:00
bc811a2882 fix: 使用ViewPager2导致CommunityBrowsingDuration埋点上报异常的问题 2024-04-30 14:56:42 +08:00
bfd986fdfd Merge remote-tracking branch 'origin/release' into dev 2024-04-28 10:59:46 +08:00
a2bd5e01e0 Merge branch 'hotfix/v5.35.0-1050/jpush_clear_badge' into 'release'
fix: 修复极光推送清除角标提前获取信息的问题

See merge request halo/android/assistant-android!1649
2024-04-28 10:43:11 +08:00
844d227f19 fix: 修复极光推送清除角标提前获取信息的问题 2024-04-28 10:26:20 +08:00
8b1f92e9c4 Merge branch 'chen/202404/GHZSCY-5293' into 'dev'
fix:【光环助手】新游开测右上角显示 https://jira.shanqu.cc/browse/GHZSCY-5293

See merge request halo/android/assistant-android!1648
2024-04-28 10:12:25 +08:00
f5834d440b fix:【光环助手】新游开测右上角显示 https://jira.shanqu.cc/browse/GHZSCY-5293 2024-04-28 10:09:20 +08:00
f982bf6478 Merge branch 'hotfix/v5.35.0-1050/disable_jpush_auto_init' into 'release'
fix: 移除极光推送通过 ContentProvider 自动初始化的问题

See merge request halo/android/assistant-android!1647
2024-04-26 14:01:09 +08:00
33d7afec71 fix: 移除极光推送通过 ContentProvider 自动初始化相关代码 2024-04-26 13:40:16 +08:00
37ef50f323 Merge branch 'feat/GHZSCY-5243' into 'dev'
feat: 穿山甲广告SDK更换新版本与信息流广告优化 https://jira.shanqu.cc/browse/GHZSCY-5243

See merge request halo/android/assistant-android!1641
2024-04-26 10:10:33 +08:00
4c6acdee3a Merge branch 'fix/GHZSCY-5261' into 'dev'
fix: 【光环助手】开屏广告神策埋点数据上报问题 https://jira.shanqu.cc/browse/GHZSCY-5261

See merge request halo/android/assistant-android!1646
2024-04-25 10:36:50 +08:00
6beb060e63 fix: 【光环助手】开屏广告神策埋点数据上报问题 https://jira.shanqu.cc/browse/GHZSCY-5261 2024-04-25 10:31:22 +08:00
d11ccba0b7 feat: 穿山甲广告SDK更换新版本与信息流广告优化 https://jira.shanqu.cc/browse/GHZSCY-5243
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-04-22 16:29:42 +08:00
115 changed files with 4021 additions and 1027 deletions

4
.gitignore vendored
View File

@ -9,4 +9,6 @@ build/
release-app/
test-app/
scripts/apk-channel/
app/src/test/java/com/gh/gamecenter
app/src/test/java/com/gh/gamecenter
app/src/main/assets-debug/
app/src/main/assets-release/

3
.gitmodules vendored
View File

@ -8,3 +8,6 @@
[submodule "ndownload"]
path = ndownload
url = ../../../android/ndownload.git
[submodule "vasdk"]
path = vasdk
url = ../../../sdg/android/vasdk.git

View File

@ -75,7 +75,7 @@ android {
versionName rootProject.ext.versionName
applicationId rootProject.ext.applicationId
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt', 'proguard-fresco.txt', rootProject.ext.va_proguard_rules
String CORE_EVENT_GAME_CATEGORY = ""
@ -164,6 +164,15 @@ android {
flavorDimensions("env", "region")
sourceSets {
debug {
assets.srcDirs += 'src/main/assets-debug'
}
release {
assets.srcDirs += 'src/main/assets-release'
}
publish {
java.srcDirs = ['src/main/java', "src/default/java"]
}
@ -368,7 +377,7 @@ dependencies {
kapt "com.alibaba:arouter-compiler:$arouterVersion"
implementation project(':ndownload')
implementation project(':vspace-bridge:vspace')
implementation project(':vspace-bridge')
implementation (project(':module_common')) {
exclude group: 'androidx.swiperefreshlayout'
@ -401,7 +410,7 @@ dependencies {
implementation(project(':feature:pkg'))
implementation(project(':feature:oaid'))
implementation(project(':feature:floating-window'))
implementation(project(':feature:csj_ad'))
// implementation(project(':feature:csj_ad'))
// implementation(project(':feature:beizi_startup_ad'))
implementation(project(':feature:xapk-installer'))
implementation(project(':feature:qq_game')) {
@ -409,12 +418,24 @@ dependencies {
}
internalImplementation(project(':module_internal_test'))
// 根据BUILD_PUSH_TYPE决定使用哪个推送SDK目前默认使用阿里云推送
def pushProject = findProperty('BUILD_PUSH_TYPE') == 'jg'
? project(':feature:jg_push') : project(':feature:acloud_push')
implementation(pushProject) {
// def pushProperty = findProperty('BUILD_PUSH_TYPE')
// // 根据BUILD_PUSH_TYPE决定使用哪个推送SDK目前默认使用极光推送
// def pushProject = (pushProperty == null || pushProperty == 'jg')
// ? project(':feature:jg_push') : project(':feature:acloud_push')
// implementation(pushProject) {
// exclude group: 'androidx.swiperefreshlayout'
// }
implementation(project(":va-lib"))
implementation(project(':va-main')) {
exclude group: 'androidx.swiperefreshlayout'
}
debugImplementation "com.bytedance.tools.codelocator:codelocator-core:2.0.3"
compileOnly project(":va-core")
compileOnly project(":va-plugin-host-lib")
implementation project(":va-plugin-host")
implementation project(":va-archive")
}
File propFile = file('sign.properties')

View File

@ -1,32 +1,79 @@
package com.gh.vspace
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.annotation.Keep
import com.gh.gamecenter.R
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.LayoutPersonalOtherItemBinding
import com.gh.vspace.installexternalgames.InstallExternalGameActivity
import com.lg.vspace.VaApp
import com.lg.vspace.plugin.host.PluginHelper
import com.lightgame.utils.Utils
import com.lightgame.utils.toast.ToastHelper
import com.lody.virtual.client.core.VirtualCore
import java.io.File
@Keep
class ExternalGameUsage : IExternalGamesUsage {
override fun addInstallExternalGameButton(viewParent: ViewGroup) {
class ExternalGameUsage : ITestCase {
private fun buttonTemplate(viewParent: ViewGroup, id: Int, fn: (LayoutPersonalOtherItemBinding) -> Unit) {
val context = viewParent.context
viewParent.findViewById<View>(R.id.install_game_from_external) ?: run {
viewParent.findViewById<View>(id) ?: run {
val binding = LayoutPersonalOtherItemBinding.inflate(LayoutInflater.from(context)).apply {
root.id = R.id.install_game_from_external
titleTv.text = context.getString(R.string.title_install_external_game)
iconIv.setImageResource(R.drawable.ic_personal_my_game)
root.setOnClickListener {
VHelper.connectService {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
}
}
root.id = id
fn(this)
}
viewParent.addView(binding.root, 0)
}
}
override fun addInstallExternalGameButton(viewParent: ViewGroup) {
val context = viewParent.context
buttonTemplate(viewParent, R.id.install_game_from_external) {
it.titleTv.text = context.getString(R.string.title_install_external_game)
it.iconIv.setImageResource(R.drawable.ic_personal_my_game)
it.root.setOnClickListener {
VHelper.connectService {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
}
}
}
}
override fun addInstallPluginButton(viewParent: ViewGroup) {
buttonTemplate(viewParent, R.id.install_plugin) {
it.titleTv.text = "安装64位插件"
it.root.setOnClickListener {
val file = File("/data/local/tmp/gh-plugins/artifacts.zip")
if (file.exists()) {
Utils.log(VHelper.LOG_TAG, "有本地更新文件: 64位插件")
// TODO: 补充debug插件更新
ToastUtils.showToast("暂未实现debug功能")
} else {
ToastUtils.showToast("data/local/tmp没有push文件")
}
}
}
}
override fun addInstallPlugin32Button(viewParent: ViewGroup) {
buttonTemplate(viewParent, R.id.install_plugin_32) {
it.titleTv.text = "安装32位插件"
it.root.setOnClickListener {
val file = File("/data/local/tmp/gh-plugins/artifacts32.zip")
if (file.exists()) {
// TODO: 补充debug插件更新
ToastUtils.showToast("暂未实现debug功能")
} else {
ToastUtils.showToast("data/local/tmp没有push文件")
}
}
}
}
}

View File

@ -25,6 +25,11 @@ class ExternalGameAdapter(private val games: List<ExternalGameUiState>, private
路径:${item.apkPath}
""".trimIndent()
}
holder.update.setOnClickListener {
onItemClickListener.onItemClick(item, OnItemClickListener.ClickType.CLICK_INSTALL)
}
holder.install.goneIf(item.isInstalled) {
holder.install.setOnClickListener {
onItemClickListener.onItemClick(item, OnItemClickListener.ClickType.CLICK_INSTALL)

View File

@ -8,4 +8,5 @@ class ExternalGameViewHolder(binding: LayoutExternalGameItemBinding) : RecyclerV
val install = binding.btnInstall
val uninstall = binding.btnUninstall
val start = binding.btnStart
val update = binding.btnUpdate
}

View File

@ -16,6 +16,7 @@ import com.gh.gamecenter.databinding.FragmentInstallExternalGamesBinding
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lg.vspace.VirtualAppManager
import com.lightgame.download.DownloadEntity
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
@ -105,6 +106,15 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
private fun install(externalGameUiState: ExternalGameUiState) {
val bit =
externalGameUiState.externalGameEntity.cpuAbi.let { if (it.size == 1 && it.contains("armeabi-v7a")) "32" else "64" }
VHelper.install(requireContext(), DownloadEntity().apply {
externalGameUiState.externalGameEntity.apply {
packageName = apkPackageName
path = apkPath
}
}, true)
if (VHelper.showDialogIfVSpaceIsNeeded(
requireContext(),
"",
@ -144,7 +154,9 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
com.gh.gamecenter.BuildConfig.VERSION_NAME,
HaloApp.getInstance().channel,
"",
""
"",
com.lg.core.BuildConfig.VERSION_NAME,
HaloApp.getInstance().oaid
)
requireActivity().startActivity(intent)
}

View File

@ -24,6 +24,14 @@
android:visibility="gone"
tools:visibility="visible" />
<Button
android:id="@+id/btn_update"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/text_update"
android:visibility="visible"
tools:visibility="visible" />
<Button
android:id="@+id/btn_uninstall"
android:layout_width="wrap_content"

View File

@ -1,4 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<item name="install_game_from_external" type="id" />
<item name="install_plugin" type="id" />
<item name="install_plugin_32" type="id" />
</resources>

View File

@ -2,6 +2,7 @@
<resources>
<string name="title_install_external_game">从SD卡安装</string>
<string name="text_install">安装</string>
<string name="text_update">更新</string>
<string name="text_uninstall">卸载</string>
<string name="text_start">启动</string>
</resources>

View File

@ -92,7 +92,30 @@
com.tencent.qqmini,
com.tencent.qqmini.minigame.external,
com.tencent.qqmini.minigame.opensdk,
com.tencent.qqmini.union.ad" />
com.tencent.qqmini.union.ad,
com.lg.vspace,
io.lg.va.common,
com.va.floating,
com.lg.cloud,
com.lg.archive,
com.lg.vclient,
com.va.realname,
com.lg.vspace.flavor,
com.lg.update,
com.lg.login,
com.lg.accelerator,
com.lody.virtual,
com.lg.core,
com.lg.ads,
com.lg.common,
com.lg.vspace.network,
com.lody.virtual.lib.res,
com.va.host,
com.lg.vspace.plugin.host,
com.lg.plugin.constant,
com.bytedance.tools.codelocator,
org.chickenhook.restrictionbypass,
com.lody.virtual.sandhook,com.lg.vspace.common" />
<!-- 去掉 SDK 一些流氓权限 -->
<uses-permission
@ -783,7 +806,8 @@
android:name="androidx.core.content.FileProvider"
android:authorities="${applicationId}"
android:exported="false"
android:grantUriPermissions="true">
android:grantUriPermissions="true"
tools:replace="android:authorities">
<meta-data
android:name="android.support.FILE_PROVIDER_PATHS"
android:resource="@xml/provider_paths" />

View File

@ -191,10 +191,11 @@ object AdDelegateHelper {
}
/**
* 热启动是否需要显示开屏广告
* 热启动是否需要显示开屏广告(目前只展示第三方广告)
*/
private fun shouldShowStartUpAdWhenHotLaunch() =
mSplashAd?.displayRule?.hotStartSplashAd?.type == AD_TYPE_SDK && mSplashAd?.hotStartThirdPartyAd != null
private fun shouldShowStartUpAdWhenHotLaunch() = (mCsjAdImpl != null || mBeiziAdImpl != null)
&& mSplashAd?.displayRule?.hotStartSplashAd?.type == AD_TYPE_SDK
&& mSplashAd?.hotStartThirdPartyAd != null
/**
* 是否需要显示下载管理广告
@ -203,6 +204,10 @@ object AdDelegateHelper {
return mDownloadManagerAd != null && !isMatchAdFreeRule(mDownloadManagerAd) && isMatchDownloadManagerAdDisplayRule()
}
fun shouldShowHelperLaunchAd(): Boolean {
return mVGameLaunchAd != null && !isMatchAdFreeRule(mVGameLaunchAd) && isMatchAdDisplayRule(mVGameLaunchAd, Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME)
}
/**
* 是否需要显示游戏搜索广告
*/
@ -247,10 +252,11 @@ object AdDelegateHelper {
/**
* 是否大于广告管理展示间隔时长
*/
private fun isMatchDownloadManagerAdDisplayRule(): Boolean {
mDownloadManagerAd?.displayRule?.run {
private fun isMatchDownloadManagerAdDisplayRule(): Boolean = isMatchAdDisplayRule(mDownloadManagerAd, Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME)
private fun isMatchAdDisplayRule(adConfig: AdConfig?, spKey: String): Boolean {
adConfig?.displayRule?.run {
if (adDisplayInterval > 0) {
val lastShowTime = SPUtils.getLong(Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME, 0L)
val lastShowTime = SPUtils.getLong(spKey, 0L)
val durationInMinutes = (System.currentTimeMillis() - lastShowTime).toFloat() / 1000 / 60
return durationInMinutes > adDisplayInterval
} else {

View File

@ -0,0 +1,44 @@
package com.gh.ad
import android.content.Context
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.lg.vspace.ui.launcher.ILaunchAd
@Route(path = RouteConsts.provider.vaAd, name = "畅玩启动页广告")
class LaunchAdImpl : ILaunchAd {
override fun init(context: Context?) {
}
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View) {
if (AdDelegateHelper.shouldShowHelperLaunchAd()) {
val launchAd = AdDelegateHelper.vGameLaunchAd
val showThirdPartyAd = launchAd?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
val thirdPartyAd = launchAd?.thirdPartyAd
if (showThirdPartyAd && thirdPartyAd != null) {
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
}
}
}

View File

@ -18,8 +18,11 @@ import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.PackageFlavorHelper
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.feedback.view.suggest.SuggestionActivity
import com.gh.gamecenter.login.view.LoginActivity
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lg.vspace.ui.launcher.LaunchActivity
// TODO移动到对应的模块
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
@ -40,11 +43,20 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
&& activity !is SkipActivity
&& activity !is AuthorizationActivity
&& activity !is SplashAdActivity
&& activity !is SuggestionActivity
) {
activity.startActivity(SplashAdActivity.getIntent(activity))
}
isFromBackgroundToForeground = false
}
if (activityCount == 1) {
// 清除桌面角标
if (activity !is SplashScreenActivity && activity !is AuthorizationActivity) {
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
pushProvider?.cleanBadgeNumber(activity.applicationContext)
}
}
}
override fun onActivityResumed(activity: Activity) {
@ -76,6 +88,8 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
}
if (activity is AppCompatActivity
&& activity !is LaunchActivity
&& activity !is LoginActivity
&& activity !is SplashScreenActivity
&& activity !is SkipActivity
&& activity !is AuthorizationActivity
@ -84,10 +98,6 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
}
XapkInstaller.updateCurrentInstallStatus()
// 清除桌面角标
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
pushProvider?.cleanBadgeNumber(activity.applicationContext)
}
override fun onActivityPaused(activity: Activity) {

View File

@ -219,7 +219,7 @@ class DefaultJsApi(
runOnUiThread {
// 若畅玩列表中安装了,优先启动畅玩游戏
if (VHelper.isInstalled(packageName)) {
if (!VHelper.showDialogIfVSpaceIsNeeded(context, "", "", "", "")) {
VHelper.validateVSpaceBeforeAction(context, packageName, null) {
VHelper.launch(context, packageName)
}
} else {

View File

@ -16,7 +16,7 @@ class ValidateVSpaceHandler : DownloadChainHandler() {
}
if (asVGame) {
VHelper.validateVSpaceBeforeAction(context, gameEntity) {
VHelper.validateVSpaceBeforeAction(context,gameEntity.getUniquePackageName(), gameEntity) {
closure.invoke()
}
} else {

View File

@ -1,6 +1,7 @@
package com.gh.common.constant;
import android.annotation.SuppressLint;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
@ -21,14 +22,18 @@ import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.core.utils.UrlFilterUtils;
import com.gh.gamecenter.entity.AppEntity;
import com.gh.gamecenter.entity.GameGuidePopupEntity;
import com.gh.gamecenter.entity.NewApiSettingsEntity;
import com.gh.gamecenter.entity.NewSettingsEntity;
import com.gh.gamecenter.entity.VNewSetting;
import com.gh.gamecenter.entity.VSetting;
import com.gh.gamecenter.feature.entity.SettingsEntity;
import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.feature.utils.ContentBlockedHelper;
import com.gh.gamecenter.receiver.PackageChangeBroadcastReceiver;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.gh.gamecenter.retrofit.service.VApiService;
import com.gh.vspace.VHelper;
import com.halo.assistant.HaloApp;
@ -39,8 +44,12 @@ import org.json.JSONObject;
import java.io.IOException;
import java.util.Locale;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import io.reactivex.subjects.BehaviorSubject;
import okhttp3.ResponseBody;
public class Config {
@ -68,6 +77,10 @@ public class Config {
private static NewApiSettingsEntity.NightMode mNightModeSetting;
private static SimulatorEntity mNewSimulatorEntity;
private static VSetting mVSetting;
private static VNewSetting mVNewSetting;
private static AppEntity mNew32UpdateEntity;
public static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static GameGuidePopupEntity mGameGuidePopupEntity;
private static SharedPreferences mDefaultSharedPreferences;
@ -94,8 +107,7 @@ public class Config {
getPreferences().edit().putString(SETTINGS_KEY, GsonUtils.toJson(settingsEntity)).apply();
mSettingsEntity = settingsEntity;
// 加载完设置后刷新下
PackageHelper.initList();
PackageHelper.refreshList();
}
@Nullable
@ -196,6 +208,27 @@ public class Config {
return mVSetting;
}
@Nullable
public static VNewSetting getVNewSettingEntity() {
if (mVNewSetting == null) {
try {
String json = SPUtils.getString(Constants.SP_V_NEW_SETTINGS);
if (!TextUtils.isEmpty(json)) {
mVNewSetting = GsonUtils.fromJson(json, VNewSetting.class);
vNewSettingSubject.onNext(mVNewSetting);
}
} catch (Exception e) {
e.printStackTrace();
}
}
return mVNewSetting;
}
@Nullable
public static AppEntity getNew32UpdateEntity() {
return mNew32UpdateEntity;
}
/**
* 请求网络数据,尝试刷新畅玩相关配置
*/
@ -216,6 +249,36 @@ public class Config {
});
}
@SuppressLint("CheckResult")
public static void getNewSetting() {
VApiService vApi = RetrofitManager.getInstance().getVApi();
vApi.getNewSettings(BuildConfig.VERSION_NAME, Build.VERSION.SDK_INT).flatMap(new Function<VNewSetting, SingleSource<AppEntity>>() {
@Override
public SingleSource<AppEntity> apply(VNewSetting data) throws Exception {
mVNewSetting = data;
vNewSettingSubject.onNext(mVNewSetting);
SPUtils.setString(Constants.SP_V_NEW_SETTINGS, GsonUtils.toJson(data));
if (data.getVa() != null && data.getVa().getArch32() != null) {
String versionNameByPackageName = PackageUtils.getVersionNameByPackageName(data.getVa().getArch32().getPackageName());
return vApi.getNewPackageUpdate(
BuildConfig.VERSION_NAME,
versionNameByPackageName != null ? versionNameByPackageName : "",
HaloApp.getInstance().getChannel()
);
}
return Single.error(new IllegalStateException("VNewSetting entity is not expected"));
}
})
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(new BiResponse<AppEntity>() {
@Override
public void onSuccess(AppEntity data) {
mNew32UpdateEntity = data;
}
});
}
@Nullable
public static GameGuidePopupEntity getGameGuidePopupEntity() {
return mGameGuidePopupEntity;
@ -270,6 +333,7 @@ public class Config {
});
refreshVSettingEntity();
getNewSetting();
RetrofitManager.getInstance()
.getApi().getGameGuidePopup(Build.MANUFACTURER, Build.VERSION.RELEASE, Build.MODEL, channel, BuildConfig.VERSION_NAME)
@ -332,8 +396,31 @@ public class Config {
if (mNewApiSettingsEntity.getGameShieldContents() != null) {
ContentBlockedHelper.INSTANCE.init(mNewApiSettingsEntity.getGameShieldContents());
}
// 更新安装列表是否开启的配置
// if (mNewApiSettingsEntity.getInstalledComplianceSwitch() != null) {
// PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(mNewApiSettingsEntity.getInstalledComplianceSwitch());
// } else {
// PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(false);
// }
// 更新包名监听是否开启
if (mNewApiSettingsEntity.isPackageObserveEnable()) {
observePackageChange(mNewApiSettingsEntity.getPackageObserveActions());
}
}
});
}
}
public static void observePackageChange(NewApiSettingsEntity.PackageObserveActions packageObserveActions) {
PackageChangeBroadcastReceiver receiver = new PackageChangeBroadcastReceiver(packageObserveActions);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(packageObserveActions.getAdd());
intentFilter.addAction(packageObserveActions.getRem());
intentFilter.addAction(packageObserveActions.getRep());
intentFilter.addDataScheme("package");
HaloApp.getInstance().registerReceiver(receiver, intentFilter);
}
}

View File

@ -19,6 +19,7 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.util.DirectUtils
import com.gh.common.util.LogUtils
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
@ -59,7 +60,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
private val mDuration = 3000
private var mDisposable: Disposable? = null
private var mAdapter: PackageCheckAdapter? = null
private var mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
private var mAllInstalledPackages = PackageHelper.getInstalledPackages(HaloApp.getInstance().application, 0)
var gameEntity: GameEntity? = null
var callBack: ConfirmListener? = null
@ -325,7 +326,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
override fun onResume() {
super.onResume()
mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
mAllInstalledPackages = PackageHelper.getInstalledPackages(HaloApp.getInstance().application, 0)
gameEntity?.packageDialog?.let {
if (isAllPackageInstalled(mAllInstalledPackages, it)) {
callBack?.onConfirm()
@ -363,7 +364,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(busFour: EBPackage) {
if (busFour.isInstalledOrUninstalled()) {
mAllInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
mAllInstalledPackages = PackageHelper.getInstalledPackages(HaloApp.getInstance().application, 0)
mAdapter?.notifyDataSetChanged()
}
}
@ -416,7 +417,7 @@ class PackageCheckDialogFragment : BaseDialogFragment() {
return
}
val allInstalledPackages = PackageUtils.getInstalledPackages(HaloApp.getInstance().application, 0)
val allInstalledPackages = PackageHelper.getInstalledPackages(HaloApp.getInstance().application, 0)
if (isAllPackageInstalled(allInstalledPackages, packageDialogEntity)) {
callBack.onConfirm()
return

View File

@ -9,10 +9,12 @@ import androidx.fragment.app.FragmentActivity
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.SplashAdActivity
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.DialogEntity
import com.gh.gamecenter.feature.entity.WelcomeDialogEntity
@ -42,12 +44,13 @@ object GlobalPriorityChainHelper : ISuperiorChain {
return activity is FragmentActivity
&& !activity.isFinishing
&& activity !is SplashScreenActivity
&& activity !is SplashAdActivity
}
/**
* 预启动所有的优先级弹窗管理链
*/
fun preStart() {
fun preStart(withSpecialDelay: Boolean) {
val launchRedirectHandler = LaunchRedirectHandler(-101)
val updateDialogHandler = UpdateDialogHandler(-100)
val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99)
@ -64,8 +67,13 @@ object GlobalPriorityChainHelper : ISuperiorChain {
launchRedirectHandler.doPreProcess()
updateDialogHandler.doPreProcess()
requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler)
requestReserveDialogData(reserveDialogHandler)
// 首次启动延迟 300ms保证请求首次启动时已经获取到了 GID 、 OAID 等标记
val requestDelay = if (withSpecialDelay) 300L else 0L
AppExecutor.uiExecutor.executeWithDelay({
requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler)
requestReserveDialogData(reserveDialogHandler)
}, requestDelay)
}
/**
@ -123,6 +131,17 @@ object GlobalPriorityChainHelper : ISuperiorChain {
mainChain.resume()
}
/**
* 添加新的 handler 到优先级弹窗管理链 (插队!)
*/
fun queueNewHandler(handler: PriorityChainHandler) {
if (mainChain.isHandlerQueueEmpty()) {
observeLifecycle()
}
mainChain.addHandler(handler)
}
/**
* 请求首页启动弹窗相关的数据并执行相关 handler 的 preProcess
*/

View File

@ -0,0 +1,28 @@
package com.gh.common.prioritychain
import androidx.fragment.app.FragmentActivity
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.common.base.GlobalActivityManager
class RequestInstalledListPermissionHandler : PriorityChainHandler(-1000) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
val currentActivity = GlobalActivityManager.currentActivity ?: return false
if (currentActivity !is FragmentActivity) return false
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity = currentActivity,
ignorePermanentlyDenied = true
) {
processNext()
}
return true
}
}

View File

@ -8,6 +8,7 @@ import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IFlavorProvider
import com.halo.assistant.HaloApp
import com.va.host.HostUtils
@Route(path = RouteConsts.provider.app, name = "Application暴露服务")
class AppProviderImpl : IAppProvider {
@ -86,4 +87,6 @@ class AppProviderImpl : IAppProvider {
override fun setSkippingThirdParty(isSkippingThirdParty: Boolean) {
HaloApp.getInstance().isSkippingThirdParty = isSkippingThirdParty
}
override fun getPluginVersion(): String = HostUtils.getPluginVersion()
}

View File

@ -3,6 +3,7 @@ package com.gh.common.provider
import android.content.Context
import android.content.pm.PackageInfo
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IPackageUtilsProvider
@ -23,7 +24,7 @@ class PackageUtilsProviderImpl : IPackageUtilsProvider {
}
override fun getInstalledPackages(context: Context, flag: Int): List<PackageInfo> {
return PackageUtils.getInstalledPackages(context, flag)
return PackageHelper.getInstalledPackages(context, flag)
}
override fun getApkSignatureByPackageName(context: Context, packageName: String): Array<String> {
@ -38,6 +39,10 @@ class PackageUtilsProviderImpl : IPackageUtilsProvider {
return PackageUtils.isSignedByGh(context, packageName)
}
override fun isInstalledWithLauncherIcon(context: Context, packageName: String): Boolean {
return PackageUtils.isInstalled(context, packageName)
}
override fun getInstalledTime(context: Context, packageName: String): Long {
return PackageUtils.getInstalledTime(context, packageName)
}

View File

@ -166,162 +166,160 @@ class SimulatorDownloadManager private constructor() {
this.gameName = gameName
this.gameType = gameCategoryChinese
PermissionHelper.checkGetInstalledAppsListBeforeAction(context, object : EmptyCallback {
override fun onCallback() {
val isInstalledNewSimulator =
SimulatorGameManager.isNewSimulatorInstalled(HaloApp.getInstance().application)
//当没有安装新版本模拟器时候 判断是否隐藏
if (simulator?.active == false && !isInstalledNewSimulator) {
showNoneEmulatorDialog(context)
return
}
var isInstalled = PackageUtils.isInstalledFromAllPackage(
context,
simulator?.apk?.packageName
)
//模拟器管理界面还是用之前的逻辑
if (isInstalledNewSimulator && location != SimulatorLocation.SIMULATOR_MANAGE) {
isInstalled = isInstalledNewSimulator
}
// val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator?.apk?.packageName)
val shouldShowUpdate =
PackageUtils.isInstalledApkMatchedMd5(simulator?.apk?.packageName, simulator?.apk?.md5)
val showAlertTag = SPUtils.getString(SimulatorGameManager.SIMULATOR_UPDATE_SHOW_ALERT_TAG, "") //当天是否弹过
val todayIsShow = showAlertTag == TimeUtils.getToday()
downloadType = if (shouldShowUpdate && isInstalled) "update" else "download"
if (downloadType == "update" && todayIsShow && location != SimulatorLocation.SIMULATOR_MANAGE) {
return
}
if (downloadType == "download" && isInstalled) {
return
}
val title = if (shouldShowUpdate && isInstalled) "更新模拟器" else "安装模拟器"
val message =
if (shouldShowUpdate && isInstalled) "检测到模拟器存在更高版本,是否前往更新" else "模拟器游戏需要先下载安装对应的模拟器,才可以运行"
val positiveText =
if (shouldShowUpdate && isInstalled) "更新(${simulator?.apk?.size}" else "下载(${simulator?.apk?.size}"
val negativeText = if (shouldShowUpdate && isInstalled) "下次再说" else "取消"
val trackableEntity = TrackableEntity(
"模拟器下载",
key = if (shouldShowUpdate && isInstalled) "更新弹窗" else "下载弹窗",
logShowEvent = true
)
if (shouldShowUpdate && isInstalled) {
NewFlatLogUtils.logSimulatorUpdateAlertShow()
}
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
DialogHelper.showDialog(
context,
title,
message,
positiveText,
negativeText,
trackMtaEvent = true,
cancelClickCallback = {
if (shouldShowUpdate && isInstalled) {
cancelCallback?.invoke()
NewFlatLogUtils.logSimulatorUpdateAlertClick("取消")
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = negativeText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = negativeText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
confirmClickCallback = {
showDownloadingDialog(context, simulator, gameId, gameName, gameCategoryChinese)
NewFlatLogUtils.logSimulatorUpdateAlertClick("更新")
MtaHelper.onEvent(
trackableEntity.event,
trackableEntity.key,
if (shouldShowUpdate && isInstalled) "点击更新" else "点击下载"
)
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = positiveText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = positiveText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
touchOutsideCallback = {
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = "关闭弹窗",
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = "关闭弹窗",
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
mtaEvent = trackableEntity.event, mtaKey = trackableEntity.key,
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
if (downloadType == "update" && location != SimulatorLocation.SIMULATOR_MANAGE) {
SPUtils.setString(SimulatorGameManager.SIMULATOR_UPDATE_SHOW_ALERT_TAG, TimeUtils.getToday())
}
PermissionHelper.checkGetInstalledAppsListBeforeAction(context) { _ ->
val isInstalledNewSimulator =
SimulatorGameManager.isNewSimulatorInstalled(HaloApp.getInstance().application)
//当没有安装新版本模拟器时候 判断是否隐藏
if (simulator?.active == false && !isInstalledNewSimulator) {
showNoneEmulatorDialog(context)
return@checkGetInstalledAppsListBeforeAction
}
})
var isInstalled = PackageUtils.isInstalledFromAllPackage(
context,
simulator?.apk?.packageName
)
//模拟器管理界面还是用之前的逻辑
if (isInstalledNewSimulator && location != SimulatorLocation.SIMULATOR_MANAGE) {
isInstalled = isInstalledNewSimulator
}
// val versionFromInstalledApp = PackageUtils.getVersionNameByPackageName(simulator?.apk?.packageName)
val shouldShowUpdate =
PackageUtils.isInstalledApkMatchedMd5(simulator?.apk?.packageName, simulator?.apk?.md5)
val showAlertTag = SPUtils.getString(SimulatorGameManager.SIMULATOR_UPDATE_SHOW_ALERT_TAG, "") //当天是否弹过
val todayIsShow = showAlertTag == TimeUtils.getToday()
downloadType = if (shouldShowUpdate && isInstalled) "update" else "download"
if (downloadType == "update" && todayIsShow && location != SimulatorLocation.SIMULATOR_MANAGE) {
return@checkGetInstalledAppsListBeforeAction
}
if (downloadType == "download" && isInstalled) {
return@checkGetInstalledAppsListBeforeAction
}
val title = if (shouldShowUpdate && isInstalled) "更新模拟器" else "安装模拟器"
val message =
if (shouldShowUpdate && isInstalled) "检测到模拟器存在更高版本,是否前往更新" else "模拟器游戏需要先下载安装对应的模拟器,才可以运行"
val positiveText =
if (shouldShowUpdate && isInstalled) "更新(${simulator?.apk?.size}" else "下载(${simulator?.apk?.size}"
val negativeText = if (shouldShowUpdate && isInstalled) "下次再说" else "取消"
val trackableEntity = TrackableEntity(
"模拟器下载",
key = if (shouldShowUpdate && isInstalled) "更新弹窗" else "下载弹窗",
logShowEvent = true
)
if (shouldShowUpdate && isInstalled) {
NewFlatLogUtils.logSimulatorUpdateAlertShow()
}
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
DialogHelper.showDialog(
context,
title,
message,
positiveText,
negativeText,
trackMtaEvent = true,
cancelClickCallback = {
if (shouldShowUpdate && isInstalled) {
cancelCallback?.invoke()
NewFlatLogUtils.logSimulatorUpdateAlertClick("取消")
MtaHelper.onEvent(trackableEntity.event, trackableEntity.key, "点击下次再说")
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = negativeText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = negativeText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
confirmClickCallback = {
showDownloadingDialog(context, simulator, gameId, gameName, gameCategoryChinese)
NewFlatLogUtils.logSimulatorUpdateAlertClick("更新")
MtaHelper.onEvent(
trackableEntity.event,
trackableEntity.key,
if (shouldShowUpdate && isInstalled) "点击更新" else "点击下载"
)
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = positiveText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = positiveText,
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
touchOutsideCallback = {
if (shouldShowUpdate && isInstalled) {
SensorsBridge.trackSimulatorUpdateDialogClick(
buttonName = "关闭弹窗",
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
} else {
SensorsBridge.trackSimulatorInstallDialogClick(
buttonName = "关闭弹窗",
gameId = gameId,
gameName = gameName,
gameType = gameCategoryChinese,
lastPageId = GlobalActivityManager.getLastPageEntity().pageId,
lastPageName = GlobalActivityManager.getLastPageEntity().pageName,
lastPageBusinessId = GlobalActivityManager.getLastPageEntity().pageBusinessId
)
}
},
mtaEvent = trackableEntity.event, mtaKey = trackableEntity.key,
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
if (downloadType == "update" && location != SimulatorLocation.SIMULATOR_MANAGE) {
SPUtils.setString(SimulatorGameManager.SIMULATOR_UPDATE_SHOW_ALERT_TAG, TimeUtils.getToday())
}
}
}
fun showDownloadingDialog(

View File

@ -21,8 +21,10 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VArchiveHelper
import com.gh.vspace.VHelper
import com.gh.vspace.VHelper.is32
import com.lg.download.DownloadError
import com.lg.download.DownloadStatus
import com.lody.virtual.client.core.VirtualCore
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import splitties.systemservices.layoutInflater
@ -50,8 +52,6 @@ object ArchiveDownloadButtonHelper {
}
downloadBtn.setOnClickListener {
when {
// 检查是否已安装畅玩助手
!VHelper.isVSpaceInstalled(context) -> showVSpaceTipDialog(context, gameEntity)
// 检查是否已安装游戏
!VHelper.isInstalled(packageName) -> {
// 检查游戏是否在安装中
@ -273,14 +273,19 @@ object ArchiveDownloadButtonHelper {
R.string.archive_apply.toResString(),
R.string.cancel.toResString(),
{
applyArchive(context, entrance, packageName, config, archiveEntity, gameEntity)
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "使用")
SensorsBridge.trackEvent(
"CloudSaveOverwriteDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"button_name", "使用"
)
if(gameEntity.is32() && VHelper.isInnerInstalled(packageName) && !VirtualCore.get().isExtPackageInstalled) {
// 32位的游戏 并且 32位畅玩没有安装 并且 一体化
VHelper.newCwValidateVspaceBeforeAction(context, gameEntity) {}
} else {
applyArchive(context, entrance, packageName, config, archiveEntity, gameEntity)
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "使用")
SensorsBridge.trackEvent(
"CloudSaveOverwriteDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"button_name", "使用"
)
}
},
{
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "取消")

View File

@ -14,12 +14,10 @@ import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.core.utils.SentryHelper;
import com.gh.gamecenter.login.entity.IdCardEntity;
@ -32,8 +30,6 @@ import com.halo.assistant.HaloApp;
import com.lightgame.config.CommonDebug;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import io.reactivex.schedulers.Schedulers;
import io.sentry.Sentry;
import io.sentry.android.core.SentryAndroid;
@ -108,45 +104,51 @@ public class DataUtils {
}
public static void getGid() {
GidHelper.getInstance().registerDevice(HaloApp.getInstance().getApplication(), new GidCallback() {
@Override
public void onSuccess(String gid) {
Utils.log("Gid", gid);
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, gid).apply();
// 默认用 APP 级已存储的 GID 来使用,不使用外部 GID
String savedGid = SPUtils.getString(Constants.GID);
if (!TextUtils.isEmpty(savedGid)) {
HaloApp.getInstance().setGid(savedGid);
onGidReceived(savedGid);
} else {
GidHelper.getInstance().registerDevice(HaloApp.getInstance().getApplication(), new GidCallback() {
@Override
public void onSuccess(String gid) {
Utils.log("Gid", gid);
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, gid).apply();
// 默认用 APP 级已存储的 GID 来使用,不使用外部 GID
String savedGid = SPUtils.getString(Constants.GID);
if (!TextUtils.isEmpty(savedGid)) {
gid = savedGid;
} else {
SPUtils.setString(Constants.GID, gid);
onGidReceived(gid);
}
HaloApp.getInstance().setGid(gid);
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
getDeviceCertification(gid);
// 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid
MetaUtil.INSTANCE.refreshMeta();
ContentValues values = new ContentValues();
values.put(GhContentProvider.KEY_GID, gid);
values.put(GhContentProvider.KEY_ANDROID_ID, MetaUtil.getBase64EncodedAndroidId());
try {
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/device"), values);
} catch (Exception exception) {
SentryHelper.INSTANCE.onEvent("DEVICE_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
exception.printStackTrace();
@Override
public void onFailure(String s) {
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
}
}
});
}
}
@Override
public void onFailure(String s) {
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
private static void onGidReceived(String gid) {
HaloApp.getInstance().setGid(gid);
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
getDeviceCertification(gid);
// 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid
MetaUtil.INSTANCE.refreshMeta();
AppExecutor.getIoExecutor().execute(() -> {
ContentValues values = new ContentValues();
values.put(GhContentProvider.KEY_GID, gid);
values.put(GhContentProvider.KEY_ANDROID_ID, MetaUtil.getBase64EncodedAndroidId());
try {
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/device"), values);
} catch (Exception exception) {
SentryHelper.INSTANCE.onEvent("DEVICE_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
exception.printStackTrace();
}
});
}

View File

@ -508,11 +508,18 @@ object DownloadObserver {
"space_schema_type",
if (downloadEntity.packageName == VHelper.VSPACE_32BIT_PACKAGENAME) "32位" else "64位"
)
} else if(downloadEntity.gameId == Constants.HALO_FUN_NEW_32_GAME_ID) {
SensorsBridge.trackEvent(
"HaloFunDownloadDone",
"space_schema_type",
"32位(新)"
)
}
if (downloadEntity.gameId != Constants.GHZS_GAME_ID
&& downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) != Constants.SIMULATOR_DOWNLOAD
&& downloadEntity.gameId != Constants.HALO_FUN_GAME_ID
&& downloadEntity.gameId != Constants.HALO_FUN_NEW_32_GAME_ID
) {
val trackJson = downloadEntity.customPageTrackDataJson
val kvs = if (!trackJson.isNullOrBlank()) {

View File

@ -282,7 +282,7 @@ object GameActivityDownloadHelper {
location: String,
traceEvent: ExposureEvent
) {
VHelper.validateVSpaceBeforeAction(context, gameEntity) {
VHelper.validateVSpaceBeforeAction(context,gameEntity.getUniquePackageName(), gameEntity) {
GamePermissionDialogFragment.show((context as AppCompatActivity), gameEntity, gameEntity.info) {
DialogUtils.checkDownload(
context,

View File

@ -50,7 +50,7 @@ public class InstallUtils {
public void handleMessage(Message msg) {
if (msg.what == INSTALL_WHAT && packageManager != null) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = PackageUtils.getInstalledPackages(context, 0);
List<PackageInfo> packageInfos = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
list.add(packageInfo.packageName);

View File

@ -768,7 +768,7 @@ public class LibaoUtils {
}
public static boolean isAppInstalled(Context context, String packageName) {
List<PackageInfo> pinfo = PackageUtils.getInstalledPackages(context, 0);
List<PackageInfo> pinfo = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;

View File

@ -93,12 +93,13 @@ object NewFlatLogUtils {
// 畅玩助手更新弹窗展示事件
@JvmStatic
fun logHaloFunUpdateDialogShow(gameId: String, gameName: String, gameArchitecture: String) {
fun logHaloFunUpdateDialogShow(gameId: String, gameName: String, gameArchitecture: String, targetVaVersion: String) {
val json = json {
KEY_EVENT to "halo_fun_update_dialog_show"
"game_id" to gameId
"game_name" to gameName
"game_architecture" to gameArchitecture
"target_va_version" to targetVaVersion
parseAndPutMeta().invoke(this)
}
log(json)
@ -142,12 +143,13 @@ object NewFlatLogUtils {
// 畅玩助手更新弹窗点击事件
@JvmStatic
fun logHaloFunUpdateDialogClick(dialogType: String, buttonType: String, architecture: String) {
fun logHaloFunUpdateDialogClick(dialogType: String, buttonType: String, architecture: String, targetVaVersion: String) {
val json = json {
KEY_EVENT to "halo_fun_update_dialog_click"
"dialog_type" to dialogType
KEY_BUTTON_TYPE to buttonType
"architecture" to architecture
"target_va_version" to targetVaVersion
parseAndPutMeta().invoke(this)
}
log(json)

View File

@ -0,0 +1,161 @@
package com.gh.common.util
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.lightgame.utils.Utils
import org.greenrobot.eventbus.EventBus
object PackageChangeHelper : DefaultLifecycleObserver {
private const val TAG = "PackageChangeHelper"
private const val INSTALL_PENDING = 1
private const val UNINSTALL_PENDING = 2
private const val UPDATE_PENDING = 3
// <包名pending 类型,应用版本> Triple
private var pendingPackagePair: Triple<String, Int, String>? = null
private var pendingGhId: String ? = null
/**
* 添加一个等待中,待确定是否已成功安装的应用
*/
fun addInstallPendingPackage(packageName: String) {
val installData = PackagesManager.getInstalledData(packageName)
if (installData == null) {
Utils.log(TAG, "添加了: $packageName 包名等待安装成功")
pendingPackagePair = Triple(packageName, INSTALL_PENDING, "")
} else {
Utils.log(TAG, "添加了: $packageName 包名等待安装更新成功")
val ghId = PackageUtils.getGhId(packageName)
// 记录光环插件相关信息,用于安装成功后的处理
if (ghId != null) {
pendingGhId = ghId.toString()
}
pendingPackagePair = Triple(packageName, UPDATE_PENDING, installData.version)
}
}
/**
* 添加一个等待中,待确定是否已成功卸载的应用
*/
fun addUninstallPendingPackage(packageName: String) {
Utils.log(TAG, "添加了: $packageName 包名等待卸载成功")
pendingPackagePair = Triple(packageName, UNINSTALL_PENDING, "")
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
if (pendingPackagePair != null) {
val packageName = pendingPackagePair?.first ?: return
val isInstallPending = pendingPackagePair?.second == INSTALL_PENDING
val isUninstallPending = pendingPackagePair?.second == UNINSTALL_PENDING
val isUpdatePending = pendingPackagePair?.second == UPDATE_PENDING
val pendingVersion = pendingPackagePair?.third ?: ""
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
val isInstalled = installedVersionName != null
if (isInstallPending && isInstalled) {
pendingPackagePair = null
pendingGhId = null
PackageRepository.addInstalledGame(packageName)
performInstallSuccessAction(packageName)
} else if (isUninstallPending && !isInstalled) {
pendingPackagePair = null
pendingGhId = null
performUninstallSuccessAction(packageName)
} else if (isUpdatePending) {
val isUpdateValid = if (installedVersionName != pendingVersion) {
true
} else {
!pendingGhId.isNullOrEmpty() && pendingGhId != PackageUtils.getGhId(packageName).toString()
}
pendingPackagePair = null
pendingGhId = null
if (isUpdateValid) {
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName)
}
}
}
}
fun addInstall(packageName: String) {
performInstallSuccessAction(packageName)
}
fun addUpdate(packageName: String) {
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName)
}
/**
* 对应包名安装成功后的操作,继承至 PackageChangeBroadcastObserver
*/
private fun performInstallSuccessAction(packageName: String, withLog: Boolean = true) {
Utils.log(TAG, "安装了: $packageName 包名的程序")
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
val gameId = if (downloadEntity != null && downloadEntity.gameId != null) downloadEntity.gameId else ""
val gameName = if (downloadEntity != null && downloadEntity.name != null) downloadEntity.name else ""
if (withLog) {
NewFlatLogUtils.logGameInstallComplete(gameId, gameName)
SensorsBridge.trackInstallGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeInstall(packageName)
PackageHelper.refreshLocalPackageList()
val versionName = PackageUtils.getVersionNameByPackageName(packageName)
val installEb = EBPackage(EBPackage.TYPE_INSTALLED, packageName, versionName)
PackageObserver.onPackageChanged(installEb)
EventBus.getDefault().post(installEb)
}
fun addUninstall(packageName: String) {
performUninstallSuccessAction(packageName)
}
/**
* 对应包名卸载成功后的操作,继承至 PackageChangeBroadcastObserver
*/
private fun performUninstallSuccessAction(packageName: String, withLog: Boolean = true) {
Utils.log(TAG, "卸载了: $packageName 包名的程序")
val install = PackagesManager.getInstalledData(packageName)
val gameId = if (install?.id != null) install.id else ""
val gameName = if (install?.name != null) install.name else ""
if (withLog) {
NewFlatLogUtils.logGameUninstallComplete(gameId!!, gameName!!)
SensorsBridge.trackUnloadGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeUninstall(packageName)
PackageHelper.refreshLocalPackageList()
val uninstallEb = EBPackage(EBPackage.TYPE_UNINSTALLED, packageName, "")
PackageObserver.onPackageChanged(uninstallEb)
EventBus.getDefault().post(uninstallEb)
}
}

View File

@ -1,24 +1,81 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.os.Build
import android.provider.Settings
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.constant.Config
import com.gh.common.prioritychain.GlobalPriorityChainHelper
import com.gh.common.prioritychain.RequestInstalledListPermissionHandler
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.common.utils.PermissionHelper.isGetInstalledListPermissionDisabled
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.WhitePackageListEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
object PackageHelper {
private const val TAG = "PackageHelper"
private const val SP_GET_INSTALLED_API_AGREED = "get_installed_api_agreed"
private const val UNKNOWN = -1
private const val UNSUPPORTED = 0
private const val SUPPORTED = 1
private const val ENABLED = 2
private const val DISABLED = 3
private var lastInstalledPackageListTime = 0L
private var cachedInstalledPackagesList: List<PackageInfo> = arrayListOf()
private var isGetInstalledPackagesApiAgreed = true // 用户是否已经同意使用已安装应用列表 API
private var isGetInstalledPackagesApiAgreedRequired = DISABLED // 需要用户手动授权才获取已安装应用列表的功能的开关
private var isGetInstalledPackagesPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
var commentPackageNameBlackList = arrayListOf<String>()
private var _commentPackageNameBlackList = arrayListOf<String>()
val commentPackageNameBlackList: ArrayList<String> = _commentPackageNameBlackList
// 关闭下载的包列表
var downloadPackageNameBlackList = arrayListOf<String>()
private var _downloadPackageNameBlackList = arrayListOf<String>()
val downloadPackageNameBlackList: ArrayList<String> = _downloadPackageNameBlackList
// 本地已安装的包去掉关闭下载的包后的列表
var validLocalPackageNameSet = hashSetOf<String>()
private var _validLocalPackageNameSet = hashSetOf<String>()
val validLocalPackageNameSet: HashSet<String> = _validLocalPackageNameSet
// 游戏包名匹配列表
var relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
// 本地已安装包的列表
var localPackageNameSet = hashSetOf<String>()
@ -31,6 +88,22 @@ object PackageHelper {
}
}
/**
* 获取已安装的白名单列表(为了在没有已安装应用列表获取能力的时候也能正常判断更新、插件化)
*/
@SuppressLint("CheckResult")
fun getInstalledWhiteList() {
RetrofitManager.getInstance().newApi.installWhitelist
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<WhitePackageListEntity>() {
override fun onSuccess(data: WhitePackageListEntity) {
data.data?.let {
addInstalledButMissingPackages(it)
}
}
})
}
@JvmStatic
fun refreshLocalPackageList() {
localPackageNameSet = getAllPackageName(HaloApp.getInstance().application)
@ -38,33 +111,26 @@ object PackageHelper {
}
@JvmStatic
fun initList() {
Config.getSettings()?.gameCommentBlackList?.let {
commentPackageNameBlackList = ArrayList(it)
}
Config.getSettings()?.gameDownloadBlackList?.let {
downloadPackageNameBlackList = ArrayList(it)
}
Config.getSettings()?.gamePackageMatch?.let {
relatedPackageList = ArrayList(it)
}
fun refreshList() {
Config.getSettings()?.gameCommentBlackList?.let { _commentPackageNameBlackList = ArrayList(it) }
Config.getSettings()?.gameDownloadBlackList?.let { _downloadPackageNameBlackList = ArrayList(it) }
Config.getSettings()?.gamePackageMatch?.let { _relatedPackageList = ArrayList(it) }
Config.getSettings()?.gameDownloadBlackList
updateValidPackageNameList()
}
private fun updateValidPackageNameList() {
validLocalPackageNameSet =
_validLocalPackageNameSet =
localPackageNameSet.filterNot { p -> downloadPackageNameBlackList.contains(p) }.toHashSet()
}
/*
/**
* 获取所有已安装的软件的包名、版本(非系统应用)
*/
private fun getAllPackageName(context: Context): HashSet<String> {
val set = HashSet<String>()
return try {
val packageInfos = PackageUtils.getInstalledPackages(context, 0)
val packageInfos = getInstalledPackages(context, 0)
for (packageInfo in packageInfos) {
if (packageInfo.applicationInfo.flags and ApplicationInfo.FLAG_SYSTEM == 0) {
if (context.packageName != packageInfo.packageName) {
@ -79,4 +145,357 @@ object PackageHelper {
}
}
/**
* 弃用已安装列表缓存
*/
fun dumpInstalledListCache() {
lastInstalledPackageListTime = 0
}
/**
* 在超时后,若后台没有开启获取已安装应用列表的功能,默认以接口不控制的方式获取已安装应用列表
*/
fun fallbackInstalledPackageApiSwitchAfterTimeout(timeout: Long) {
CoroutineScope(SupervisorJob()).launch {
delay(timeout)
if (isGetInstalledPackagesApiAgreedRequired == UNKNOWN) {
Utils.log(TAG, "后台没有开启获取已安装应用列表的功能,超时后默认以接口不控制的方式获取已安装应用列表")
updateIsGetInstalledPackagesApiAgreedRequired(false)
}
}
}
/**
* 更新已安装应用列表获取开关状态
*/
fun updateIsGetInstalledPackagesApiAgreedRequired(isEnabled: Boolean) {
// 若状态不为 unknown 或者用户已经同意使用了,无需再更新
if (isGetInstalledPackagesApiAgreedRequired != UNKNOWN || isGetInstalledPackagesApiAgreed()) {
Utils.log(TAG, "installedPackageApiSwitchStatus 不为 UNKNOWN无需再更新")
return
}
if (isEnabled) {
getInstalledWhiteList()
_installedPackageApiSwitchStatusLiveData.postValue(true)
isGetInstalledPackagesApiAgreedRequired = ENABLED
} else {
isGetInstalledPackagesApiAgreedRequired = DISABLED
if (isSupportGetInstalledAppsPermission(HaloApp.getInstance())) {
GlobalPriorityChainHelper.queueNewHandler(RequestInstalledListPermissionHandler())
} else {
agreeOnGetInstalledPackagesApi()
}
}
}
/**
* 用户是否已经允许了调用获取已安装应用列表接口
* 优先用内存的值,没有再从 SP 中获取并更新
*/
fun isGetInstalledPackagesApiAgreed(): Boolean {
return isGetInstalledPackagesApiAgreed
|| (SPUtils.getBoolean(SP_GET_INSTALLED_API_AGREED).also { isGetInstalledPackagesApiAgreed = it })
}
fun isGetInstalledPackagesApiAgreedRequired(): Boolean {
return isGetInstalledPackagesApiAgreedRequired == ENABLED
}
/**
* 同意使用已安装应用列表 API
*/
private fun agreeOnGetInstalledPackagesApi() {
isGetInstalledPackagesApiAgreed = true
SPUtils.setBoolean(SP_GET_INSTALLED_API_AGREED, true)
_installedPackageApiSwitchStatusLiveData.postValue(false)
}
/**
* 获取已安装应用列表
*/
fun getInstalledPackages(context: Context?, flags: Int): List<PackageInfo> {
Utils.log(TAG, "即将获取已安装应用列表")
// 用户未同意使用已安装应用列表 API返回空列表
if (!isGetInstalledPackagesApiAgreed()) {
Utils.log(TAG, "用户未同意使用已安装应用列表 API返回空列表")
return cachedInstalledPackagesList
}
// 简单 debounce 过于频繁的获取已安装应用列表调用
if (System.currentTimeMillis() - lastInstalledPackageListTime < 3000 && cachedInstalledPackagesList.isNotEmpty()) {
Utils.log(TAG, "使用了缓存的已安装应用列表")
return cachedInstalledPackagesList
}
var shouldGetNewInstalledPackagedList = false
// 当前设备是否支持限制获取已安装应用列表的功能
if (isSupportGetInstalledAppsPermission(context!!)) {
Utils.log(TAG, "当前设备支持限制获取已安装应用列表的功能")
// 当前设备是否支持禁用了获取已安装应用列表
if (!isGetInstalledListPermissionDisabled(context)) {
Utils.log(TAG, "当前设备没有限制获取已安装应用列表的功能")
shouldGetNewInstalledPackagedList = true
} else {
Utils.log(TAG, "当前设备已限制获取已安装应用列表的功能")
}
} else {
Utils.log(TAG, "当前设备不支持限制获取已安装应用列表的功能")
shouldGetNewInstalledPackagedList = true
}
if (shouldGetNewInstalledPackagedList) {
lastInstalledPackageListTime = System.currentTimeMillis()
cachedInstalledPackagesList = getInstalledPackagesInternal(context, flags)
}
return cachedInstalledPackagesList
}
/**
* 显示获取已安装应用列表的对话框并请求权限
*/
fun showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity: FragmentActivity,
ignorePermanentlyDenied: Boolean = false,
resultClosure: (Boolean) -> Unit
) {
val globalOnPermissionGrantedClosure = {
agreeOnGetInstalledPackagesApi()
// 进行包名初始化相关的操作
PackageRepository.initData()
refreshLocalPackageList()
refreshList()
}
if (isSupportGetInstalledAppsPermission(activity)) {
// 若系统已经授予了获取应用列表的权限,直接进行授权成功回调
if (!isGetInstalledListPermissionDisabled(activity)) {
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
return
}
PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = true,
ignorePermanentlyDenied = ignorePermanentlyDenied
) { isGranted ->
if (isGranted) {
SensorsBridge.trackInstalledListPermissionsResult("成功")
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
} else {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsResult("拒绝")
}
}
} else {
val hintDialog = PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = false,
) {
// do nothing
}
SensorsBridge.trackInstalledListPermissionsCustomDialogShow()
val noticeDialog = DialogHelper.showGuideDialog(
context = activity,
title = "权限申请",
content = "是否允许“光环助手”获取已安装的应用信息",
confirmText = "开启",
cancelText = "拒绝",
confirmClickCallback = {
SensorsBridge.trackInstalledListPermissionsCustomClick("开启")
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
},
cancelClickCallback = {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsCustomClick("拒绝")
}
)
noticeDialog?.setOnDismissListener {
hintDialog?.dismiss()
}
}
}
/**
* 延迟5秒后上报已安装应用列表
*/
private fun trackInstalledListAfterDelay() {
CoroutineScope(SupervisorJob()).launch {
delay(5000)
SensorsBridge.trackNumberOfInstalledList(localPackageNameSet.size, localPackageNameSet)
}
}
/**
* 是否支持动态获取已安装应用列表权限
*/
fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
// 若存在缓存,直接返回缓存结果。
if (isGetInstalledPackagesPermissionSupported != UNKNOWN) {
return isGetInstalledPackagesPermissionSupported != UNSUPPORTED
}
try {
// 根据官方提供的方法来判定是否支持限制获取已安装应用列表
val flag =
Settings.Secure.getInt(context.contentResolver, "oem_installed_apps_runtime_permission_enable", 0)
if (flag == 1) {
isGetInstalledPackagesPermissionSupported = SUPPORTED
return true
}
// 部分未升级的手机没有上面配置项,有定义下面危险权限也认为是支持设备软件列表管控
val packageManager = context.packageManager
val permissionInfo = packageManager.getPermissionInfo("com.android.permission.GET_INSTALLED_APPS", 0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS) {
isGetInstalledPackagesPermissionSupported = SUPPORTED
return true
} else {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
} else {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
} catch (e: PackageManager.NameNotFoundException) {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
}
/**
* 确保指定包名的应用在已安装了的情况下能正常收录
*/
fun addInstalledButMissingPackages(packageNameSet: HashSet<String>) {
Utils.log(TAG, "addInstalledButMissingPackages 检查已安装但未收录的应用")
val installedPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
if (!PackagesManager.isInstalled(packageName)
&& PackageUtils.getVersionNameByPackageName(packageName) != null
) {
installedPackageNameSet.add(packageName)
}
}
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
PackageRepository.addInstalledGames(
pkgNameList = ArrayList(installedPackageNameSet),
updateInstallStatus = true
)
}
fun refreshWrongInstallStatus(packageNameSet: MutableSet<String>) {
runOnIoThread {
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
val installedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
if (!PackagesManager.isInstalled(packageName)
&& installedVersionName != null
) {
installedButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName == null) {
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName != null
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)) {
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
}
}
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已安装状态的包数量为 ${installedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}")
runOnUiThread {
if (installedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in installedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addInstall(packageName)
}
}
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUninstall(packageName)
}
}
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUpdate(packageName)
}
}
}
}
}
/**
* 在5.1系统手机使用PackageManager获取已安装应用容易发生Package manager has died异常
* https://stackoverflow.com/questions/13235793/transactiontoolargeeception-when-trying-tÏo-get-a-list-of-applications-installed/30062632#30062632
*/
private fun getInstalledPackagesInternal(context: Context, flags: Int): List<PackageInfo> {
Utils.log(TAG, "调用系统 API 获取已安装应用列表")
val pm = context.packageManager
try {
return pm.getInstalledPackages(flags)
} catch (ignored: java.lang.Exception) {
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
val process: Process
val result: MutableList<PackageInfo> = java.util.ArrayList()
var bufferedReader: BufferedReader? = null
try {
process = Runtime.getRuntime().exec("pm list packages")
bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
var line: String
while ((bufferedReader.readLine().also { line = it }) != null) {
val packageName = line.substring(line.indexOf(':') + 1)
val packageInfo = pm.getPackageInfo(packageName, flags)
result.add(packageInfo)
}
process.waitFor()
} catch (e: java.lang.Exception) {
e.printStackTrace()
if (e is InterruptedException) {
Thread.currentThread().interrupt()
}
} finally {
if (bufferedReader != null) try {
bufferedReader.close()
} catch (e: IOException) {
e.printStackTrace()
}
}
return result
}
}

View File

@ -108,6 +108,12 @@ object PackageInstaller {
return
}
val packageName = downloadEntity?.packageName ?: PackageUtils.getPackageNameByPath(context, pkgPath)
packageName?.let {
PackageChangeHelper.addInstallPendingPackage(packageName)
}
try {
// 判断是否需要使用浏览器来进行安装
if (BrowserInstallHelper.isUseBrowserToInstallEnabled()
@ -250,6 +256,8 @@ object PackageInstaller {
fun uninstallForPackageName(context: Context, pkn: String?) {
if (pkn.isNullOrEmpty()) return
PackageChangeHelper.addUninstallPendingPackage(pkn)
val uninstallIntent = Intent()
uninstallIntent.action = Intent.ACTION_DELETE
uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT)

View File

@ -7,14 +7,12 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -29,8 +27,6 @@ import com.gh.common.xapk.XapkInstaller;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.PackageFlavorHelper;
import com.gh.gamecenter.common.utils.PermissionHelper;
import com.gh.gamecenter.core.utils.MD5Utils;
import com.gh.gamecenter.core.utils.SentryHelper;
import com.gh.gamecenter.feature.entity.ApkEntity;
@ -49,12 +45,10 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@ -68,20 +62,14 @@ import java.util.zip.ZipFile;
public class PackageUtils {
private static long mLastInstalledPackageListTime = 0L;
private static List<PackageInfo> mInstalledPackageList = null;
private static final String TAG = "PackageUtils";
// 设备是否支持禁用获取已安装应用列表。-1 代表支持情况未知0 代表不支持, 1 代表支持
private static int mIsSupportGetInstalledListPermission = -1;
public static String getInstallPackageInfoSourceDir(String packageName) {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).applicationInfo.sourceDir;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}
@ -319,7 +307,7 @@ public class PackageUtils {
return new String[]{null, null};
}
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return new String[]{null, null};
}
@ -599,7 +587,7 @@ public class PackageUtils {
.getPackageInfo(packageName, 0);
return packageInfo.firstInstallTime;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
}
@ -626,7 +614,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager()
.getPackageInfo(BuildConfig.APPLICATION_ID, 0).lastUpdateTime;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
@ -640,7 +628,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}
@ -653,7 +641,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
}
@ -667,7 +655,7 @@ public class PackageUtils {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}
@ -677,7 +665,7 @@ public class PackageUtils {
*/
public static ArrayList<String> getAllPackageName(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
List<PackageInfo> packageInfos = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
if (!context.getPackageName().equals(packageInfo.packageName)) {
@ -690,7 +678,7 @@ public class PackageUtils {
public static ArrayList<String> getAllPackageNameIncludeGh(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
List<PackageInfo> packageInfos = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
list.add(packageInfo.packageName);
@ -704,11 +692,9 @@ public class PackageUtils {
*/
public static ArrayList<String> getAllPackageNameIncludeSystemApps(Context context) {
ArrayList<String> list = new ArrayList<>();
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
List<PackageInfo> packageInfos = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
for (PackageInfo packageInfo : packageInfos) {
if (!context.getPackageName().equals(packageInfo.packageName)) {
list.add(packageInfo.packageName);
}
list.add(packageInfo.packageName);
}
return list;
}
@ -717,7 +703,7 @@ public class PackageUtils {
JSONArray jsonArray = new JSONArray();
try {
PackageManager pm = context.getPackageManager();
List<PackageInfo> packageInfos = getInstalledPackages(context, 0);
List<PackageInfo> packageInfos = PackageHelper.INSTANCE.getInstalledPackages(context, 0);
for (PackageInfo packageInfo : packageInfos) {
if ((packageInfo.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
JSONObject jsonObject = new JSONObject();
@ -758,7 +744,6 @@ public class PackageUtils {
}
}
/**
* 启动应用
* 请使用 PackageLauncher.launchApp()
@ -778,22 +763,6 @@ public class PackageUtils {
}
}
/*
* 根据包名,获取软件名称
*/
public static String getNameByPackageName(Context context, String packageName) {
try {
PackageManager pm = context.getApplicationContext().getPackageManager();
ApplicationInfo applicationInfo = pm.getApplicationInfo(
packageName, 0);
return applicationInfo.loadLabel(pm).toString();
} catch (NameNotFoundException e) {
e.printStackTrace();
}
return null;
}
/**
* todo 统一判断
* <p>
@ -912,8 +881,8 @@ public class PackageUtils {
String packageName = context.getApplicationContext().getPackageName();
for (ActivityManager.RunningAppProcessInfo appProcess : appProcesses) {
// The name of the process that this object is associated with.
if (appProcess.processName.equals(packageName) && appProcess.importance
== ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
if (appProcess.processName.equals(packageName)
&& appProcess.importance == ActivityManager.RunningAppProcessInfo.IMPORTANCE_FOREGROUND) {
return true;
}
}
@ -924,132 +893,6 @@ public class PackageUtils {
return false;
}
/**
* 弃用已安装列表缓存
*/
public static void dumpInstalledListCache() {
mLastInstalledPackageListTime = 0;
}
public static List<PackageInfo> getInstalledPackages(Context context, int flags) {
Utils.log(TAG, "即将获取已安装应用列表");
// 简单 debounce 掉过于频繁的调用获取已安装列表调用
if (System.currentTimeMillis() - mLastInstalledPackageListTime < 1000
&& mInstalledPackageList != null
&& mInstalledPackageList.size() > 0) {
Utils.log(TAG, "使用了缓存的已安装应用列表");
return new ArrayList<>(mInstalledPackageList);
}
// 是否需要调用系统 API 获取最新的已安装应用列表
boolean shouldGetNewInstalledPackagedList = false;
// 当前设备是否支持限制获取已安装应用列表的功能
if (isSupportGetInstalledAppsPermission(context)) {
Utils.log(TAG, "当前设备支持限制获取已安装应用列表的功能");
// 当前设备是否支持禁用了获取已安装应用列表
if (!PermissionHelper.isGetInstalledListPermissionDisabled(context)) {
Utils.log(TAG, "当前设备没有限制获取已安装应用列表的功能");
shouldGetNewInstalledPackagedList = true;
} else {
Utils.log(TAG, "当前设备已限制获取已安装应用列表的功能");
}
} else {
Utils.log(TAG, "当前设备不支持限制获取已安装应用列表的功能");
shouldGetNewInstalledPackagedList = true;
}
if (shouldGetNewInstalledPackagedList) {
mLastInstalledPackageListTime = System.currentTimeMillis();
mInstalledPackageList = getInstalledPackagesInternal(context, flags);
}
if (mInstalledPackageList == null) {
mInstalledPackageList = new ArrayList<>();
}
return mInstalledPackageList;
}
public static boolean isSupportGetInstalledAppsPermission(Context context) {
// 若存在缓存,直接返回缓存结果。为 0 代表不支持,为 1 代表支持
if (mIsSupportGetInstalledListPermission != -1) {
return mIsSupportGetInstalledListPermission != 0;
}
try {
// 根据官方提供的方法来判定是否支持限制获取已安装应用列表
int flag = Settings.Secure.getInt(context.getContentResolver(), "oem_installed_apps_runtime_permission_enable", 0);
if (flag == 1) {
mIsSupportGetInstalledListPermission = 1;
return true;
}
// 部分未升级的手机没有上面配置项,有定义下面危险权限也认为是支持设备软件列表管控
PackageManager packageManager = context.getPackageManager();
PermissionInfo permissionInfo = packageManager.getPermissionInfo("com.android.permission.GET_INSTALLED_APPS", 0);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (permissionInfo.getProtection() == PermissionInfo.PROTECTION_DANGEROUS) {
mIsSupportGetInstalledListPermission = 1;
return true;
} else {
mIsSupportGetInstalledListPermission = 0;
return false;
}
} else {
mIsSupportGetInstalledListPermission = 0;
return false;
}
} catch (NameNotFoundException e) {
mIsSupportGetInstalledListPermission = 0;
return false;
}
}
/**
* 在5.1系统手机使用PackageManager获取已安装应用容易发生Package manager has died异常
* https://stackoverflow.com/questions/13235793/transactiontoolargeeception-when-trying-tÏo-get-a-list-of-applications-installed/30062632#30062632
*/
private static List<PackageInfo> getInstalledPackagesInternal(Context context, int flags) {
Utils.log(TAG, "调用系统 API 获取已安装应用列表");
final PackageManager pm = context.getPackageManager();
try {
return pm.getInstalledPackages(flags);
} catch (Exception ignored) {
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
Process process;
List<PackageInfo> result = new ArrayList<>();
BufferedReader bufferedReader = null;
try {
process = Runtime.getRuntime().exec("pm list packages");
bufferedReader = new BufferedReader(new InputStreamReader(process.getInputStream()));
String line;
while ((line = bufferedReader.readLine()) != null) {
final String packageName = line.substring(line.indexOf(':') + 1);
final PackageInfo packageInfo = pm.getPackageInfo(packageName, flags);
result.add(packageInfo);
}
process.waitFor();
} catch (Exception e) {
e.printStackTrace();
if (e instanceof InterruptedException) {
Thread.currentThread().interrupt();
}
} finally {
if (bufferedReader != null)
try {
bufferedReader.close();
} catch (IOException e) {
e.printStackTrace();
}
}
return result;
}
public static String getWebviewPath(Context context) {
final PackageInfo webViewPackageInfo = WebViewCompat.getCurrentWebViewPackage(context);
return webViewPackageInfo != null ? webViewPackageInfo.applicationInfo.sourceDir : null;

View File

@ -14,7 +14,7 @@ import com.gh.gamecenter.common.retrofit.EmptyResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper
import com.gh.gamecenter.core.utils.UrlFilterUtils
@ -153,7 +153,7 @@ object PackageObserver {
}
}
runOnIoThread { postNewlyInstalledApp(gameId, packageName) }
AppExecutor.logExecutor.execute { postNewlyInstalledApp(gameId, packageName) }
}
if (EBPackage.TYPE_UNINSTALLED == busFour.type) {

View File

@ -24,6 +24,7 @@ import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.TimeElapsedHelper
@ -181,7 +182,7 @@ class DownloadDialog : BaseDraggableDialogFragment() {
AppExecutor.uiExecutor.executeWithDelay({
recyclerView.adapter?.let {
for (i in 0 until it.itemCount) {
val apkEntity = itemList[i].normal ?: continue
val apkEntity = itemList.safelyGetInRelease(i)?.normal ?: continue
val apkCollection = apkEntity.apkCollection
val platformName = platformList[0].name
val packageName = platformList[0].packageName

View File

@ -28,10 +28,10 @@ import org.json.JSONObject
import java.io.File
import java.net.URLEncoder
import java.util.*
import kotlin.collections.ArrayList
object BrowserInstallHelper {
// 随便选的 32321 居然在部分 vivo 手机上被占用,喷了
private const val PORT = 40705
private const val RESERVE_PORT = 40706
@ -40,15 +40,12 @@ object BrowserInstallHelper {
private val mContext by lazy { HaloApp.getInstance().application }
private var mUseReservePort = false
private val mAllInstalledPackageList: ArrayList<String> by lazy {
PackageUtils.getAllPackageNameIncludeSystemApps(HaloApp.getInstance().applicationContext).apply {
add(HaloApp.getInstance().applicationContext.packageName)
}
}
private var mValidInstalledPackageList: ArrayList<String> = arrayListOf()
private var mValidConditionMatchedCache: Boolean? = null
private fun getServer(port: Int): DownloadServer {
val server = DownloadServer(port)
for (packageName in mAllInstalledPackageList) {
for (packageName in getAllInstalledPackageList()) {
if (packageName.contains("com.freeme") || packageName.contains("com.zhuoyi")) {
server.isBuggyDevice = true
break
@ -57,6 +54,23 @@ object BrowserInstallHelper {
return server
}
private fun getAllInstalledPackageList(): ArrayList<String> {
when {
mValidInstalledPackageList.isNotEmpty() -> {
return mValidInstalledPackageList
}
else -> {
val allInstalledPackageList = PackageUtils.getAllPackageNameIncludeSystemApps(mContext)
if (allInstalledPackageList.isNotEmpty()) {
mValidInstalledPackageList = allInstalledPackageList
}
return mValidInstalledPackageList
}
}
}
fun downloadFile(filePath: String) {
if (!::mServer.isInitialized) mServer = if (mUseReservePort) getServer(RESERVE_PORT) else getServer(PORT)
if (!mServer.isAlive && !startServer()) {
@ -237,32 +251,43 @@ object BrowserInstallHelper {
* 是否满足开启浏览器安装的条件
*/
private fun isConditionMatched(settingsEntity: NewSettingsEntity): Boolean {
if (mValidConditionMatchedCache != null) {
return mValidConditionMatchedCache!!
}
val packageList = getAllInstalledPackageList()
if (packageList.isEmpty()) return false
settingsEntity.installModel?.whiteList?.let {
for (packageName in it) {
if (mAllInstalledPackageList.contains(packageName)) {
return false
if (packageList.contains(packageName)) {
mValidConditionMatchedCache = false
break
}
}
}
settingsEntity.installModel?.packages?.let {
for (packageName in it) {
if (mAllInstalledPackageList.contains(packageName)) {
return true
if (packageList.contains(packageName)) {
mValidConditionMatchedCache = true
break
}
}
}
// 匹配部分字符串即可
settingsEntity.installModel?.regexPackages?.let {
for (packageNamePieces in it) {
for (installedPackageName in mAllInstalledPackageList) {
for (installedPackageName in packageList) {
if (installedPackageName.contains(packageNamePieces)) {
return true
mValidConditionMatchedCache = true
break
}
}
}
}
return false
return mValidConditionMatchedCache ?: false
}
fun onApkInstalled(path: String?) {

View File

@ -136,6 +136,9 @@ import io.reactivex.SingleSource;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.HttpException;
@ -281,6 +284,8 @@ public class MainActivity extends BaseActivity {
Config.getGhzsSettings();
} else if (Config.getVSettingEntity() == null) {
Config.refreshVSettingEntity();
} else if (Config.getVNewSettingEntity() == null) {
Config.getNewSetting();
}
// 耗时操作
@ -556,7 +561,10 @@ public class MainActivity extends BaseActivity {
} else {
TextView jumpBtn = findViewById(R.id.jumpBtn);
jumpBtn.setText(String.format(Locale.CHINA, "跳过 %d", COUNTDOWN_MAX_COUNT - mCountdownCount));
mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_AD, 1000);
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
}
}
}
@ -735,14 +743,11 @@ public class MainActivity extends BaseActivity {
ToastUtils.showToast("游戏启动中,请稍后~");
handler.postDelayed(() -> {
VHelper.postOnInitialized(() -> {
if (VHelper.isInstalled(gamePackageName)) {
VHelper.launch(this, gamePackageName, false, true);
} else {
ToastUtils.showToast("应用已被卸载!");
}
return null;
});
if(VHelper.isInnerInstalled(gamePackageName)) {
launchGame(gamePackageName).invoke();
} else {
VHelper.postOnInitialized(launchGame(gamePackageName));
}
}, 500);
break;
case KEY_MARKET_DETAILS:
@ -760,6 +765,19 @@ public class MainActivity extends BaseActivity {
}, 500);
}
@NonNull
private Function0<Unit> launchGame(String gamePackageName) {
return () -> {
if (!VHelper.isInnerInstalled(gamePackageName) && !VHelper.isInstalled(gamePackageName)) {
ToastUtils.showToast("应用已被卸载!");
} else {
VHelper.shortcutLaunch();
VHelper.launch(this, gamePackageName, false, true);
}
return null;
};
}
/**
* 应用跳转
*/
@ -927,6 +945,8 @@ public class MainActivity extends BaseActivity {
Config.getGhzsSettings();
} else if (Config.getVSettingEntity() == null) {
Config.refreshVSettingEntity();
} else if (Config.getVNewSettingEntity() == null) {
Config.getNewSetting();
}
mPackageViewModel.checkData();
@ -937,8 +957,10 @@ public class MainActivity extends BaseActivity {
// 接收登录和登出更新事件统计的 Meta
@Subscribe(threadMode = ThreadMode.MAIN)
public void onEventMainThread(EBReuse reuse) {
if (reuse.getType().equals(LOGIN_TAG) || reuse.getType().equals(LOGOUT_TAG)) {
boolean isLoginEvent = reuse.getType().equals(LOGIN_TAG);
if (isLoginEvent || reuse.getType().equals(LOGOUT_TAG)) {
MetaUtil.INSTANCE.refreshMeta();
VHelper.INSTANCE.updateAuthorizeInfo(isLoginEvent);
}
}

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter;
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_BROWSER;
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_PUSH;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ANSWER;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARCHIVE_LOGIN;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARTICLE;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_CATEGORY;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COLUMN;
@ -21,6 +22,7 @@ import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_QQ_GAME;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_QQ_GROUP;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_QQ_QUN;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_QUESTION;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_RESTART_GAME;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_SUGGESTION;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_TOOLBOX;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_UPLOAD_VIDEO;
@ -64,8 +66,10 @@ import com.gh.gamecenter.core.utils.ToastUtils;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.entity.VideoLinkEntity;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.gamecenter.login.view.LoginActivity;
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel;
import com.gh.gamecenter.video.videomanager.VideoManagerActivity;
import com.gh.vspace.VHelper;
import com.gh.vspace.shortcut.OnCreateShortcutResult;
import com.gh.vspace.shortcut.ShortcutManager;
import com.gh.vspace.shortcut.ShortcutPermissionTipsDialog;
@ -405,6 +409,27 @@ public class SkipActivity extends BaseActivity {
} catch (JSONException ignored) {
}
break;
case HOST_ARCHIVE_LOGIN:
String gamePkg = uri.getQueryParameter(EntranceConsts.KEY_GAME_PKG);
if(CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
} else {
Bundle newBundle = new Bundle();
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivity(this, null, newBundle, (resultCode, data) -> {
if(CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
}
VHelper.launch(this, gamePkg, false, false);
finish();
});
return;
}
break;
case HOST_RESTART_GAME:
String restartGamePkg = uri.getQueryParameter(EntranceConsts.KEY_GAME_PKG);
VHelper.launch(this, restartGamePkg, false, true);
break;
default:
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页
return;

View File

@ -197,7 +197,7 @@ class SplashScreenActivity : BaseActivity() {
// 尝试获取安装应用列表权限并启动首页(不在乎结果)
private fun requestGetInstallListPermissionAndLaunchMainActivity() {
if (PackageUtils.isSupportGetInstalledAppsPermission(this)
if (PackageHelper.isSupportGetInstalledAppsPermission(this)
&& PermissionHelper.isGetInstalledListPermissionDisabled(this)
) {
PermissionHelper.requestGetInstalledAppsListPermission(this, true) {

View File

@ -18,6 +18,7 @@ import android.widget.TextView;
import androidx.collection.ArrayMap;
import androidx.core.content.ContextCompat;
import com.gh.common.util.PackageHelper;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.gamecenter.R;
@ -188,7 +189,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
}
private int doType(String packageName) {
List<PackageInfo> pakageinfos = PackageUtils.getInstalledPackages(mContext, 0);
List<PackageInfo> pakageinfos = PackageHelper.INSTANCE.getInstalledPackages(mContext, 0);
for (PackageInfo pi : pakageinfos) {
if ((pi.applicationInfo.flags & ApplicationInfo.FLAG_SYSTEM) == 0) {
String pi_packageName = pi.packageName;

View File

@ -27,7 +27,7 @@ class CommunicationService : Service() {
override fun installGameCompleted(packageName: String, params: VGameInstallerResult) {
Utils.log(VHelper.LOG_TAG, "包名 $packageName 安装的结果是 ${params.status}")
VHelper.onInstallFinished(packageName, params)
VHelper.onInstallFinished(packageName, params.status)
}
override fun connectionCompleted() {

View File

@ -45,7 +45,7 @@ import kotlin.math.abs
class AmwayFragment : LazyListFragment<AmwayListItemData, AmwayViewModel>() {
private lateinit var mViewModel: AmwayViewModel
private val mViewModel: AmwayViewModel by lazy { viewModelProvider() }
private val mElapsedHelper by lazy { TimeElapsedHelper() }
private lateinit var mExposureListener: ExposureListener
@ -131,14 +131,13 @@ class AmwayFragment : LazyListFragment<AmwayListItemData, AmwayViewModel>() {
}
}
override fun provideListViewModel(): AmwayViewModel {
mViewModel = viewModelProvider()
return mViewModel
}
override fun getItemDecoration() = VerticalItemDecoration(context, 12F, false)
.apply { mItemDecoration = this }
override fun provideListViewModel(): AmwayViewModel? {
return mViewModel
}
override fun provideListAdapter(): ListAdapter<*> {
if (mAdapter == null) {
val basicExposureSource = arrayListOf<ExposureSource>().apply {

View File

@ -5,6 +5,7 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.utils.formatTime
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.databinding.ItemArchiveLimitBinding
import com.gh.gamecenter.entity.ArchiveEntity
@ -29,10 +30,7 @@ class ArchiveLimitAdapter(context: Context) : ListAdapter<ArchiveLimitAdapter.Ar
if (holder is ArchiveLimitViewHolder) {
val item = mEntityList[position]
holder.binding.tvTitle.text = item.data.name
val timeLong = item.data.time.create
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)
val date = Date(timeLong)
holder.binding.tvTime.text = sdf.format(date)
holder.binding.tvTime.text = item.data.time.create.formatTime("yyyy-MM-dd HH:mm")
val resId = if (item.isChecked) R.drawable.ic_selector_selected else R.drawable.ic_selector_default
holder.binding.ivSelector.setImageResource(resId)

View File

@ -33,10 +33,12 @@ import com.gh.gamecenter.livedata.EventObserver
import com.gh.gamecenter.manager.PackagesManager
import com.gh.vspace.VArchiveHelper
import com.gh.vspace.VHelper
import com.gh.vspace.VHelper.is32
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
import com.lightgame.utils.Utils
import com.lody.virtual.client.core.VirtualCore
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.File
@ -101,6 +103,10 @@ class CloudArchiveManagerActivity : BaseActivity_TabLayout(), ArchiveLimitSelect
controlUploadGameArchive()
uploadTv.setOnClickListener {
when {
mGameEntity.is32() && VHelper.isInnerInstalled(mGameEntity?.getUniquePackageName()) && !VirtualCore.get().isExtPackageInstalled -> {
VHelper.newCwValidateVspaceBeforeAction(this@CloudArchiveManagerActivity, mGameEntity) {
}
}
// 检查是否已安装游戏
!VHelper.isInstalled(mGameEntity?.getUniquePackageName()) -> toast("暂未检测到本地的存档数据,请玩会儿游戏再试~")
else -> {

View File

@ -21,6 +21,7 @@ import androidx.recyclerview.widget.RecyclerView
import com.gh.ad.AdDelegateHelper
import com.gh.common.util.HomePluggableHelper
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageInstaller
import com.gh.download.DownloadManager
import com.gh.gamecenter.DownloadManagerActivity
@ -276,6 +277,12 @@ class DownloadFragment : BaseFragment_TabLayout() {
if (mBinding.adGameItemContainer.isVisible) {
DownloadManager.getInstance().addObserver(mDataWatcher)
}
refreshInstallStatus()
}
private fun refreshInstallStatus() {
PackageHelper.refreshWrongInstallStatus(PackagesManager.getInstalledSet())
}
override fun onParentActivityFinish() {

View File

@ -10,8 +10,8 @@ import com.ethanhua.skeleton.ViewSkeletonScreen
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
import com.gh.common.util.PackageHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.eventbus.EBReuse
@ -29,7 +29,6 @@ import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.packagehelper.PackageViewModel
import com.gh.gamecenter.wrapper.MainWrapperFragment
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
@ -147,15 +146,20 @@ class NewInstalledGameFragment : ToolbarFragment() {
}
mBinding.run {
if (PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
val isGetInstalledListDisagreed = PackageHelper.isGetInstalledPackagesApiAgreedRequired()
&& !PackageHelper.isGetInstalledPackagesApiAgreed()
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
reuseNoneData.reuseNoneDataIv.visibility = View.GONE
reuseNoneData.reuseNoneDataTv.text = "开启应用列表权限"
reuseNoneData.reuseNoneDataDescTv.text = " 及时获悉游戏最新的更新消息"
reuseNoneData.reuseResetLoadTv.text = "去开启"
reuseNoneData.reuseResetLoadTv.setOnClickListener {
PermissionHelper.requestGetInstalledAppsListPermission(requireActivity()) {
updateNoDataView()
PackageRepository.initData()
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
if (isGranted) {
updateNoDataView()
}
}
}
} else {

View File

@ -3,8 +3,8 @@ package com.gh.gamecenter.download
import android.view.View
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DirectUtils
import com.gh.common.util.PackageHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.EntranceConsts
@ -12,12 +12,10 @@ import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.FixLinearLayoutManager
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.EmptyCallback
import com.gh.gamecenter.databinding.FragmentGameUpdatableBinding
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.packagehelper.PackageViewModel
import com.gh.gamecenter.wrapper.MainWrapperFragment
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.Subscribe
@ -40,7 +38,6 @@ class UpdatableGameFragment : LazyFragment() {
}
}
override fun getRealLayoutId() = R.layout.fragment_game_updatable
override fun onRealLayoutInflated(inflatedView: View) {
mBinding = FragmentGameUpdatableBinding.bind(inflatedView)
@ -98,21 +95,21 @@ class UpdatableGameFragment : LazyFragment() {
noDataContainer.reuseResetLoadTv.layoutParams = layoutParam
noDataContainer.reuseResetLoadTv.visibility = View.VISIBLE
noDataContainer.reuseNoneDataDescTv.visibility = View.VISIBLE
if (PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
val isGetInstalledListDisagreed = !PackageHelper.isGetInstalledPackagesApiAgreed()
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
noDataContainer.reuseNoneDataIv.visibility = View.GONE
noDataContainer.reuseNoneDataTv.text = "开启应用列表权限"
noDataContainer.reuseNoneDataDescTv.text = "及时获悉游戏最新的更新消息"
noDataContainer.reuseResetLoadTv.text = "去开启"
noDataContainer.reuseResetLoadTv.setOnClickListener {
PermissionHelper.requestGetInstalledAppsListPermission(
requireActivity(),
false,
object : EmptyCallback {
override fun onCallback() {
updateNoDataView()
PackageRepository.initData()
}
})
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
if (isGranted) {
updateNoDataView()
}
}
}
} else {
noDataContainer.reuseNoneDataIv.visibility = View.VISIBLE

View File

@ -34,7 +34,9 @@ class AppEntity(
*/
var alert: String? = null,
// 关联64位更新
var relation: AppEntity? = null
var relation: AppEntity? = null,
@SerializedName("_id")
var id: String? = null
) : Parcelable {
fun isAlertEveryTime() = alert == "EVERY_TIME_OPEN"

View File

@ -11,6 +11,12 @@ class NewApiSettingsEntity(
var startup: StartupAdEntity? = null,//启动文案广告
@SerializedName("user_interested_game")
var userInterestedGame: Boolean = false, //偏好设置状态开关
@SerializedName("installed_compliance_switch")
var installedComplianceSwitch: Boolean? = false, //安装合规开关
@SerializedName("listen_switch")
var isPackageObserveEnable: Boolean = false, // 安装包监听开关
@SerializedName("listen_str")
var packageObserveActions: PackageObserveActions? = null, // 安装包监听的三个 action
var install: Install, // 安装相关的
@SerializedName("game_shield_contents")
var gameShieldContents: List<String>? = listOf(),//游戏屏蔽内容
@ -46,4 +52,13 @@ class NewApiSettingsEntity(
val type: String,
val link: LinkEntity
)
class PackageObserveActions(
@SerializedName("ADD")
val add: String,
@SerializedName("REM")
val rem: String,
@SerializedName("REP")
val rep: String
)
}

View File

@ -1,8 +1,6 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import android.text.TextUtils
import com.gh.common.constant.Config
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.GameEntity
@ -110,11 +108,14 @@ data class SubjectEntity(
@IgnoredOnParcel
var data: MutableList<GameEntity>?
get() = RegionSettingHelper.filterGame(mData)
get() = filteredData ?: RegionSettingHelper.filterGame(mData).also { filteredData = it }
set(value) {
mData = value
mData = RegionSettingHelper.filterGame(value).also { filteredData = it }
}
@IgnoredOnParcel
private var filteredData: MutableList<GameEntity>? = null
val showStar: Boolean
get() = _showStar ?: false

View File

@ -0,0 +1,42 @@
package com.gh.gamecenter.entity
import com.google.gson.annotations.SerializedName
class VNewSetting {
@SerializedName("va")
var va: Va? = null
@SerializedName("va_plugin")
var vaPlugin: VaPlugin? = null
class Va(
@SerializedName("32-bit")
val arch32: VaArch? = null,
)
class VaArch(
val size: String,
@SerializedName("package")
val packageName: String,
@SerializedName("version")
val versionName: String,
@SerializedName("version_code")
val versionCode: Int,
val url: String
)
data class VaPlugin(
@SerializedName("_id")
val id: String?,
@SerializedName("version_name")
val versionName: String?,
@SerializedName("change_log")
var changeLog: String?,
@SerializedName("url_32")
val url32: String?,
@SerializedName("url_64")
val url64: String?,
)
}

View File

@ -0,0 +1,5 @@
package com.gh.gamecenter.entity
class WhitePackageListEntity {
var data: HashSet<String>? = null
}

View File

@ -17,11 +17,9 @@ import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
import com.gh.common.browse.BrowseTimer
import com.gh.common.browse.withLifecycle
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.TrackableDialog
import com.gh.gamecenter.common.base.adapter.FragmentAdapter
@ -74,7 +72,6 @@ class CommunityHomeFragment : LazyFragment() {
private var mBottomTabId = ""
private val browseTimer = BrowseTimer()
.withLifecycle(this)
.withResult {
SensorsBridge.trackCommunityBrowsingDuration(it / 1000.0)
}
@ -202,13 +199,14 @@ class CommunityHomeFragment : LazyFragment() {
mBottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: ""
}
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
browseTimer.start()
} else {
browseTimer.stop()
}
override fun onResume() {
super.onResume()
browseTimer.start()
}
override fun onPause() {
super.onPause()
browseTimer.stop()
}
override fun onSaveInstanceState(outState: Bundle) {

View File

@ -798,6 +798,8 @@ class GameCollectionDetailFragment :
}
}, 2000)
}
mListViewModel.refreshPackageStatus()
}
override fun onPause() {

View File

@ -10,6 +10,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.ErrorHelper
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.constant.Constants
@ -64,6 +65,8 @@ open class GameCollectionDetailViewModel(
var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
var isPostFirstOver = false
private var packageNameSet = hashSetOf<String>()
@SuppressLint("CheckResult")
fun getGameCollectionDetail() {
mApi.getGameCollectionDetail(gameCollectionId)
@ -128,10 +131,17 @@ open class GameCollectionDetailViewModel(
add(mResultLiveData.value!![i])
}
} else {
packageNameSet = hashSetOf()
games?.forEach {
it.isAdData = adIconActive
add(CommentItemData(game = it))
it.getApk().forEach { apk ->
packageNameSet.add(apk.packageName)
}
}
refreshPackageStatus()
}
}
@ -158,6 +168,12 @@ open class GameCollectionDetailViewModel(
override fun getHandleTopCommentCondition(index: Int) =
!isHandleTopComment && gameCollectionDetail != null && topCommentId.isNotBlank() && index == 0
fun refreshPackageStatus() {
if (packageNameSet.isNotEmpty()) {
PackageHelper.refreshWrongInstallStatus(packageNameSet)
}
}
fun followingCommand(userId: String, isFollow: Boolean) {
val observable = if (isFollow) {
RetrofitManager.getInstance().api.postFollowing(userId)

View File

@ -2399,6 +2399,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
DownloadManager.getInstance().addObserver(dataWatcher)
controlInstallHint()
mViewModel.refreshWrongInstallStatus()
}
override fun onFragmentPause() {

View File

@ -14,8 +14,8 @@ import com.gh.common.history.HistoryHelper
import com.gh.common.util.CheckLoginUtils
import com.gh.gamecenter.feature.utils.ConcernUtils
import com.gh.common.util.LibaoUtils
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.livedata.NonStickyMutableLiveData
import com.gh.gamecenter.common.mvvm.Resource
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
@ -184,6 +184,7 @@ class GameDetailViewModel(
}
}
refreshWrongInstallStatus()
filterGameTags(data)
filterBlockedContent(data)
replaceWithMirrorInfoIfNeeded(data)
@ -256,6 +257,14 @@ class GameDetailViewModel(
}
}
fun refreshWrongInstallStatus() {
val packageNameSet = hashSetOf<String>()
game?.getApk()?.forEach {
packageNameSet.add(it.packageName)
}
PackageHelper.refreshWrongInstallStatus(packageNameSet)
}
private fun replaceWithMirrorInfoIfNeeded(data: NewGameDetailEntity) {
// 获取镜像相关数据,不存在时不替换
val mirrorData = getMirrorData(data) ?: return

View File

@ -222,9 +222,9 @@ class CustomPageData(
get() = _adIconActive ?: false
var games: List<GameEntity>
get() = RegionSettingHelper.filterGame(_games)
get() = filteredGames ?: RegionSettingHelper.filterGame(_games).also { filteredGames = it }
set(value) {
_games = value
_games = RegionSettingHelper.filterGame(value).also { filteredGames = it }
}
val user: User
@ -246,6 +246,9 @@ class CustomPageData(
val styleChinese: String
get() = CustomPageItem.subjectTypeToComponentStyle[style] ?: ""
@IgnoredOnParcel
private var filteredGames: List<GameEntity>? = null
data class User(
@SerializedName("_id")
private val _id: String? = null,

View File

@ -168,10 +168,8 @@ class CustomHomeItemGameTestV2ViewHolder(
} else {
RIGHT_TEXT_MORE
}
if (tvRight.text.isBlank()) {
tvRight.text = rightText
}
tvRight.text = rightText
tvRight.setOnClickListener {
if (data.rightTop.text == ALL) {
NewFlatLogUtils.logGameTestV2MoreClick(rightText, "自定义页面")

View File

@ -36,7 +36,7 @@ public class DataCollectionManager {
}
public static void onEvent(Context context, String type, Map<String, Object> map, boolean isUpload) {
AppExecutor.getIoExecutor().execute(() -> {
AppExecutor.getLogExecutor().execute(() -> {
map.put("createdOn", Utils.getTime(context));
if (isUpload) {
DataCollectionManager.getInstance().realTimeUpload(type, map);
@ -179,11 +179,22 @@ public class DataCollectionManager {
}
}
public static void onEvent(Context context, String type, Map<String, Object> map) {
AppExecutor.getLogExecutor().execute(() -> {
map.put("createdOn", Utils.getTime(context));
if ("news".equals(type) || "download".equals(type) || "search".equals(type)) {
DataCollectionManager.getInstance().realTimeUpload(type, map);
return;
}
onEvent(type, new JSONObject(map).toString(), true);
});
}
/*
* 统计点击数据
*/
public void statClickData() {
AppExecutor.getIoExecutor().execute(() -> {
AppExecutor.getLogExecutor().execute(() -> {
List<DataCollectionInfo> list = dao.getClickData();
if (list != null && !list.isEmpty()) {
List<String> ids = new ArrayList<>();
@ -206,17 +217,6 @@ public class DataCollectionManager {
});
}
public static void onEvent(Context context, String type, Map<String, Object> map) {
AppExecutor.getIoExecutor().execute(() -> {
map.put("createdOn", Utils.getTime(context));
if ("news".equals(type) || "download".equals(type) || "search".equals(type)) {
DataCollectionManager.getInstance().realTimeUpload(type, map);
return;
}
onEvent(type, new JSONObject(map).toString(), true);
});
}
private static class SingletonHolder {
private static final DataCollectionManager INSTANCE = new DataCollectionManager();
}

View File

@ -7,6 +7,7 @@ import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.entity.GameUpdateEntity
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
/**
* todo 整理部分与[PackageUtils]冲突的方法
@ -15,14 +16,13 @@ object PackagesManager {
// 存在网络延迟
private var mUpdateAndPluginList = ArrayList<GameUpdateEntity>()
private val mInstalledList = ArrayList<GameInstall>()
private var mInstalledList = ArrayList<GameInstall>()
// 实时更新(已安装到本地的包名列表,不包括畅玩游戏)
private val mInstalledPkgList = Collections.synchronizedList(ArrayList<String>())
private var mInstalledPkgSet = HashSet<String>()
fun initInstallPkgList(list: List<String>) {
mInstalledPkgList.clear()
mInstalledPkgList.addAll(list)
fun initInstallPkgSet(set: Set<String>) {
mInstalledPkgSet = HashSet(set)
}
/**
@ -41,8 +41,7 @@ object PackagesManager {
* @param list 已安装列表数据
*/
fun initGameInstall(list: ArrayList<GameInstall>) {
mInstalledList.clear()
mInstalledList.addAll(list)
mInstalledList = ArrayList(list)
}
/**
@ -86,9 +85,11 @@ object PackagesManager {
return false
}
return mInstalledPkgList.contains(packageName)
return mInstalledPkgSet.contains(packageName)
}
fun getInstalledSet(): MutableSet<String> = HashSet(mInstalledPkgSet)
/**
* 根据包名版本号判断是否已安装相应版本的应用
*

View File

@ -1,12 +1,14 @@
package com.gh.gamecenter.packagehelper
import android.annotation.SuppressLint
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.common.utils.tryWithDefaultCatch
import com.gh.gamecenter.entity.PackageFilter
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.room.AppDatabase
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
object PackageFilterManager {
@ -77,11 +79,20 @@ object PackageFilterManager {
if (appendOnly) {
mPendingPackageNameSet.addAll(packageList)
} else {
if (exception is retrofit2.HttpException && exception.code() == 403) {
// 403 代表 key 过期,需要重新获取
callbackClosure?.invoke(arrayListOf())
return
}
// 接口访问失败时从数据库读取上一次缓存的已安装收录列表进行更新
val packageEntityList =
AppDatabase.getInstance().packageFilterDao().getAllPackageName()
for (packageEntity in packageEntityList) {
mValidPackageNameSet.add(packageEntity.packageName)
// 依然为已安装状态才加入到有效包名列表中
if (PackageUtils.isInstalled(HaloApp.getInstance(), packageEntity.packageName)) {
mValidPackageNameSet.add(packageEntity.packageName)
}
}
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))

View File

@ -16,23 +16,27 @@ import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.ObservableUtil
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.secondOrNull
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
import org.json.JSONObject
import java.util.*
import kotlin.collections.HashSet
/**
* 该类存储的是已安装的所有游戏(助手后台已收录的)和所有更新(包括插件化)数据
@ -54,10 +58,12 @@ object PackageRepository {
private const val LAST_UPLOAD_APPLIST_TIME = "last_upload_applist_time"
private const val PAGE_SIZE = 50
private val mInstalledPkgList = Collections.synchronizedList(ArrayList<String>())
private val mInstalledPkgSet = Collections.synchronizedSet(HashSet<String>())
@Volatile
private var mIsInitialisingData = false
var installedPkgRefreshed = false
var vaPkgRefreshed = false
val gameUpdateLiveData = MutableLiveData<List<GameUpdateEntity>>()
val gameInstalledLiveData = MutableLiveData<List<GameInstall>>()
@ -82,11 +88,14 @@ object PackageRepository {
if (mIsInitialisingData) return
mIsInitialisingData = true
installedPkgRefreshed = false
vaPkgRefreshed = false
runOnIoThread {
if (gameInstalled.isNotEmpty()) gameInstalled.clear()
if (mInstalledGameList.isNotEmpty()) mInstalledGameList.clear()
if (gameUpdate.isNotEmpty()) gameUpdate.clear()
if (mInstalledPkgList.isNotEmpty()) mInstalledPkgList.clear()
if (mInstalledPkgSet.isNotEmpty()) mInstalledPkgSet.clear()
val list = PackageUtils.getAllPackageName(mApplication)
@ -95,13 +104,38 @@ object PackageRepository {
initFilterPackage(list) { filteredList ->
mIsInitialisingData = false
mInstalledPkgList.addAll(filteredList)
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
loadInstalledGameDigestAndNotifyData(filteredList)
loadInstalledGameDigestAndNotifyData(filteredList) {
installedPkgRefreshed = true
mIsInitialisingData = !(installedPkgRefreshed && vaPkgRefreshed)
}
}
loadGhzsUpdate()
// 畅玩游戏更新
var allGames = VHelper.getAllVGameSnapshots()
if (allGames.isNullOrEmpty()) {
VHelper.refreshVGameSnapshot()
allGames = VHelper.getAllVGameSnapshots()
}
val allGamePkgNames = allGames.map { it.packageName }.toArrayList()
if (allGamePkgNames.isNotEmpty()) {
notifyInstallPkgData()
updateFilterPackage(allGamePkgNames) {
Utils.log("xxx", "PackageRepository::filteredList::${allGamePkgNames}")
loadInstalledGameDigestAndNotifyData(
filteredList = allGamePkgNames,
onWorkerThreadOnly = false,
isVGame = true
) {
vaPkgRefreshed = true
mIsInitialisingData = !(installedPkgRefreshed && vaPkgRefreshed)
}
}
}
}
}
@ -134,7 +168,8 @@ object PackageRepository {
PackageUtils.getGhVersionName(),
PackageUtils.getGhVersionCode(),
HaloApp.getInstance().channel,
Build.VERSION.SDK_INT)
Build.VERSION.SDK_INT
)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<AppEntity>() {
@ -203,12 +238,15 @@ object PackageRepository {
* @param filteredList 已安装的游戏包名集合 (仅已收录部分)
* @param onWorkerThreadOnly 是否在工作线程执行
* @param isVGame 包名列表是否为畅玩游戏
* @param updateInstallStatus 更新安装状态 (通过 EventBus 来进行)
*/
@SuppressLint("CheckResult")
private fun loadInstalledGameDigestAndNotifyData(
filteredList: ArrayList<String>,
onWorkerThreadOnly: Boolean = false,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
loadFinishCallback: (() -> Unit)? = null
) {
var isNotifyUpdate = false
val maxPageCount = (filteredList.size / PAGE_SIZE) + 1
@ -217,6 +255,7 @@ object PackageRepository {
val latch = ObservableUtil.latch(maxPageCount, {
if (isNotifyUpdate || gameUpdateLiveData.value == null) notifyGameUpdateData()
notifyGameInstallData()
loadFinishCallback?.invoke()
}, Any())
while (++page <= maxPageCount) {
@ -243,9 +282,7 @@ object PackageRepository {
for (game in validGames) {
if (gh_id == null || gh_id == game.id) {
gameInstalled.add(
GameInstall.transformGameInstall(game, pkgName, isVGame)
)
gameInstalled.add(GameInstall.transformGameInstall(game, pkgName, isVGame))
mInstalledGameList.add(game)
val isCanPluggable = checkGamePlugin(game, pkgName)
val isCanUpdate = checkGameUpdate(game, isVGame)
@ -253,6 +290,10 @@ object PackageRepository {
if (!isNotifyUpdate && isCanUpdate || isCanPluggable) {
isNotifyUpdate = true
}
if (updateInstallStatus) {
EventBus.getDefault().post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
}
}
}
}
@ -400,14 +441,18 @@ object PackageRepository {
* 新增已安装的游戏
* @param pkgName 已安装的游戏包名
*/
fun addInstalledGame(pkgName: String) {
mInstalledPkgList.add(pkgName)
fun addInstalledGame(pkgName: String, updateInstallStatus: Boolean = false) {
mInstalledPkgSet.add(pkgName)
notifyInstallPkgData()
val list = arrayListOf(pkgName)
updateFilterPackage(list) {
loadInstalledGameDigestAndNotifyData(list, true)
loadInstalledGameDigestAndNotifyData(
filteredList = list,
onWorkerThreadOnly = true,
updateInstallStatus = updateInstallStatus
)
}
changeRecentVaPlayed()
}
@ -415,18 +460,29 @@ object PackageRepository {
/**
* 批量新增已安装的游戏数量
* @param pkgNameList 数组列表
* @param isVGame 是否为畅玩游戏
* @param updateInstallStatus 是否更新安装状态
*/
fun addInstalledGames(pkgNameList: ArrayList<String>, isVGame: Boolean = false) {
fun addInstalledGames(pkgNameList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
) {
// 畅玩游戏不添加至本地的已安装包名列表中
if (!isVGame) {
mInstalledPkgList.addAll(pkgNameList)
mInstalledPkgSet.addAll(pkgNameList)
} else {
changeRecentVaPlayed()
}
notifyInstallPkgData()
updateFilterPackage(pkgNameList) {
loadInstalledGameDigestAndNotifyData(pkgNameList, true, isVGame)
loadInstalledGameDigestAndNotifyData(
filteredList = pkgNameList,
onWorkerThreadOnly = true,
isVGame = isVGame,
updateInstallStatus = updateInstallStatus
)
}
changeRecentVaPlayed()
}
/**
@ -435,9 +491,8 @@ object PackageRepository {
* @param isVGame 是否来自于畅玩游戏
*/
fun addUninstalledGame(pkgName: String, isVGame: Boolean) {
// TODO 检查为什么会有两个相同的包名添加到 mInstalledPkgList 里
if (!isVGame && mInstalledPkgList.isNotEmpty()) {
mInstalledPkgList.removeAll { it == pkgName }
if (!isVGame && mInstalledPkgSet.isNotEmpty()) {
mInstalledPkgSet.remove(pkgName)
}
// 尝试从临时的当前版本列表里移除已卸载的条目
tryCatchInRelease {
@ -493,7 +548,14 @@ object PackageRepository {
}
private fun notifyInstallPkgData() {
PackagesManager.initInstallPkgList(mInstalledPkgList)
val pkgSet = hashSetOf<String>()
val iterator = mInstalledPkgSet.iterator()
while (iterator.hasNext()) {
pkgSet.add(iterator.next())
}
PackagesManager.initInstallPkgSet(pkgSet)
}
}

View File

@ -3,9 +3,10 @@ package com.gh.gamecenter.packagehelper
import android.app.Application
import android.text.TextUtils
import androidx.lifecycle.*
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import kotlin.collections.set
class PackageViewModel(
@ -79,6 +80,7 @@ class PackageViewModel(
if (mRepository.gameInstalled.size == 0
|| PackageFilterManager.hasPendingPackage()
) {
Utils.log("xxx", "PackageViewModel call initData")
mRepository.initData()
}
}

View File

@ -60,7 +60,7 @@ import com.gh.gamecenter.room.AppDatabase
import com.gh.gamecenter.setting.SettingBridge
import com.gh.gamecenter.wrapper.MainWrapperFragment
import com.gh.gamecenter.wrapper.MainWrapperViewModel
import com.gh.vspace.IExternalGamesUsage
import com.gh.vspace.ITestCase
import com.google.android.material.appbar.AppBarLayout
import com.halo.assistant.HaloApp
import com.jakewharton.rxbinding2.view.RxView
@ -695,7 +695,11 @@ class HaloPersonalFragment : BaseLazyFragment() {
(try {
Class.forName("com.gh.vspace.ExternalGameUsage")?.newInstance()
} catch (e: Exception) {
} as? IExternalGamesUsage)?.addInstallExternalGameButton(mStubBinding.otherItems)
} as? ITestCase)?.apply {
addInstallExternalGameButton(mStubBinding.otherItems)
addInstallPluginButton(mStubBinding.otherItems)
addInstallPlugin32Button(mStubBinding.otherItems)
}
}
mStubBinding.settingItem.run {

View File

@ -14,10 +14,10 @@ class PkgConfigProviderImpl: IPkgConfigProvider {
override fun getPkgConfig(): ArrayList<String> {
val configList = arrayListOf<String>()
val sdk = when (BuildConfig.FLAVOR) {
"tea" -> "头条"
"kuaishou" -> "快手"
"gdt" -> "广点通"
val sdk = when {
BuildConfig.FLAVOR.contains("tea") -> "头条"
BuildConfig.FLAVOR.contains("kuaishou") -> "快手"
BuildConfig.FLAVOR.contains("gdt") -> "广点通"
else -> "普通包"
} + " " + BuildConfig.SDK_VERSION + " (仅头条有效)"
@ -28,7 +28,7 @@ class PkgConfigProviderImpl: IPkgConfigProvider {
}
configList.add(sdk)
if (BuildConfig.FLAVOR == "kuaishou") {
if (BuildConfig.FLAVOR.contains("kuaishou")) {
configList.add("appid: ${BuildConfig.SDK_APP_ID.ifEmpty { "81537" }}")
configList.add("appname: ${BuildConfig.SDK_APP_NAME.ifEmpty { "guanghuanzhushou_1"}}")
}

View File

@ -16,11 +16,11 @@ import com.gh.gamecenter.common.utils.NewFlatLogUtils;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.entity.NewApiSettingsEntity;
import com.gh.gamecenter.eventbus.EBPackage;
import com.gh.gamecenter.feature.entity.GameInstall;
import com.gh.gamecenter.install.InstallService;
import com.gh.gamecenter.manager.PackagesManager;
import com.gh.ndownload.NDownloadService;
import com.halo.assistant.HaloApp;
import com.lightgame.download.DownloadEntity;
import com.lightgame.utils.Utils;
@ -29,21 +29,26 @@ import org.greenrobot.eventbus.EventBus;
import java.util.Locale;
/**
* 监听应用安装和卸载的广播
*
* @author LGT 黄壮华
* 监听安装包变更
*/
public class InstallAndUninstallReceiver extends BroadcastReceiver {
public class PackageChangeBroadcastReceiver extends BroadcastReceiver {
private static final String TAG = "PackageChangeBroadcastReceiver";
private static final String webviewPackageName = "com.google.android.webview";
private final NewApiSettingsEntity.PackageObserveActions mActions;
public PackageChangeBroadcastReceiver(NewApiSettingsEntity.PackageObserveActions actions) {
mActions = actions;
}
@Override
public void onReceive(Context context, Intent intent) {
PackageUtils.dumpInstalledListCache();
PackageHelper.INSTANCE.dumpInstalledListCache();
ExtensionsKt.doOnMainProcessOnly(() -> {
Utils.log("InstallAndUninstallReceiver:: onReceive->" + intent.getAction() + "==" + intent.getDataString());
Utils.log(TAG, "onReceive->" + intent.getAction() + "==" + intent.getDataString());
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU
&& Build.MANUFACTURER.toLowerCase(Locale.CHINA).contains("xiaomi")) {
@ -53,10 +58,10 @@ public class InstallAndUninstallReceiver extends BroadcastReceiver {
}
// 接收安装广播
if (intent.getAction().equals(Intent.ACTION_PACKAGE_ADDED)) {
if (intent.getAction().equals(mActions.getAdd())) {
String packageName = intent.getDataString();
packageName = packageName.substring(packageName.indexOf(":") + 1);
Utils.log("安装了:" + packageName + "包名的程序");
Utils.log(TAG, "安装了:" + packageName + "包名的程序");
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName);
String gameId = downloadEntity != null && downloadEntity.getGameId() != null ? downloadEntity.getGameId() : "";
String gameName = downloadEntity != null && downloadEntity.getName() != null ? downloadEntity.getName() : "";
@ -84,10 +89,10 @@ public class InstallAndUninstallReceiver extends BroadcastReceiver {
}
// 接收卸载广播
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REMOVED)) {
if (intent.getAction().equals(mActions.getRem())) {
String packageName = intent.getDataString();
packageName = packageName.substring(packageName.indexOf(":") + 1);
Utils.log("卸载了:" + packageName + "包名的程序");
Utils.log(TAG, "卸载了:" + packageName + "包名的程序");
GameInstall install = PackagesManager.getInstalledData(packageName);
String gameId = install != null && install.getId() != null ? install.getId() : "";
String gameName = install != null && install.getName() != null ? install.getName() : "";
@ -107,10 +112,10 @@ public class InstallAndUninstallReceiver extends BroadcastReceiver {
}
// 接收替换广播
if (intent.getAction().equals(Intent.ACTION_PACKAGE_REPLACED)) {
if (intent.getAction().equals(mActions.getRep())) {
String packageName = intent.getData().getSchemeSpecificPart();
packageName = packageName.substring(packageName.indexOf(":") + 1);
Utils.log("替换了:" + packageName + "包名的程序");
Utils.log(TAG, "替换了:" + packageName + "包名的程序");
String versionName = PackageUtils.getVersionNameByPackageName(packageName);
EBPackage updateEb = new EBPackage(EBPackage.TYPE_REPLACED, packageName, versionName);

View File

@ -93,6 +93,7 @@ import com.gh.gamecenter.entity.VideoDraftEntity;
import com.gh.gamecenter.entity.VideoEntity;
import com.gh.gamecenter.entity.VideoTagEntity;
import com.gh.gamecenter.entity.VoteEntity;
import com.gh.gamecenter.entity.WhitePackageListEntity;
import com.gh.gamecenter.feature.entity.AnswerEntity;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.ArticleDraftEntity;
@ -784,6 +785,9 @@ public interface ApiService {
@Query("systemVersion") int systemVersion,
@Query("ghVersion") String ghVersion);
@GET("/settings/game_installed/whitelist")
Single<WhitePackageListEntity> getInstallWhitelist();
/**
* 给一个回答新增评论
*/

View File

@ -1,9 +1,11 @@
package com.gh.gamecenter.retrofit.service
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.entity.VNewSetting
import com.gh.gamecenter.entity.VSetting
import io.reactivex.Single
import retrofit2.http.GET
import retrofit2.http.Header
import retrofit2.http.Query
interface VApiService {
@ -24,4 +26,16 @@ interface VApiService {
@GET("setting")
fun getSettings(@Query("version") version: String?, @Query("android") androidSdkVersion: Int): Single<VSetting>
@GET("new/setting")
fun getNewSettings(
@Query("version") version: String?,
@Query("android") androidSdkVersion: Int,
): Single<VNewSetting>
@GET("new/upgrade")
fun getNewPackageUpdate(
@Query("version") version: String?,
@Query("va_version") vaVersion: String?,
@Query("channel") channel: String,
): Single<AppEntity>
}

View File

@ -148,6 +148,8 @@ class SearchGameIndexFragment : ListFragment<GameEntity, SearchGameResultViewMod
super.onResume()
DownloadManager.getInstance().addObserver(dataWatcher)
mListViewModel.refreshWrongInstallStatus()
}
override fun onPause() {

View File

@ -331,6 +331,8 @@ open class SearchGameResultFragment : ListFragment<GameEntity, SearchGameResultV
override fun onResume() {
super.onResume()
DownloadManager.getInstance().addObserver(dataWatcher)
mListViewModel.refreshWrongInstallStatus()
}
override fun onPause() {

View File

@ -6,6 +6,7 @@ import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.ad.AdDelegateHelper
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.baselist.ListViewModel
@ -45,6 +46,8 @@ class SearchGameResultViewModel(
private var mAdPositionSet: HashSet<Int>? = null
private val mAdGameMap = ConcurrentHashMap<String, List<GameEntity>>()
private var mPackageNameSet = hashSetOf<String>()
fun updateSearchKeyWithType(searchKey: String, searchType: String) {
mSearchKey = searchKey
mSearchType = searchType
@ -67,6 +70,18 @@ class SearchGameResultViewModel(
SearchItemData(game = game, isFirst = index == 0)
}
)
mPackageNameSet = hashSetOf()
// 添加所有出现的游戏包名
list.forEach {
it.getApk().forEach { apk ->
mPackageNameSet.add(apk.packageName)
}
}
refreshWrongInstallStatus()
repository.getSearchSubject(mSearchKey, mPage)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
@ -167,6 +182,12 @@ class SearchGameResultViewModel(
}
}
fun refreshWrongInstallStatus() {
if (mPackageNameSet.isNotEmpty()) {
PackageHelper.refreshWrongInstallStatus(mPackageNameSet)
}
}
private fun decorateListWithThirdPartyAdOnly(
decoratedItemDataList: ArrayList<SearchItemData>,
thirdPartyAdList: List<AdConfig>,

View File

@ -5,8 +5,8 @@ import android.app.Application
import android.content.pm.PackageManager
import com.gh.common.constant.Config
import com.gh.common.simulator.SimulatorGameManager
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.common.utils.NetworkUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.feature.entity.ApkEntity
import com.gh.gamecenter.feature.entity.SimulatorEntity
@ -56,7 +56,7 @@ class SimulatorManagementViewModel(application: Application) :
return Single.create<List<SimulatorEntity>> { emitter ->
val simulatorEntityList = ArrayList<SimulatorEntity>()
val allInstalledPackages =
PackageUtils.getInstalledPackages(HaloApp.getInstance().application, PackageManager.GET_ACTIVITIES)
PackageHelper.getInstalledPackages(HaloApp.getInstance().application, PackageManager.GET_ACTIVITIES)
allInstalledPackages.forEach {
if (it.packageName.contains("com.gh")) {
val activityInfo =

View File

@ -3,12 +3,15 @@ package com.gh.gamecenter.wrapper
import android.graphics.PorterDuff
import android.net.Uri
import android.os.Bundle
import android.text.SpannableStringBuilder
import android.text.TextUtils
import android.view.View
import androidx.annotation.ColorRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.color
import androidx.core.view.doOnNextLayout
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.ShellActivity
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
@ -77,6 +80,15 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
mViewModel?.realNameInfoUpdateLiveData?.observe(viewLifecycleOwner) {
updateRealNameErrorContainer()
}
PackageHelper.installedPackageApiSwitchStatusLiveData.observe(viewLifecycleOwner) {
if (it) {
showInstallApiHintView(mBinding)
SensorsBridge.trackInstalledListPermissionsDialogShow()
} else {
mBinding.installApiContainer.visibility = View.GONE
}
}
}
fun setCurrentItem(page: Int) {
@ -255,6 +267,39 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
}
}
private fun showInstallApiHintView(binding: FragmentMainBinding) {
val contentText = SpannableStringBuilder()
.color(R.color.text_theme.toColor(requireContext())) { append("您未授予已安装列表权限,可能") }
.color(R.color.secondary_red.toColor(requireContext())) { append("导致无法安装及更新") }
.color(R.color.text_theme.toColor(requireContext())) { append("建议开启权限!") }
binding.installApiContainer.visibility = View.VISIBLE
binding.installApiContainer.setOnClickListener {
// do nothing
}
binding.installApiCloseIv.enlargeTouchArea()
binding.installApiContentTv.text = contentText
binding.installApiCloseIv.setOnClickListener {
binding.installApiContainer.visibility = View.GONE
SensorsBridge.trackInstalledListPermissionsClick("关闭")
}
binding.installApiBtn.setOnClickListener {
SensorsBridge.trackInstalledListPermissionsClick("去开启")
val callbackClosure: (Boolean) -> Unit = { isGranted ->
if (isGranted) {
binding.installApiContainer.visibility = View.GONE
}
}
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity = requireActivity(),
ignorePermanentlyDenied = false,
resultClosure = callbackClosure
)
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
changeBottomTabStyle(mBinding.viewPager.currentItem)

View File

@ -65,6 +65,7 @@ import com.gh.gamecenter.message.MessageUnreadViewModel
import com.gh.gamecenter.packagehelper.PackageViewModel
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperFragment.Style
import com.gh.vspace.VHelper
import com.google.android.material.appbar.AppBarLayout
import com.google.android.material.tabs.TabLayout
import com.halo.assistant.HaloApp
@ -1533,6 +1534,7 @@ class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbar
*/
override fun logTabSelected(tabEntity: MultiTabNav.LinkMultiTabNav, position: Int) {
super.logTabSelected(tabEntity, position)
VHelper.mainEntranceMap[mBottomTabName] = tabEntity.name
LogUtils.logHomeTopTabClick(
tabEntity.name,
tabEntity.link?.type,

View File

@ -314,12 +314,19 @@ object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
}
// 记录重定向时获取到的最终格式,因为缓存问题,原格式与实际下载的格式有区别
val realFormat = connection?.getHeaderField("Halo-Format")
if (!realFormat.isNullOrEmpty() && downloadEntity.format != realFormat) {
Utils.log(TAG, "获取到新的文件格式, $realFormat")
try {
val realFormat = connection?.getHeaderField("Halo-Format")
if (!realFormat.isNullOrEmpty() && downloadEntity.format != realFormat) {
Utils.log(TAG, "获取到新的文件格式, $realFormat")
downloadEntity.addMetaExtra(REAL_FORMAT, realFormat)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
downloadEntity.addMetaExtra(REAL_FORMAT, realFormat)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
} catch (e: NullPointerException) {
// getHeaderField 的时候内部可能会触发 NullPointerException原因未知
// 由于这里的异常不会影响正常下载,所以直接打印异常,不做处理
// 具体可见 https://sentry.shanqu.cc/organizations/lightgame/issues/371082/
e.printStackTrace()
}
NDataChanger.notifyDataChanged(downloadEntity)

View File

@ -0,0 +1,21 @@
package com.gh.vspace;
public class Constants {
// intent extra key
public static final String INTENT_EXTRA_KEY_PAGE_SOURCE = "page_source";
// 畅玩启动事件来源
public static final String PAGE_SOURCE_SEARCH = "搜索";
public static final String PAGE_SOURCE_RANK = "排行榜";
public static final String PAGE_SOURCE_RECENTLY_PLAYING = "最近在玩";
public static final String PAGE_SOURCE_CW_MANAGER = "畅玩管理";
public static final String PAGE_SOURCE_GAME_DETAIL = "游戏详情";
public static final String PAGE_SOURCE_TOPIC = "专题";
public static final String PAGE_SOURCE_NON_MAIN = "二级自定义页面";
public static final String PAGE_SOURCE_OTHERS = "其他";
public static final String PAGE_SOURCE_MY_GAME = "我的游戏";
public static final String PAGE_SOURCE_SHORTCUT = "桌面快捷启动";
}

View File

@ -4,8 +4,10 @@ import android.view.ViewGroup;
import androidx.annotation.NonNull;
public interface IExternalGamesUsage {
public interface ITestCase {
void addInstallExternalGameButton(@NonNull ViewGroup viewParent);
void addInstallPluginButton(@NonNull ViewGroup viewParent);
void addInstallPlugin32Button(@NonNull ViewGroup viewParent);
}

View File

@ -2,8 +2,11 @@ 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
@ -22,7 +25,11 @@ 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
@ -43,6 +50,31 @@ object VArchiveHelper {
// 本地存档存放的位置
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) {
@ -162,8 +194,26 @@ object VArchiveHelper {
mSaveArchiveListener = saveArchiveListener
// 记录最近一次创建的存档文件
mLatestArchiveFile = archiveFile
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
context.startActivity(intent)
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)
}
})
}
}
}
/**
@ -179,8 +229,43 @@ object VArchiveHelper {
applyArchiveListener: ((String, Boolean) -> Unit)? = null
) {
mApplyArchiveListener = applyArchiveListener
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
context.startActivity(intent)
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)
}
})
}
/**

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,307 @@
package com.gh.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD
import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE
import com.gh.common.util.*
import com.gh.common.util.NewFlatLogUtils
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogVspace32NewBinding
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.view.DownloadButton
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.AppManager
import java.io.File
class VSpace32NewDialogFragment : BaseDraggableDialogFragment() {
private var mAppEntity: AppEntity? = null
private var mGameId: String = ""
private var mGameName: String = ""
private val mDownloadUrl by lazy { mAppEntity?.url ?: "" }
private val mBinding by lazy { DialogVspace32NewBinding.inflate(layoutInflater) }
private var mIsLogInstallShow = false
private var mIsLogAutoInstallClick = false
private var mIsClickDownloadThisTime = false // 是否本次弹出Dialog点击的下载按钮
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.url == mDownloadUrl && isAdded) {
updateDownloadButton(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
onDataChanged(downloadEntity)
}
}
override fun getRootView(): View = mBinding.root
override fun getDragCloseView(): View = mBinding.dragClose
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAppEntity = arguments?.get(KEY_APP_ENTITY_32) as AppEntity
mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: ""
mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: ""
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mViewModel = viewModelProvider<VSpaceDialogFragment.VSpaceDialogViewModel>()
mViewModel.packageLiveData.observe(this) {
if (it.packageName == VHelper.VSPACE_32BIT_NEW_PACKAGENAME) {
dismissAllowingStateLoss()
}
}
val downloadSnapshot = DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(VHelper.VSPACE_32BIT_NEW_PACKAGENAME)
mBinding.downloadBtn.text = if (downloadSnapshot?.status == done) "安装" else "下载"
mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
mBinding.descTv.text = "$mGameName》需安装完整的畅玩服务组件,安装后即可给您带来急速的畅玩体验~"
mBinding.privacyPolicyTv.setOnClickListener {
NewFlatLogUtils.logHaloFunEvent("halo_fun_download_dialog_privacy_click")
DirectUtils.directToWebView(requireContext(), Constants.SMOOTH_GAME_PRIVACY_POLICY_ADDRESS)
}
if (downloadSnapshot?.status == done) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_show")
SensorsBridge.trackEvent("HaloFunExpandInstallDialogShow")
mIsLogInstallShow = true
} else {
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_download_tip_dialog_show", mGameId, mGameName)
SensorsBridge.trackEvent("HaloFunExpandDownloadDialogShow", "game_id", mGameId, "game_name", mGameName)
}
mBinding.downloadBtn.setOnClickListener {
if (downloadSnapshot?.status == done) {
if (!File(downloadSnapshot.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadSnapshot.url)
DownloadManager.getInstance().add(downloadSnapshot)
} else {
PackageInstaller.install(requireContext(), downloadSnapshot)
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
}
} else {
val downloadEntity = DownloadEntity()
val name = "畅玩游戏助手V" + mAppEntity?.version
val downloadId = PackageInstaller.createDownloadId(name)
downloadEntity.url = mDownloadUrl
downloadEntity.name = name
downloadEntity.platform = "官方版"
downloadEntity.gameId = Constants.HALO_FUN_NEW_32_GAME_ID
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk")
downloadEntity.packageName = VHelper.VSPACE_32BIT_NEW_PACKAGENAME
downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name)
gameEntity.gameVersion = mAppEntity?.version ?: ""
// 确定下载类型
val downloadType =
if (arguments?.getBoolean(VSpaceDialogFragment.KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD
downloadEntity.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity,
downloadEntity.platform,
null,
downloadType
).toJson()
mIsClickDownloadThisTime = true
AppExecutor.uiExecutor.executeWithDelay({
DownloadManager.getInstance().cancel(mDownloadUrl)
DownloadManager.getInstance().add(downloadEntity)
}, 200)
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始")
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_download_tip_dialog_click", mGameId, mGameName)
SensorsBridge.trackEvent("HaloFunExpandDownloadDialogDownloadClick")
}
}
}
override fun onStart() {
super.onStart()
DownloadManager.getInstance().addObserver(mDataWatcher)
// 上面监听安装包名变化的 LiveData 监听有可能被冲掉了
// 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题
if (PackageUtils.isInstalledFromAllPackage(requireContext(), VHelper.VSPACE_32BIT_NEW_PACKAGENAME)) {
dismissAllowingStateLoss()
}
}
override fun onStop() {
super.onStop()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
private fun updateDownloadButton(downloadEntity: DownloadEntity) {
val downloadBtn = mBinding.downloadBtn
downloadBtn.progress = (downloadEntity.progress * 10).toInt()
when (downloadEntity.status) {
downloading -> {
downloadBtn.setText(R.string.pause)
downloadBtn.progress = (downloadEntity.percent * 10).toInt()
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().pause(mDownloadUrl)
}
}
pause -> {
downloadBtn.setText(R.string.resume)
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
}
}
overflow,
timeout,
neterror,
diskisfull,
waiting,
subscribe -> {
downloadBtn.setText(R.string.waiting)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, false)
}
}
done -> {
downloadBtn.setText(R.string.install)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
if (!mIsLogInstallShow) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_show")
NewFlatLogUtils.logHaloFunInstallTipDialogShow("32位")
SensorsBridge.trackEvent("HaloFunExpandInstallDialogShow")
mIsLogInstallShow = true
}
val isVSpace32DownloadOnly =
downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.VSPACE_32_DOWNLOAD_ONLY
val isAutoInstall = SPUtils.getBoolean(Constants.SP_AUTO_INSTALL, true)
if (!isVSpace32DownloadOnly && isAutoInstall && !mIsLogAutoInstallClick && mIsClickDownloadThisTime) {
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
mIsLogAutoInstallClick = true
}
downloadBtn.setOnClickListener {
if (!File(downloadEntity.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadEntity.url)
DownloadManager.getInstance().add(downloadEntity)
} else {
PackageInstaller.install(requireContext(), downloadEntity)
NewFlatLogUtils.logHaloFunEvent("halo_fun_32_install_tip_dialog_click")
SensorsBridge.trackEvent("HaloFunExpandInstallButtonClick")
}
}
}
cancel,
hijack,
notfound,
uncertificated,
unqualified -> {
downloadBtn.text = "下载"
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
}
}
else -> {
// do nothing
}
}
}
companion object {
const val KEY_APP_ENTITY_32 = "app_entity_32"
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
gameId: String = "",
gameName: String = ""
) {
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
context
} else {
val currentActivity = AppManager.getInstance().currentActivity()
if (currentActivity is FragmentActivity) {
currentActivity
} else {
throw IllegalStateException("current activity context must be FragmentActivity")
}
}
// 防止重复弹出
if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return
val downloadDialog = VSpace32NewDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_APP_ENTITY_32, appEntity32)
putString(EntranceConsts.KEY_GAME_ID, gameId)
putString(EntranceConsts.KEY_GAME_NAME, gameName)
}
}
downloadDialog.show(
fragmentActivity.supportFragmentManager,
VSpace32NewDialogFragment::class.java.name
)
}
private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {
val fragments: List<Fragment> = fragmentActivity.supportFragmentManager.fragments
fragments.forEach { fragment ->
if (fragment is VSpace32NewDialogFragment) return true
}
return false
}
}
}

View File

@ -13,6 +13,7 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.Utils
class VSpaceLoadingFragment : BaseFragment<Any>() {
@ -87,11 +88,19 @@ class VSpaceLoadingFragment : BaseFragment<Any>() {
showFakeInstallationProgress(mBinding)
}
VHelper.getPackageInstalledLiveData().observe(viewLifecycleOwner) {
VHelper.mPackageInstalledLiveData.observe(viewLifecycleOwner) {
val currentPackageName = mGame?.getApk()?.firstOrNull()?.packageName
if (it == currentPackageName) {
VHelper.launch(requireActivity(), currentPackageName!!)
requireActivity().finish()
}
}
VHelper.mVSpaceLoadingCloseLiveData.observe(viewLifecycleOwner) {
if (it == true) {
activity?.let {
if(!it.isFinishing) {
it.finish()
}
}
}
}
}

View File

@ -0,0 +1,303 @@
package com.gh.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.ExposureUtils.DownloadType.DOWNLOAD
import com.gh.common.exposure.ExposureUtils.DownloadType.UPDATE
import com.gh.common.util.DataCollectionUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageInstaller
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogVspaceUpdate32NewBinding
import com.gh.gamecenter.entity.AppEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.AppManager
import java.io.File
class VSpaceUpdate32NewDialogFragment : BaseDialogFragment() {
private var mAppEntity: AppEntity? = null
private var mGameId: String = ""
private var mGameName: String = ""
private val mDownloadUrl by lazy { mAppEntity?.url ?: "" }
private val mBinding by lazy { DialogVspaceUpdate32NewBinding.inflate(layoutInflater) }
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.url == mDownloadUrl && isAdded) {
updateDownloadButton(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
onDataChanged(downloadEntity)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mAppEntity = arguments?.get(KEY_APP_ENTITY_32) as AppEntity
mGameId = arguments?.getString(EntranceConsts.KEY_GAME_ID) ?: ""
mGameName = arguments?.getString(EntranceConsts.KEY_GAME_NAME) ?: ""
}
@SuppressLint("ClickableViewAccessibility")
override fun onCreateView(
inflater: LayoutInflater,
container: ViewGroup?,
savedInstanceState: Bundle?
): View {
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
val mViewModel = viewModelProvider<VSpaceDialogFragment.VSpaceDialogViewModel>()
mViewModel.packageLiveData.observe(this) {
if (it.packageName == VHelper.VSPACE_32BIT_NEW_PACKAGENAME) {
dismissAllowingStateLoss()
}
}
val downloadSnapshot = DownloadManager.getInstance()
.getDownloadEntitySnapshotByPackageName(VHelper.VSPACE_32BIT_NEW_PACKAGENAME)
mBinding.downloadBtn.text = if (downloadSnapshot?.status == done) "安装" else "下载"
mBinding.downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
mBinding.contentTv.text = "$mGameName》需安装完整的畅玩服务组件,安装后即可给您带来急速的畅玩体验~"
mBinding.downloadBtn.setOnClickListener {
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
if (downloadSnapshot?.status == done) {
if (!File(downloadSnapshot.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadSnapshot.url)
DownloadManager.getInstance().add(downloadSnapshot)
} else {
PackageInstaller.install(requireContext(), downloadSnapshot)
}
} else {
val downloadEntity = DownloadEntity()
val name = "畅玩游戏助手V" + mAppEntity?.version
val downloadId = PackageInstaller.createDownloadId(name)
downloadEntity.url = mDownloadUrl
downloadEntity.name = name
downloadEntity.platform = "官方版"
downloadEntity.gameId = Constants.HALO_FUN_NEW_32_GAME_ID
downloadEntity.path = PackageInstaller.getDownloadPathWithId(downloadId, "apk")
downloadEntity.packageName = VHelper.VSPACE_32BIT_NEW_PACKAGENAME
downloadEntity.addMetaExtra(DownloadConfig.KEY_PROGRESS_CALLBACK_INTERVAL, "200")
val gameEntity = GameEntity(downloadEntity.gameId, downloadEntity.name)
gameEntity.gameVersion = mAppEntity?.version ?: ""
// 确定下载类型
val downloadType =
if (arguments?.getBoolean(VSpaceDialogFragment.KEY_IS_UPDATE) == true) UPDATE else DOWNLOAD
downloadEntity.exposureTrace = ExposureUtils.logADownloadExposureEvent(
gameEntity,
downloadEntity.platform,
null,
downloadType
).toJson()
AppExecutor.uiExecutor.executeWithDelay({
DownloadManager.getInstance().cancel(mDownloadUrl)
DownloadManager.getInstance().add(downloadEntity)
}, 200)
DataCollectionUtils.uploadDownload(HaloApp.getInstance(), downloadEntity, "开始")
}
}
}
override fun onStart() {
super.onStart()
DownloadManager.getInstance().addObserver(mDataWatcher)
// 上面监听安装包名变化的 LiveData 监听有可能被冲掉了
// 手动再检查一下安装状态,避免出现已安装但是没有 dismiss 弹窗的问题
if (PackageUtils.isInstalledFromAllPackage(requireContext(), VHelper.VSPACE_32BIT_NEW_PACKAGENAME)
&& mAppEntity?.version == PackageUtils.getVersionNameByPackageName(VHelper.VSPACE_32BIT_NEW_PACKAGENAME)
) {
dismissAllowingStateLoss()
}
}
override fun onStop() {
super.onStop()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
private fun updateDownloadButton(downloadEntity: DownloadEntity) {
val downloadBtn = mBinding.downloadBtn
downloadBtn.progress = (downloadEntity.progress * 10).toInt()
when (downloadEntity.status) {
downloading -> {
downloadBtn.setText(R.string.pause)
downloadBtn.progress = (downloadEntity.percent * 10).toInt()
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().pause(mDownloadUrl)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
pause -> {
downloadBtn.setText(R.string.resume)
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
overflow,
timeout,
neterror,
diskisfull,
waiting,
subscribe -> {
downloadBtn.setText(R.string.waiting)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, false)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
done -> {
downloadBtn.setText(R.string.install)
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
downloadBtn.setOnClickListener {
if (!File(downloadEntity.path).exists()) {
ToastUtils.toast("畅玩组件已损坏,即将重新下载")
DownloadManager.getInstance().cancel(downloadEntity.url)
DownloadManager.getInstance().add(downloadEntity)
} else {
PackageInstaller.install(requireContext(), downloadEntity)
NewFlatLogUtils.logHaloFun32DialogEvent(
"halo_fun_32_update_tip_dialog_click",
mGameId,
mGameName
)
}
}
}
cancel,
hijack,
notfound,
uncertificated,
unqualified -> {
downloadBtn.text = "下载"
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
downloadBtn.setOnClickListener {
DownloadManager.getInstance().resume(downloadEntity, true)
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_click", mGameId, mGameName)
}
}
else -> {
// do nothing
}
}
}
companion object {
const val KEY_APP_ENTITY_32 = "app_entity_32"
const val KEY_AUTO_DOWNLOAD = "auto_download"
const val KEY_IS_UPDATE = "is_update"
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
autoDownload: Boolean = false,
isUpdate: Boolean = false,
gameId: String = "",
gameName: String = ""
) {
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
context
} else {
val currentActivity = AppManager.getInstance().currentActivity()
if (currentActivity is FragmentActivity) {
currentActivity
} else {
throw IllegalStateException("current activity context must be FragmentActivity")
}
}
// 防止重复弹出
if (hasDialogDisplayedInCurrentActivity(fragmentActivity)) return
NewFlatLogUtils.logHaloFun32DialogEvent("halo_fun_32_update_tip_dialog_show", gameId, gameName)
val downloadDialog = VSpaceUpdate32NewDialogFragment().apply {
arguments = Bundle().apply {
putParcelable(KEY_APP_ENTITY_32, appEntity32)
putString(EntranceConsts.KEY_GAME_ID, gameId)
putString(EntranceConsts.KEY_GAME_NAME, gameName)
putBoolean(KEY_AUTO_DOWNLOAD, autoDownload)
putBoolean(KEY_IS_UPDATE, isUpdate)
}
}
downloadDialog.show(
fragmentActivity.supportFragmentManager,
VSpaceUpdate32NewDialogFragment::class.java.name
)
}
@JvmStatic
fun showDownloadDialog(
context: Context?,
appEntity32: AppEntity,
gameEntity: GameEntity?,
autoDownload: Boolean = false,
isUpdate: Boolean = false
) {
showDownloadDialog(
context,
appEntity32,
autoDownload,
isUpdate,
gameEntity?.id ?: "",
gameEntity?.name ?: ""
)
}
private fun hasDialogDisplayedInCurrentActivity(fragmentActivity: FragmentActivity): Boolean {
val fragments: List<Fragment> = fragmentActivity.supportFragmentManager.fragments
fragments.forEach { fragment ->
if (fragment is VSpaceUpdate32NewDialogFragment) return true
}
return false
}
}
}

View File

@ -320,7 +320,7 @@ class GAppsDownloadDialogFragment : BaseBottomDialogFragment<DialogGappsDownload
context?.let {
mIsInstalling = true
VHelper.batchInstall(it, installFileMap)
VHelper.batchInstall(it, installFileMap, mTriggerPackageName)
}
runOnUiThread {

View File

@ -20,6 +20,7 @@ import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lg.core.BuildConfig
import com.lg.vspace.VirtualAppManager
import com.muugi.shortcut.core.Executor
import com.muugi.shortcut.core.Shortcut
@ -89,8 +90,8 @@ class ShortcutManager private constructor() {
* 知道游戏id和包名创建桌面图标
*/
fun tryCreateShortCut(context: Context, gameId: String, gamePkg: String, result: OnCreateShortcutResult) {
VHelper.postOnInitialized {
val downloadEntity = VHelper.getVDownloadEntitySnapshot(gameId, gamePkg)
val downloadEntity = VHelper.getVDownloadEntitySnapshot(gameId, gamePkg)
val createShortcutLambda = {
runOnUiThread {
if (downloadEntity == null) {
result.failed()
@ -100,6 +101,11 @@ class ShortcutManager private constructor() {
}
}
}
if (VHelper.isInnerInstalled(gamePkg)) {
createShortcutLambda.invoke()
} else {
VHelper.postOnInitialized(createShortcutLambda)
}
}
@ -185,25 +191,7 @@ class ShortcutManager private constructor() {
}
}
/**
* 获取游戏快捷方式跳转Intent
* 直接启动游戏
*/
private fun getIntent(gameEntity: GameEntity): Intent {
val intent = mDelegateManager.getStartGameIntent(
gameEntity.getUniquePackageName(),
gameEntity.id,
gameEntity.name ?: "unknown",
gameEntity.icon ?: "",
MetaUtil.getBase64EncodedAndroidId(),
HaloApp.getInstance().gid,
com.gh.gamecenter.BuildConfig.VERSION_NAME,
HaloApp.getInstance().channel,
"", ""
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return intent
}
/**
* 获取游戏快捷方式跳转Intent

View File

@ -3,7 +3,6 @@ package com.halo.assistant;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
@ -39,14 +38,15 @@ import com.gh.common.util.DownloadNotificationHelper;
import com.gh.common.util.DownloadObserver;
import com.gh.common.util.LogUtils;
import com.gh.common.util.LunchType;
import com.gh.common.util.PackageChangeHelper;
import com.gh.common.util.PackageHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.SignatureRepository;
import com.gh.common.videolog.VideoRecordUtils;
import com.gh.download.DownloadManager;
import com.gh.download.simple.DownloadMessageHandler;
import com.gh.download.simple.SimpleDownloadDatabase;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.Injection;
import com.gh.gamecenter.common.constant.Config;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.RouteConsts;
@ -73,7 +73,6 @@ import com.gh.gamecenter.packagehelper.PackageRepository;
import com.gh.gamecenter.provider.FlavorProviderImp;
import com.gh.gamecenter.receiver.ActivitySkipReceiver;
import com.gh.gamecenter.receiver.DownloadReceiver;
import com.gh.gamecenter.receiver.InstallAndUninstallReceiver;
import com.gh.gamecenter.receiver.InstallReceiver;
import com.gh.gamecenter.receiver.NetworkStateReceiver;
import com.gh.gamecenter.wrapper.MainWrapperRepository;
@ -81,8 +80,13 @@ import com.gh.vspace.VHelper;
import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
import com.lg.ndownload.DownloadCore;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
import com.lg.ndownload.DownloadDbManager;
import com.lg.vspace.VaApp;
import com.lightgame.utils.Utils;
import com.llew.huawei.verifier.LoadedApkHuaWei;
import com.lody.virtual.client.core.VirtualCore;
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
import com.shuyu.gsyvideoplayer.player.PlayerFactory;
@ -120,7 +124,7 @@ public class HaloApp extends MultiDexApplication {
public boolean isSkippingThirdParty = false; // 应用是否处于跳转第三方Activity状态
private List<String> webViewAbiList;
private IFlavorProvider mFlavorProvider = new FlavorProviderImp();
private final IFlavorProvider mFlavorProvider = new FlavorProviderImp();
private final ServiceLoader<IApplication> mApplicationList = ServiceLoader.load(IApplication.class, this.getClass().getClassLoader());
private LunchType mLaunchType = null;
@ -208,6 +212,7 @@ public class HaloApp extends MultiDexApplication {
@Override
public void onCreate() {
super.onCreate();
VaApp.get().onCreate(this);
initArouter();
mInstance = this;
@ -331,6 +336,9 @@ public class HaloApp extends MultiDexApplication {
// 获取 settings 配置
com.gh.common.constant.Config.getGhzsSettings();
// 5 秒获取后台配置的获取应用列表限制接口结果超时后,回落到关闭状态
// PackageHelper.INSTANCE.fallbackInstalledPackageApiSwitchAfterTimeout(5000L);
String localTemporaryDeviceId = SPUtils.getString(Constants.SP_TEMPORARY_DEVICE_ID);
if (!TextUtils.isEmpty(localTemporaryDeviceId)) {
HaloApp.getInstance().setLocalTemporaryDeviceId(localTemporaryDeviceId);
@ -340,7 +348,7 @@ public class HaloApp extends MultiDexApplication {
// 必须放在外面,否则不能及时刷新用户数据
UserRepository.getInstance().getLoginUserInfo();
GlobalPriorityChainHelper.INSTANCE.preStart();
GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);
MainWrapperRepository.Companion.getInstance().getDataUnion();
@ -351,7 +359,7 @@ public class HaloApp extends MultiDexApplication {
PackageRepository.initData();
PackageHelper.refreshLocalPackageList();
PackageHelper.initList();
PackageHelper.refreshList();
initReceiver();
initPackageChangesReceiver();
@ -367,6 +375,9 @@ public class HaloApp extends MultiDexApplication {
// 初始化畅玩相关数据
retrieveVGameInfoIfNeeded();
// 移除已安装但还在本地数据库中的包(避免因为没有监听到安装结果导致安装包没有删除的问题)
removeInstalledButRemainedPackages();
// 开发环境不要强制捕获相关异常,这些异常通常是需要处理的
if (!BuildConfig.DEBUG) {
RxJavaPlugins.setErrorHandler(throwable -> {
@ -497,13 +508,7 @@ public class HaloApp extends MultiDexApplication {
}
private void initPackageChangesReceiver() {
InstallAndUninstallReceiver receiver = new InstallAndUninstallReceiver();
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(Intent.ACTION_PACKAGE_ADDED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REPLACED);
intentFilter.addAction(Intent.ACTION_PACKAGE_REMOVED);
intentFilter.addDataScheme("package");
this.registerReceiver(receiver, intentFilter);
ProcessLifecycleOwner.get().getLifecycle().addObserver(PackageChangeHelper.INSTANCE);
}
private void initConnectivityChangesReceiver() {
@ -526,6 +531,7 @@ public class HaloApp extends MultiDexApplication {
LoadedApkHuaWei.hookHuaWeiVerifier(this);
DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao());
VHelper.INSTANCE.preparePluginUpdate();
});
}
@ -575,6 +581,32 @@ public class HaloApp extends MultiDexApplication {
VHelper.init(this);
}
/**
* 移除已安装但还在本地数据库中的包
*/
private void removeInstalledButRemainedPackages() {
AppExecutor.getIoExecutor().execute(() -> {
for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntity()) {
if (downloadEntity.getStatus() != DownloadStatus.done) {
continue;
}
try {
String versionName = downloadEntity.getVersionName();
String packageName = downloadEntity.getPackageName();
// 这里暴力删除会影响畅玩游戏快速安装功能,但先不管了
if (versionName != null
&& packageName != null
&& versionName.equals(PackageUtils.getVersionNameByPackageName(packageName))) {
DownloadManager.getInstance().cancel(downloadEntity.getUrl());
}
} catch (Exception ignored) {
// 不在乎删除的结果,尝试即可
}
}
});
}
public boolean isReinstallTheSameVersion() {
return mIsReinstallTheSameVersion;
}
@ -608,15 +640,10 @@ public class HaloApp extends MultiDexApplication {
return mFlavorProvider;
}
// @NonNull
// @Override
// public Configuration getWorkManagerConfiguration() {
// return new Configuration.Builder().build();
// }
@Override
protected void attachBaseContext(Context base) {
super.attachBaseContext(base);
VaApp.get().attachBaseContext(this, base);
for (IApplication application : mApplicationList) {
application.attachBaseContext();
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

View File

@ -0,0 +1,110 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:background="@drawable/background_shape_white_radius_12_top_only"
android:descendantFocusability="blocksDescendants">
<View
android:id="@+id/bgView"
android:layout_width="match_parent"
android:layout_height="98dp"
android:background="@drawable/vspace_dialog_top_background"
app:layout_constraintTop_toTopOf="parent" />
<ImageView
android:layout_width="88dp"
android:layout_height="20dp"
android:layout_marginTop="39dp"
android:src="@drawable/ic_smooth_title"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/descTv"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:includeFontPadding="false"
android:lineSpacingExtra="4dp"
android:textColor="@color/text_subtitle"
android:textSize="14sp"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/bgView"
tools:text="《XXX》需安装完整的畅玩服务组件安装后即可给您带来急速的畅玩体验~" />
<LinearLayout
android:id="@+id/downloadContainer"
android:layout_width="280dp"
android:layout_height="wrap_content"
android:layout_marginTop="20dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/descTv">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_shape_space_radius_8"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:id="@+id/vspace32Tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="32位组件"
android:textColor="@color/text_title"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.gh.gamecenter.feature.view.DownloadButton
android:id="@+id/downloadBtn"
android:layout_width="56dp"
android:layout_height="28dp"
app:download_button_show_progress="true"
app:download_button_text_size="@dimen/secondary_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
<TextView
android:id="@+id/privacyPolicyTv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginTop="24dp"
android:layout_marginBottom="40dp"
android:text="《畅玩助手服务组件隐私协议》"
android:textColor="@color/theme_font"
android:textSize="12sp"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/downloadContainer" />
<View
android:id="@+id/drag_close"
android:layout_width="match_parent"
android:layout_height="64dp"
android:clickable="false"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -0,0 +1,95 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="@dimen/default_dialog_width"
android:layout_height="wrap_content"
android:layout_gravity="center"
android:background="@drawable/background_shape_white_radius_8">
<TextView
android:id="@+id/titleTv"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginTop="24dp"
android:layout_marginRight="24dp"
android:drawablePadding="4dp"
android:gravity="center"
android:text="服务工具更新提示"
android:textColor="@color/text_title"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/contentTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="24dp"
android:layout_marginTop="12dp"
android:layout_marginRight="24dp"
android:lineSpacingExtra="7dp"
android:textColor="@color/text_subtitle"
android:textSize="14sp"
app:layout_constraintLeft_toLeftOf="parent"
app:layout_constraintRight_toRightOf="parent"
app:layout_constraintTop_toBottomOf="@id/titleTv"
tools:text="这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文,这里是正文。" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="24dp"
android:layout_marginLeft="24dp"
android:layout_marginTop="12dp"
android:layout_marginEnd="24dp"
android:layout_marginRight="24dp"
android:layout_marginBottom="24dp"
android:orientation="vertical"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/contentTv"
app:layout_constraintBottom_toBottomOf="parent">
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="52dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_shape_space_radius_8"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<TextView
android:id="@+id/vspace32Tv"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="32位组件"
android:textColor="@color/text_title"
android:textSize="16sp"
android:textStyle="bold"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<com.gh.gamecenter.feature.view.DownloadButton
android:id="@+id/downloadBtn"
android:layout_width="56dp"
android:layout_height="28dp"
app:download_button_show_progress="true"
app:download_button_text_size="@dimen/secondary_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</androidx.constraintlayout.widget.ConstraintLayout>
</FrameLayout>

View File

@ -1,5 +1,6 @@
<com.gh.gamecenter.common.view.MaterializedConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:fitsSystemWindows="true"
@ -29,7 +30,7 @@
android:layout_height="40dp"
android:background="@color/home_realname_error"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/bottomTabContainer"
app:layout_constraintBottom_toTopOf="@+id/installApiContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent">
@ -41,7 +42,8 @@
android:src="@drawable/ic_realname_error_hint"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible" />
<TextView
android:id="@+id/realNameErrorHintTv"
@ -77,6 +79,60 @@
</androidx.constraintlayout.widget.ConstraintLayout>
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/installApiContainer"
android:layout_width="0dp"
android:layout_height="52dp"
android:background="@color/ui_surface"
android:visibility="gone"
app:layout_constraintBottom_toTopOf="@+id/bottomTabContainer"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
tools:visibility="visible">
<View
android:layout_width="match_parent"
android:layout_height="match_parent"
android:background="@color/primary_theme_10" />
<ImageView
android:id="@+id/installApiCloseIv"
android:layout_width="8dp"
android:layout_height="8dp"
android:layout_marginStart="16dp"
android:src="@drawable/ic_install_api_close"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/installApiContentTv"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginStart="16dp"
android:layout_marginEnd="8dp"
android:text="您未授予已安装列表权限,可能导致无法安装及更新等异常情况,建议开启权限!"
android:textSize="@dimen/secondary_size"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/installApiBtn"
app:layout_constraintStart_toEndOf="@id/installApiCloseIv"
app:layout_constraintTop_toTopOf="parent" />
<TextView
android:id="@+id/installApiBtn"
style="@style/BtnSmallStyle"
android:layout_width="64dp"
android:layout_height="28dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_common_button_fill_gradient_blue"
android:text="去开启"
android:textColor="@color/text_aw_primary"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:id="@+id/bottomTabContainer"
android:layout_width="0dp"

View File

@ -5,11 +5,18 @@ apply from: 'vspace-bridge/config.gradle'
buildscript {
ext.kotlin_version = '1.7.20'
ext.shadow_version = '1.0.5'
repositories {
google()
jcenter()
mavenCentral()
maven {
url "https://nexus.shanqu.cc/repository/lightgame-public/"
credentials {
username("lg_android")
password("u9gZYH4MQEwLLQZK")
}
}
maven { url 'https://jitpack.io' }
maven { url "https://maven.google.com" }
}
@ -20,6 +27,7 @@ buildscript {
// 使用了 1.2.21 在蓝叠模拟器上无法进入首页? 但是不使用又会出现触发 V3 签名...
classpath 'io.github.leon406:AndResGuard-gradle-plugin:1.2.23'
classpath 'com.sensorsdata.analytics.android:android-gradle-plugin2:3.5.3'
classpath "com.lg.shadow.core:gradle-plugin:$shadow_version"
}
}
@ -48,11 +56,13 @@ allprojects {
maven { url 'https://developer.huawei.com/repo/' }
}
}
task clean(type: Delete) {
delete rootProject.buildDir
}
subprojects {
subproject ->
afterEvaluate {
if ((subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
if (!project.name.startsWith("va-") && (subproject.plugins.hasPlugin('android') || subproject.plugins.hasPlugin('android-library'))) {
if (!android.compileSdkVersion) {
android {
compileSdkVersion rootProject.ext.compileSdkVersion

View File

@ -7,8 +7,8 @@ ext {
targetSdkVersion = 30
// application info (每个大版本之间的 versionCode 增加 20)
versionCode = 1050
versionName = "5.35.0"
versionCode = 1056
versionName = "5.35.6"
applicationId = "com.gh.gamecenter"
applicationIdGat = "com.gh.gamecenter.intl"
@ -129,7 +129,7 @@ ext {
documentfile = "1.0.1"
csjVersion = "5.6.2.0"
csjVersion = "5.8.0.4.0"
qGameVersion = "1.57.14"
qGameAdVersion = "4.520.1390"
@ -138,4 +138,8 @@ ext {
acloudPush = "3.8.8.1"
jpushVersion = "5.2.4"
}
}
apply from: 'dependencies_vasdk.gradle'
apply from: "${pluginBasePath}/PluginConfig.gradle"
apply from: "${pluginBasePath}/VaConfig.gradle"

30
dependencies_vasdk.gradle Normal file
View File

@ -0,0 +1,30 @@
ext {
vaCompileSdkVersion = 33
vaMinSdkVersion = 21
vaTargetSdkVersion = 28
}
ext {
signing_storeFile = "${rootDir}/app/gh.keystore"
signing_storePassword = "20150318"
signing_keyAlias = "gh.keystore"
signing_keyPassword = "20150318"
va_proguard_rules = "${rootDir}/vasdk/proguard/proguard-rules.pro"
}
// android dependencies
// 光环助手dependencies.gradle已经有的就不用重复定义了
ext {
shadow_version = '1.0.5'
swipeRefresh = "1.1.0"
glide = "4.12.0"
mmkv = "1.2.8"
flycoTablayout = "3.0.0"
smartRefresh = "2.0.3"
versionCompare = "1.4.1"
boltsTasks = "1.4.0"
utilcodex = "com.blankj:utilcodex:1.30.4"
}

View File

@ -72,18 +72,19 @@ object CsjAdHelper {
*/
override fun getDevOaid(): String = oaid
})
.build(),
object : TTVfSdk.InitCallback {
override fun success() {
Utils.log(TAG, "穿山甲初始化成功")
mIsInitialed = true
}
.build())
override fun fail(p0: Int, p1: String?) {
Utils.log(TAG, "穿山甲初始化失败, $p0 $p1")
mIsInitialed = false
}
})
TTVfSdk.start(object : TTVfSdk.Callback {
override fun success() {
Utils.log(TAG, "穿山甲初始化成功")
mIsInitialed = true
}
override fun fail(p0: Int, p1: String?) {
Utils.log(TAG, "穿山甲初始化失败, $p0 $p1")
mIsInitialed = false
}
})
}
fun updateThemeStatus(isDarkMode: Boolean) {
@ -124,8 +125,8 @@ object CsjAdHelper {
}
mTTVfNative.loadSphVs(adSlot, object : TTVfNative.CSJSplashAdListener {
override fun onSplashLoadSuccess() {
Utils.log(TAG, "开屏广告加载成功")
override fun onSplashLoadSuccess(p0: CSJSplashAd?) {
Utils.log(TAG, "开屏广告加载成功 $p0")
}
override fun onSplashLoadFail(p0: CSJAdError?) {
@ -199,7 +200,7 @@ object CsjAdHelper {
.setCodeId(slotId) // 广告位id
.setSupportDeepLink(true)
.setAdCount(1) // 请求广告数量为1到3条
.setExpressViewAcceptedSize(expressViewAcceptedSize, 96F) // 期望模板广告view的size,宽度最低为375单位dp
.setExpressViewAcceptedSize(expressViewAcceptedSize, 0F) // 期望模板广告view的size,宽度最低为375单位dp
.setAdLoadType(TTAdLoadType.LOAD) // 推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略
.build()

View File

@ -32,6 +32,12 @@
</intent-filter>
</service>
<provider
android:name="cn.jpush.android.service.InitProvider"
android:authorities="${applicationId}.jiguang.InitProvider"
android:exported="false"
tools:node="remove" />
</application>
</manifest>

View File

@ -85,3 +85,4 @@ android.injected.testOnly = false
# 动态配置插件
isRelease = true
pluginBasePath=vasdk/

View File

@ -63,6 +63,7 @@ public class Constants {
public static final String GHZS_GAME_ID = "5ae4462c2924bc7936438d07";
public static final String HALO_FUN_GAME_ID = "62bd412bbbf04747cd3de539"; // 畅玩助手ID
public static final String HALO_FUN_NEW_32_GAME_ID = "eeeeeeeeeeeeeeeeeeeeeeee"; // 32位新畅玩助手ID
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
public static final String EXTRA_IS_MODDED_GAME = "extra_is_modded_game"; // 是否是修改版游戏
@ -186,6 +187,9 @@ public class Constants {
// 用户是否曾经永久拒绝过存储权限
public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission";
// 用户是否曾经永久拒绝过获取应用安装列表权限
public static final String SP_USER_HAS_PERMANENTLY_DENIED_GET_INSTALL_LIST_PERMISSION = "user_has_permanently_denied_get_install_list_permission";
// 是否已经填写邀请码
public static final String SP_HAS_COMPLETE_INVITE_CODE = "has_complete_invite_code";
@ -198,6 +202,10 @@ public class Constants {
// 畅玩组件的配置
public static final String SP_V_SETTINGS = "v_settings";
// 畅玩,插件的配置
public static final String SP_V_NEW_SETTINGS = "v_new_settings";
// 头像挂件ID
public static final String SP_CHOOSE_AVATAR_ID = "choose_avatar_id";
@ -423,6 +431,7 @@ public class Constants {
public static final String SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME = "last_download_manager_ad_show_time";
public static final String SP_LAST_GAME_SEARCH_AD_SHOW_TIME = "last_game_search_ad_show_time";
public static final String SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME = "last_helper_launch_ad_show_time";
public static final String SP_NEW_FIRST_LAUNCH_VERSION = "isNewFirstLaunchV";
public static final String SP_PLUGIN_AREA_SHOWED_LAUNCH_ID = ""; // 插件化区域显示过的 launchId Tracker.launchId

View File

@ -81,6 +81,10 @@ public class EntranceConsts {
public static final String HOST_QQ_GAME = "qgame";
public static final String HOST_HOME_GAME_COLLECTION_SQUARE = "home_game_collection_square";
public static final String HOST_ARCHIVE_LOGIN = "archive_login";
public static final String HOST_RESTART_GAME = "restart_game";
public static final String KEY_DATA = "data";
public static final String KEY_MESSAGE = "message";
public static final String KEY_MESSAGE_ID = "message_id";

View File

@ -122,6 +122,8 @@ object RouteConsts {
const val push = "/push/push"
const val realName = "/realName/realName"
const val vaAd = "/vaAd/vaAd"
}
}

Some files were not shown because too many files have changed in this diff Show More