Compare commits

..

80 Commits

Author SHA1 Message Date
1040f5ff4d Merge branch 'sentry/407275' into 'release'
fix: Sentry 407275 修复Z10机型报BadTokenException的异常

See merge request halo/android/assistant-android!1915
2024-10-09 14:56:18 +08:00
97be320529 fix: Sentry 407275 修复Z10机型报BadTokenException的异常 2024-10-09 14:56:18 +08:00
59cde4e2bf Merge branch 'hotfix/v5.37.5-1095/privacy-policy' into 'release'
feat: 优化获取进程名的方式 https://jira.shanqu.cc/browse/GHZSCY-6690

See merge request halo/android/assistant-android!1898
2024-09-20 10:56:40 +08:00
cc0c7c7fae feat: 优化获取进程名的方式 https://jira.shanqu.cc/browse/GHZSCY-6690 2024-09-20 10:32:29 +08:00
9e9ce6a84f Merge branch 'hotfix/v5.37.5-1095/download_ad_crash' into 'release'
fix: 修复下载管理展示第三方广告失败回落展示自有广告时出现的闪退问题...

See merge request halo/android/assistant-android!1895
2024-09-14 17:23:26 +08:00
7d0b500ff9 fix: 修复下载管理展示第三方广告失败回落展示自有广告时出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/409586/?project=22 2024-09-14 17:13:23 +08:00
425456b263 Merge branch 'feat/v5.37.5-1095/va-2.0.5' into 'release'
Feat/v5.37.5 1095/va 2.0.5

See merge request halo/android/assistant-android!1890
2024-09-11 17:08:07 +08:00
f6abab9a2d Revert "chore: 组件升级 2.0.5-debug" 2024-09-11 17:05:56 +08:00
d194f969e4 chore: va组件2.0.5-debug 2024-09-10 16:43:57 +08:00
95f66344fb chore: va组件2.0.5 2024-09-10 13:46:15 +08:00
570e2fa9bc Revert "feat:同步商业版代码至:dbc3b8ecaca3e774b1e63f41e70b651f4acfaee1"
This reverts commit 811d42457c.
2024-09-05 15:22:32 +08:00
9e07080043 feat: 调整依赖源顺序 2024-09-04 17:03:37 +08:00
e10a329159 chore: 版本更新至 5.37.5 2024-09-04 16:00:00 +08:00
b3bc7b43f7 Merge branch 'feat/v5.37.5-1095/update-va' into 'release'
feat:同步商业版代码至:dbc3b8ecaca3e774b1e63f41e70b651f4acfaee1

See merge request halo/android/assistant-android!1875
2024-09-04 15:54:34 +08:00
811d42457c feat:同步商业版代码至:dbc3b8ecaca3e774b1e63f41e70b651f4acfaee1 2024-09-04 15:52:46 +08:00
ac0b819ea9 Merge branch 'feat/GHZSCY-6644' into 'release'
feat: 【光环助手】安装相关优化功能埋点事件的属性字段英文错误的问题 https://jira.shanqu.cc/browse/GHZSCY-6644

See merge request halo/android/assistant-android!1874
2024-09-04 15:43:47 +08:00
d931fb5940 feat: 【光环助手】安装相关优化功能埋点事件的属性字段英文错误的问题 https://jira.shanqu.cc/browse/GHZSCY-6644 2024-09-04 14:42:17 +08:00
cea62b55e2 Merge branch 'hotfix/v5.37.4-1094/GHZSCY-6631' into 'release'
fix: 特定场景下首次启动APP未直接打开设置跳转的页面 https://jira.shanqu.cc/browse/GHZSCY-6631

See merge request halo/android/assistant-android!1871
2024-09-04 11:40:58 +08:00
ff6cdb1ba3 Merge branch 'hotfix/v5.37.4-1094/GHZSCY-6613' into 'release'
fix: 客户端启动过程中若首页视频自动播放会置顶显示在开屏广告上 https://jira.shanqu.cc/browse/GHZSCY-6613

See merge request halo/android/assistant-android!1870
2024-09-04 11:40:43 +08:00
b6e531fa96 fix: 特定场景下首次启动APP未直接打开设置跳转的页面 https://jira.shanqu.cc/browse/GHZSCY-6631 2024-09-04 11:06:39 +08:00
e7a37df690 fix: 客户端启动过程中若首页视频自动播放会置顶显示在开屏广告上 https://jira.shanqu.cc/browse/GHZSCY-6613
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-03 17:57:38 +08:00
239b056abb Merge branch 'hotfix/v5.37.4-1094/illegal_state_exception' into 'release'
fix: 修复请求所有文件访问权限弹窗弹出偶发的闪退问题...

See merge request halo/android/assistant-android!1864
2024-08-30 10:05:24 +08:00
315be3797c fix: 修复请求所有文件访问权限弹窗弹出偶发的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/406178/?project=22 2024-08-30 09:49:52 +08:00
bdcca58770 build: fix 编译时无法拉取远端依赖的问题 2024-08-29 15:48:16 +08:00
644b93ab02 fix: 编译问题 2024-08-28 15:10:24 +08:00
1ee0f292ba Revert "fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题"
This reverts commit 0731cc1fde.
2024-08-26 16:06:59 +08:00
f609637f0b Merge branch 'feat/GHZSCY-6578' into 'release'
feat:【光环助手】设备信息显示 https://jira.shanqu.cc/browse/GHZSCY-6578

See merge request halo/android/assistant-android!1860
2024-08-26 14:07:55 +08:00
9d10add8eb feat:【光环助手】设备信息显示 https://jira.shanqu.cc/browse/GHZSCY-6578 2024-08-26 14:07:54 +08:00
f5a39f982e chore: 版本更新至 5.37.4 2024-08-26 10:33:09 +08:00
1a063bb0c1 Merge branch 'hotfix/v5.37.3-1093/sentry_culprit' into 'release'
fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题,捕抓更多 deadSystemException

See merge request halo/android/assistant-android!1859
2024-08-26 10:30:37 +08:00
86340d603f fix: 捕抓应用安装完成后上报用户玩过的游戏有机率遇到的 deadSystemException 并上报 sentry https://sentry.shanqu.cc/organizations/lightgame/issues/404487
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-26 10:21:42 +08:00
0731cc1fde fix: 修复因为低级失误导致 sentry.onEvent 方法失效的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-26 10:20:00 +08:00
86dfee2ff9 Merge branch 'hotfix/v5.37.3-1093/null_pointer_exception' into 'release'
fix: 修复获取BusinessId时出现的空指针闪退问题...

See merge request halo/android/assistant-android!1858
2024-08-26 09:58:11 +08:00
edf82952ce fix: 修复获取BusinessId时出现的空指针闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404588/?project=22 2024-08-26 09:54:22 +08:00
4412d159bb Merge branch 'fix/sentry-404504-3' into 'release'
fix: 下载悬浮窗Detach执行图标贴边动画造成的崩溃...

See merge request halo/android/assistant-android!1856
2024-08-23 16:05:11 +08:00
e119b7fecd fix: 下载悬浮窗Detach执行图标贴边动画造成的崩溃... 2024-08-23 16:05:11 +08:00
3fe7c8252e chore: 版本更新至 5.37.3 2024-08-22 11:19:28 +08:00
36ebab77ee Merge branch 'hotfix/v5.37.2-1092/dead_system_exception' into 'release'
fix: catch 部分高频调用的包信息获取方法,遇到 DeadSystemException 时上报 sentry

See merge request halo/android/assistant-android!1853
2024-08-22 11:18:35 +08:00
e84db01984 Merge branch 'hotfix/v5.37.2-1092/view_binding' into 'release'
fix: 用 sentry 记录 SearchToolbarTabWrapperFragment ViewBinding bind 异常时 gid,尝试重试一次

See merge request halo/android/assistant-android!1852
2024-08-22 11:16:36 +08:00
ae605d4a55 fix: 用 sentry 记录 SearchToolbarTabWrapperFragment ViewBinding bind 异常时 gid,尝试重试一次
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-22 11:11:12 +08:00
882bf4b551 fix: catch 部分高频调用的包信息获取方法,遇到 DeadSystemException 时上报 sentry
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-22 10:05:09 +08:00
968164af55 Merge branch 'hotfix/v5.37.2-1092/class_cast_exception' into 'release'
fix: 修复帮助中心分类弹窗弹出时的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/405504/?project=22

See merge request halo/android/assistant-android!1850
2024-08-21 10:05:47 +08:00
0119e6f123 fix: 修复帮助中心分类弹窗弹出时的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/405504/?project=22 2024-08-21 09:59:40 +08:00
7093552259 Merge branch 'FIX/sentry-404504-2' into 'release'
fix: 悬浮窗Detach时机问题造成的崩溃2

See merge request halo/android/assistant-android!1847
2024-08-20 14:02:02 +08:00
2be52d21ee fix: 悬浮窗Detach时机问题造成的崩溃2 2024-08-20 14:02:02 +08:00
cb093cc3f0 Merge branch 'hotfix/v5.37.1-1091/jobservice_abstract_method_error' into 'release'
fix: 修复 94d0e2b5f19760d22381ead093c9e89778a14bf4 这个版本没有修复的...

See merge request halo/android/assistant-android!1844
2024-08-19 16:08:04 +08:00
3cbdd94189 fix: 修复 94d0e2b5f19760d22381ead093c9e89778a14bf4 这个版本没有修复的 java.lang.AbstractMethodError: abstract method "void android.app.job.IJobService.onNetworkChanged(android.app.job.JobParameters)" 错误。 2024-08-19 16:02:01 +08:00
11013319bf chore: 版本更新至 5.37.2 2024-08-19 12:40:31 +08:00
aa954b3af9 Merge branch 'hotfix/v5.37.1-1091/mmkv_wrong_usage' into 'release'
fix: 修复2.0.3光环启动32位游戏,概率没有初始化mmkv

See merge request halo/android/assistant-android!1843
2024-08-19 11:59:16 +08:00
93f279a384 fix: 修复2.0.3光环启动32位游戏,概率没有初始化mmkv 2024-08-19 11:45:12 +08:00
4e54833cc9 Merge branch 'hotfix/v5.37.1-1091/login_crash' into 'release'
fix: 临时处理登录后 token 为空引起的闪退,并上报相关日志到 sentry

See merge request halo/android/assistant-android!1842
2024-08-19 11:11:31 +08:00
f95862da8b Merge branch 'hotfix/v5.37.1-1091/class_cast_exception' into 'release'
fix: 修复偶发的类型转换异常闪退...

See merge request halo/android/assistant-android!1841
2024-08-19 10:50:23 +08:00
1515df34b5 fix: 临时处理登录后 token 为空引起的闪退,并上报相关日志到 sentry
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-19 10:44:14 +08:00
8a8ef40dc1 fix: 修复偶发的类型转换异常闪退 https://sentry.shanqu.cc/organizations/lightgame/issues/404710/events/?project=22 2024-08-19 09:27:35 +08:00
b6194fed26 Merge branch 'chen/202408/fix-quick-login-crash' into 'release'
fix:点击快速登录有概率出现crash https://sentry.shanqu.cc/organizations/lightgame/issues/404488/?project=22

See merge request halo/android/assistant-android!1840
2024-08-16 17:39:58 +08:00
02752f6c81 fix:点击快速登录有概率出现crash https://sentry.shanqu.cc/organizations/lightgame/issues/404488/?project=22 2024-08-16 17:34:17 +08:00
b33d367dfd Merge branch 'fix/sentry-404504' into 'release'
fix: 悬浮窗Detach时机问题造成的崩溃

See merge request halo/android/assistant-android!1839
2024-08-16 14:04:09 +08:00
d5046ab431 fix: 悬浮窗Detach时机问题造成的崩溃 2024-08-16 14:04:09 +08:00
a53168c9bc Merge branch 'hotfix/v5.37.1-1091/add_game_popup_crash' into 'release'
fix: 修复退出添加游戏弹窗后弹出引导弹窗出现的闪退问题...

See merge request halo/android/assistant-android!1838
2024-08-16 10:47:28 +08:00
3cbc8c2f74 fix: 修复退出添加游戏弹窗后弹出引导弹窗出现的闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404461/?project=22 2024-08-16 10:42:10 +08:00
7398b83004 Merge branch 'chen/202408/fix-bottom-tab_parse_lottie_crash' into 'release'
fix:当后台bottom tab上传的lottie文件格式不合法时,解析出现crash

See merge request halo/android/assistant-android!1837
2024-08-15 17:19:19 +08:00
8013426ded fix:当后台bottom tab上传的lottie文件格式不合法时,解析出现crash 2024-08-15 17:16:14 +08:00
b86d85b15f chore: 版本更新至 5.37.1-1091
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-15 14:42:55 +08:00
5a73afbab3 Merge branch 'hotfix/v5.37.0-1090-get_meta_data_crash' into 'release'
fix: catch 获取应用 meta data 时的 DeadSystemException 并上报至 sentry...

See merge request halo/android/assistant-android!1836
2024-08-15 14:30:46 +08:00
e19ee25839 fix: catch 获取应用 meta data 时的 DeadSystemException 并上报至 sentry https://sentry.shanqu.cc/organizations/lightgame/issues/390674
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-15 14:26:12 +08:00
61bf39e95f Merge branch 'hotfix/v5.37.0-1090/index_out_of_bounds_exception' into 'release'
fix: 修复数组越界闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404202/?project=22

See merge request halo/android/assistant-android!1835
2024-08-15 14:20:39 +08:00
ef82ae2378 fix: 修复数组越界闪退问题 https://sentry.shanqu.cc/organizations/lightgame/issues/404202/?project=22 2024-08-15 14:17:32 +08:00
2dcccc22a8 Merge branch 'fix/sentry-404211' into 'release'
fix: 工作线程显示悬浮窗造成的崩溃问题

See merge request halo/android/assistant-android!1834
2024-08-15 14:09:06 +08:00
cb0e77d204 fix: 工作线程显示悬浮窗造成的崩溃问题 2024-08-15 14:09:06 +08:00
a6ea178609 Merge branch 'fix/sentry-404179' into 'release'
fix: 悬浮窗权限申请弹窗在Activity销毁后弹出造成的崩溃问题

See merge request halo/android/assistant-android!1833
2024-08-15 14:08:58 +08:00
6a62f08891 fix: 悬浮窗权限申请弹窗在Activity销毁后弹出造成的崩溃问题 2024-08-15 14:08:58 +08:00
ddb48481d2 Merge branch 'hotfix/v5.37.0-1090-dual_download_error_issue' into 'release'
fix: 双下载时使用本地下载进行下载时临时屏蔽特殊弹窗和下载悬浮窗 https://jira.shanqu.cc/browse/GHZSCY-6521

See merge request halo/android/assistant-android!1832
2024-08-14 11:36:51 +08:00
48a55a6b31 fix: 双下载时使用本地下载进行下载时临时屏蔽特殊弹窗和下载悬浮窗 2024-08-14 11:35:20 +08:00
50fcb0c3ea fix: 方法入参异常造成的编译问题 2024-08-13 16:20:01 +08:00
e97722b6fb Merge branch 'feat/cw-apk-udpate' into 'release'
feat: 优化畅玩游戏更新逻辑

See merge request halo/android/assistant-android!1790
2024-08-13 15:19:22 +08:00
2163b2ff9e Merge branch 'fix/GHZSCY-6438' into 'release'
fix: 已安装游戏替换问题 https://jira.shanqu.cc/browse/GHZSCY-6438

See merge request halo/android/assistant-android!1831
2024-08-13 15:06:03 +08:00
b9558e2732 Merge branch 'fix/GHZSCY-6414' into 'release'
fix: 安装流程相关优化—2024年8月2日测试组 https://jira.shanqu.cc/browse/GHZSCY-6414

See merge request halo/android/assistant-android!1830
2024-08-13 14:25:51 +08:00
e9139c66e1 fix: 已安装游戏替换问题 https://jira.shanqu.cc/browse/GHZSCY-6438
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-08 17:43:31 +08:00
f492d2ea97 fix: 安装流程相关优化—2024年8月2日测试组 https://jira.shanqu.cc/browse/GHZSCY-6414
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-02 14:49:59 +08:00
46955685bb feat: 优化畅玩游戏更新逻辑 2024-07-30 16:54:30 +08:00
66 changed files with 1108 additions and 286 deletions

View File

@ -72,6 +72,7 @@ android_build:
only:
- dev
- release
- feat/GHZSCY-6644
# 代码检查
sonarqube_analysis:
@ -156,4 +157,5 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail-jira-comment.py
only:
- dev
- release
- release
- feat/GHZSCY-6644

View File

@ -32,11 +32,43 @@ class ExternalGameUsage : ITestCase {
it.titleTv.text = context.getString(R.string.title_install_external_game)
it.iconIv.setImageResource(R.drawable.ic_personal_my_game)
it.root.setOnClickListener {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
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

@ -1,12 +1,7 @@
package com.gh.vspace.installexternalgames
import android.Manifest
import android.app.Dialog
import android.content.ComponentName
import android.content.pm.PackageManager
import android.os.Bundle
import androidx.activity.result.contract.ActivityResultContracts
import androidx.core.content.ContextCompat
import androidx.recyclerview.widget.LinearLayoutManager
import com.gh.common.util.DialogUtils
import com.gh.gamecenter.R
@ -45,19 +40,6 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
private lateinit var dialog: Dialog
private val requestPermissionLauncher = registerForActivityResult<String, Boolean>(
ActivityResultContracts.RequestPermission()
) { result ->
if (result == true) {
// grant
mViewModel.scanPaths()
} else {
// not grant
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setNavigationTitle(getString(com.gh.gamecenter.R.string.title_install_external_game))
@ -74,30 +56,11 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
)
adapter.notifyDataSetChanged()
}
mViewModel.scanPaths()
requestStoragePermission()
}
private fun requestStoragePermission() {
when {
ContextCompat.checkSelfPermission(
requireContext(),
Manifest.permission.READ_EXTERNAL_STORAGE
) == PackageManager.PERMISSION_GRANTED -> {
mViewModel.scanPaths()
}
shouldShowRequestPermissionRationale(Manifest.permission.READ_EXTERNAL_STORAGE) -> {
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
else -> {
requestPermissionLauncher.launch(Manifest.permission.READ_EXTERNAL_STORAGE)
}
}
}
private fun initView() {
dialog = DialogUtils.showWaitDialog(requireContext(), "")
mBinding.externalGamesList.let {
@ -131,11 +94,9 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
OnItemClickListener.ClickType.CLICK_INSTALL -> {
install(externalGameUiState)
}
OnItemClickListener.ClickType.CLICK_UNINSTALL -> {
uninstall(externalGameUiState)
}
OnItemClickListener.ClickType.CLICK_START -> {
start(externalGameUiState)
}
@ -143,8 +104,9 @@ 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.disableLaunchGameAfterInstallation()
VHelper.install(requireContext(), DownloadEntity().apply {
externalGameUiState.externalGameEntity.apply {
packageName = apkPackageName
@ -152,12 +114,20 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
}
}, true)
VHelper.newCwValidateVspaceBeforeAction(
requireContext(),null,
) {
dialog.show()
}
if (VHelper.showDialogIfVSpaceIsNeeded(
requireContext(),
"",
externalGameUiState.externalGameEntity.appName,
"",
bit = bit
)
) return
dialog.show()
externalGameUiState.externalGameEntity.let {
val intent = VirtualAppManager.getInstallIntent(context, it.apkPath, it.apkPackageName)
requireActivity().startActivity(intent)
}
}
private fun uninstall(externalGameUiState: ExternalGameUiState) {
@ -188,12 +158,6 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
com.gh.gamecenter.BuildConfig.VA_VERSION_NAME,
HaloApp.getInstance().oaid
)
intent.setComponent(
ComponentName(
com.gh.gamecenter.BuildConfig.APPLICATION_ID,
VirtualAppManager.AIDL_SERVER_REMOTE_GUIDE_ACTIVITY
)
)
requireActivity().startActivity(intent)
}

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

@ -0,0 +1,16 @@
package com.gh.common.prioritychain
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
class VideoHandler(priority: Int, val scrollCalculatorHelper: ScrollCalculatorHelper): PriorityChainHandler(priority) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
scrollCalculatorHelper.enableAndPlayIfValid()
return true
}
}

View File

@ -2,12 +2,15 @@ package com.gh.common.provider
import android.content.Context
import android.content.Intent
import android.os.Build
import android.os.Bundle
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.common.util.EntranceUtils
import com.gh.gamecenter.common.avoidcallback.Callback
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IEntranceUtilsProvider
import com.lightgame.utils.AppManager
@Route(path = RouteConsts.provider.entranceUtils, name = "EntranceUtils暴露服务")
class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
@ -20,11 +23,16 @@ class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
}
override fun jumpActivityWithCallback(context: Context, bundle: Bundle, callback: () -> Unit) {
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
callback()
}
})
if (context is FragmentActivity && !context.supportFragmentManager.isDestroyed) {
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
callback()
}
})
} else {
EntranceUtils.jumpActivity(AppManager.getInstance().currentActivity(), bundle)
}
}
override fun init(context: Context?) {

View File

@ -37,10 +37,10 @@ import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
import com.gh.vspace.VHelper
import com.lightgame.download.DownloadConfig
@ -697,7 +697,7 @@ object DownloadItemUtils {
// 为 downloadButton 添加游戏实体,供点击的时候上报用
downloadBtn.putObject(gameEntity)
val gamePermissionDialogFragment = (context as AppCompatActivity).supportFragmentManager.findFragmentByTag(
val gamePermissionDialogFragment = (context as? AppCompatActivity)?.supportFragmentManager?.findFragmentByTag(
GamePermissionDialogFragment::class.java.name
) as GamePermissionDialogFragment?
gamePermissionDialogFragment?.dismissAllowingStateLoss()

View File

@ -105,9 +105,14 @@ public class EntranceUtils {
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtras(bundle);
if (context instanceof AppCompatActivity) {
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
} else {
// 不要回调,正常跳转
context.startActivity(intent1);
}
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
}
} else {
// 应用未在运行

View File

@ -7,6 +7,7 @@ import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
@ -111,6 +112,14 @@ object GameSubstituteRepositoryHelper {
}
}
// 检查是否已安装该游戏 id
if (PackagesManager.getInstalledDataByGameId(game.id) != null) {
// 将该位置的游戏标记为需要替换
positionOfGameToBeReplacedList.add(index)
thisPositionNeedToBeReplaced = true
break
}
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
if (!thisPositionNeedToBeReplaced) {
var relatedPackageList = arrayListOf<String>()

View File

@ -308,7 +308,7 @@ object GameUtils {
}
/**
* 是否以畅玩游戏的形式来处理
* 是否默认以畅玩游戏的形式来处理
*/
@JvmStatic
fun shouldPerformAsVGame(gameEntity: GameEntity): Boolean {

View File

@ -57,6 +57,10 @@ object HomeBottomBarHelper {
return BottomTab(name = "我的光环", jsCode = animationCode, iconSelector = R.drawable.selector_ic_user, link = LinkEntity(type = TYPE_MY_HALO))
}
fun isDefaultHomeBottomTabDataExist(): Boolean {
return SPUtils.getString(KEY_HOME_BOTTOM_TAB).isNotEmpty()
}
@JvmStatic
fun getDefaultHomeBottomTabData(): List<BottomTab> {
try {

View File

@ -90,7 +90,7 @@ object PackageChangeHelper : DefaultLifecycleObserver {
pendingPackageTriple = null
pendingGhId = null
PackageRepository.addInstalledGame(packageName)
PackageRepository.addInstalledGame(PackageRepository.packageFilterManager, packageName)
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)

View File

@ -81,23 +81,28 @@ object PackageHelper {
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
private var _commentPackageNameBlackList = arrayListOf<String>()
val commentPackageNameBlackList: ArrayList<String> = _commentPackageNameBlackList
val commentPackageNameBlackList: ArrayList<String>
get() = _commentPackageNameBlackList
// 关闭下载的包列表
private var _downloadPackageNameBlackList = arrayListOf<String>()
val downloadPackageNameBlackList: ArrayList<String> = _downloadPackageNameBlackList
val downloadPackageNameBlackList: ArrayList<String>
get() = _downloadPackageNameBlackList
// 本地已安装的包去掉关闭下载的包后的列表
private var _validLocalPackageNameSet = hashSetOf<String>()
val validLocalPackageNameSet: HashSet<String> = _validLocalPackageNameSet
val validLocalPackageNameSet: HashSet<String>
get() = _validLocalPackageNameSet
// 游戏包名匹配列表
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages>
get() = _relatedPackageList
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean>
get() = _installedPackageApiSwitchStatusLiveData
// 本地已安装包的列表
var localPackageNameSet = hashSetOf<String>()
@ -541,6 +546,7 @@ object PackageHelper {
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
PackageRepository.addInstalledGames(
packageFilterManager = PackageRepository.packageFilterManager,
pkgNameList = ArrayList(installedPackageNameSet),
updateInstallStatus = true
)

View File

@ -14,6 +14,7 @@ import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.text.TextUtils;
import android.util.AndroidException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -222,8 +223,19 @@ public class PackageUtils {
if (metaDate != null) {
return metaDate.get(name);
}
} catch (NameNotFoundException e) {
// e.printStackTrace();
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_META_DATA_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -627,8 +639,19 @@ public class PackageUtils {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_VERSION_NAME_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -640,8 +663,18 @@ public class PackageUtils {
try {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_VERSION_CODE_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return 0;
}
@ -654,8 +687,18 @@ public class PackageUtils {
try {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
// do nothing
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_ICON_ERROR",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
}
return null;
}
@ -706,8 +749,19 @@ public class PackageUtils {
jsonObject.put("version", packageInfo.versionName);
}
return jsonObject;
} catch (JSONException | NameNotFoundException e) {
} catch (Exception e) {
e.printStackTrace();
if (e instanceof AndroidException) {
// 有些设备会出现 DeadSystemException
SentryHelper.INSTANCE.onEvent(
"GET_APP_BASIC_INFO_BY_PACKAGE_NAME",
"packageName",
packageName,
"exception_digest",
e.getLocalizedMessage()
);
}
return jsonObject;
}
}

View File

@ -59,8 +59,8 @@ class GameDetailActivity : DownloadToolbarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as GameDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? GameDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAMEID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -161,6 +161,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
val showOnFailed = downloadManagerAd?.displayRule?.onFailedAction == "show"
if ((showThirdPartyAd && thirdPartyAd != null) || (!showThirdPartyAd && thirdPartyAd != null && ownerAd == null)) {
initThirdPartyAd(thirdPartyAd) { isSuccess ->
if (!isAdded) return@initThirdPartyAd
mBinding.maskView.goneIf(!isSuccess)
if (!isSuccess && ownerAd != null && showOnFailed) {
mSlideInterval = ownerAd.adSource?.sliderInterval ?: -1
@ -193,7 +194,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
}
private fun initOwnerAd(adConfig: AdConfig) {
if (adConfig.id.isEmpty()) return
if (!isAdded || adConfig.id.isEmpty()) return
mAdGameViewModel = viewModelProvider(AdGameViewModel.Factory(adConfig))
initAdGameBanner(adConfig)
mBinding.closeAdIv.setOnClickListener {
@ -227,6 +228,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
adConfig.displayRule.onFailedAction == "show" && adConfig.thirdPartyAd != null) {
// 自有广告游戏为空时,显示第三方广告
initThirdPartyAd(adConfig.thirdPartyAd) { isSuccess ->
if (!isAdded) return@initThirdPartyAd
mBinding.maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME, System.currentTimeMillis())

View File

@ -2,8 +2,8 @@ package com.gh.gamecenter.game.columncollection.detail
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
@ -34,8 +34,8 @@ class ColumnCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as ColumnCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? ColumnCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -36,8 +36,8 @@ class CommonCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as CommonCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? CommonCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""

View File

@ -36,8 +36,8 @@ class CustomCommonCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as CustomCommonCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? CustomCommonCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""

View File

@ -182,7 +182,9 @@ class AddGamesDialogFragment : BaseDialogFragment() {
return super.onBack()
}
private fun showGuidePopupWindow(): PopupWindow {
private fun showGuidePopupWindow(): PopupWindow? {
if (!isAdded) return null
val guideBinding = LayoutGameCollectionAddGamesGuideBinding.inflate(layoutInflater)
val popupWindow = BugFixedPopupWindow(
guideBinding.root,

View File

@ -7,8 +7,8 @@ import android.view.View
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.core.utils.DisplayUtils
/**
* 游戏单详情
@ -30,8 +30,8 @@ class GameCollectionDetailActivity : ToolBarActivity() {
}
override fun getBusinessId(): Pair<String, String> {
val fragment = targetFragment as GameCollectionDetailFragment
return if (fragment.arguments != null) {
val fragment = targetFragment as? GameCollectionDetailFragment?
return if (fragment?.arguments != null) {
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAME_COLLECTION_ID) ?: "", "")
} else {
super.getBusinessId()

View File

@ -149,8 +149,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
private var mShowConcernOnMenu = false
private var mSpecialDownloadDetailFragmentIsShowing = false
private val mFragmentsList = ArrayList<Fragment>()
private val mTabTitleList = ArrayList<String>()
private val mTabTypeList = ArrayList<String>() // tab 类型的列表,用于确定某个类型的 tab 在第几个位置
@ -167,7 +165,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|| downloadEntity.status == DownloadStatus.redirected
) {
// 特殊下载弹窗
if (isSpecialDownloadDialogAvailable() && !mSpecialDownloadDetailFragmentIsShowing) {
if (isSpecialDownloadDialogAvailable(downloadEntity) && !isSpecialDownloadDetailFragmentIsShowing()) {
updateSpecialDownloadDialogIcon(true)
if (downloadEntity.status == DownloadStatus.add) {
@ -868,8 +866,10 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
showConcernIconAtBottomBarIfAvailable()
if (isSpecialDownloadDialogAvailable()
&& DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity) != null) {
val downloadEntitySnapshot = DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity)
if (isSpecialDownloadDialogAvailable(downloadEntitySnapshot)
&& downloadEntitySnapshot != null) {
updateSpecialDownloadDialogIcon(true)
}
@ -2562,12 +2562,15 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
* 2. 未获取到游戏详情数据
* 3. 当前游戏 APK 不为 1 个
* 4. 当前游戏类型不为畅玩
* 5. 当前游戏不是双下载时使用本地下载进行下载
*/
private fun isSpecialDownloadDialogAvailable(): Boolean {
private fun isSpecialDownloadDialogAvailable(downloadEntity: DownloadEntity? = null): Boolean {
if (Config.getNewApiSettingsEntity()?.install?.questionTip?.linkEntity == null) return false
if (mNewGameDetailEntity == null || mGameEntity == null) return false
if (mGameEntity?.getApk()?.size != 1) return false
if (GameUtils.shouldPerformAsVGame(mGameEntity!!)) return false
if (downloadEntity?.asVGame() == true) return false
if (downloadEntity?.isSimulatorGame() == true) return false
if (downloadEntity?.isLocalDownloadInDualDownloadMode() == true) return false
return true
}
@ -2585,7 +2588,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
if (fragment == null) {
if (!visibilityViewModel.specialDownloadVisibleLiveData.hasObservers()) {
visibilityViewModel.specialDownloadVisibleLiveData.observe(viewLifecycleOwner) {
mSpecialDownloadDetailFragmentIsShowing = it
updateSpecialDownloadDialogIcon(visible = !it)
}
}
@ -2601,6 +2603,10 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
}
}
private fun isSpecialDownloadDetailFragmentIsShowing(): Boolean {
return childFragmentManager.findFragmentByTag(TAG_SPECIAL_DOWNLOAD_DIALOG) != null
}
override fun scrollToTop() {
val fragment = mFragmentsList.safelyGetInRelease(mBodyBinding.gamedetailVp.currentItem)
if (fragment is IScrollable && fragment.isAdded) {

View File

@ -394,7 +394,7 @@ class SpecialDownloadDialogFragment : BaseDraggableDialogFragment() {
SensorsBridge.trackDownloadComponentsContentClick(
gameId = gameEntity.id,
gameName = gameEntity.name ?: "unknown",
gameSchemeType = gameEntity.gameBitChinese,
gameSchemaType = gameEntity.gameBitChinese,
downloadStatus = gameEntity.downloadStatusChinese,
gameType = gameEntity.categoryChinese,
downloadType = if (asVGame) "畅玩下载" else "本地下载",
@ -405,7 +405,7 @@ class SpecialDownloadDialogFragment : BaseDraggableDialogFragment() {
SensorsBridge.trackDownloadComponentsShow(
gameId = gameEntity.id,
gameName = gameEntity.name ?: "unknown",
gameSchemeType = gameEntity.gameBitChinese,
gameSchemaType = gameEntity.gameBitChinese,
downloadStatus = gameEntity.downloadStatusChinese,
gameType = gameEntity.categoryChinese,
downloadType = if (asVGame) "畅玩下载" else "本地下载",

View File

@ -17,6 +17,7 @@ import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CustomFloatingWindowHandler
import com.gh.common.prioritychain.PriorityChain
import com.gh.common.prioritychain.PullDownPushHandler
import com.gh.common.prioritychain.VideoHandler
import com.gh.common.util.DefaultSearchHintHelper
import com.gh.common.util.DialogUtils
import com.gh.common.util.DirectUtils
@ -468,9 +469,9 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
binding = FragmentCustomBinding.bind(mCachedView)
buildPriorityChain()
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0, false)
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0)
buildPriorityChain()
adapter = CustomPageAdapter(
viewModel,
@ -512,9 +513,11 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
private fun buildPriorityChain() {
val floatingWindowHandler = CustomFloatingWindowHandler(23)
val videoHandler = VideoHandler(24, scrollCalculatorHelper)
priorityChain.addHandler(pullDownPushHandler)
priorityChain.addHandler(floatingWindowHandler)
priorityChain.addHandler(videoHandler)
(parentFragment as? BaseTabWrapperFragment)?.addTabGuideHandlerIfExists(priorityChain)
viewModel.floatingWindows.observe(viewLifecycleOwner, EventObserver {

View File

@ -5,7 +5,6 @@ import android.os.Handler
import android.os.Looper
import android.view.View
import android.view.ViewGroup
import androidx.core.view.forEach
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.common.constant.Constants
@ -15,13 +14,28 @@ import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.video.detail.CustomManager
import com.halo.assistant.HaloApp
class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int, private val mRangeTop: Int) {
class ScrollCalculatorHelper(
val mListRv: RecyclerView,
private val mPlayId: Int,
private val mRangeTop: Int,
private var isEnabled: Boolean = true
) {
private var mFirstVisible = -1
private var mLastVisible = 0
private var mRunnable: PlayRunnable? = null
private val mPlayHandler = Handler(Looper.getMainLooper())
var currentPlayer: AutomaticVideoView? = null
fun enableAndPlayIfValid() {
isEnabled = true
if (mListRv.isAttachedToWindow
&& mListRv.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
playVideo(mListRv)
}
}
fun onScrollStateChanged(scrollState: Int) {
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
playVideo(mListRv)
@ -80,7 +94,7 @@ class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int
}
private fun playVideo(view: RecyclerView?) {
if (view == null) return
if (view == null || !view.isAttachedToWindow || !isEnabled) return
val layoutManager = view.layoutManager
var gsyBaseVideoPlayer: AutomaticVideoView
for (i in mFirstVisible until mLastVisible + 1) {

View File

@ -13,7 +13,7 @@ object HeadUpDisplayLogHelper {
source = source,
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
gameId = downloadEntity.gameId,
gameName = downloadEntity.name
@ -25,7 +25,7 @@ object HeadUpDisplayLogHelper {
source = source,
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
gameId = downloadEntity.gameId,
gameName = downloadEntity.name
@ -36,7 +36,7 @@ object HeadUpDisplayLogHelper {
SensorsBridge.trackAutomaticInstallationPromptBarShow(
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
gameId = downloadEntity.gameId,
gameName = downloadEntity.name
@ -47,7 +47,7 @@ object HeadUpDisplayLogHelper {
SensorsBridge.trackAutomaticInstallationPromptBarClick(
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
gameId = downloadEntity.gameId,
gameName = downloadEntity.name

View File

@ -130,6 +130,24 @@ object PackagesManager {
return null
}
/**
* 根据游戏 ID 获取已安装的信息
*
* @param gameId 游戏 Id
* @return 如果为空:未安装
*/
fun getInstalledDataByGameId(gameId: String?): GameInstall? {
if (TextUtils.isEmpty(gameId)) {
return null
}
for (gameInstall in mInstalledList) {
if (gameInstall.id == gameId) {
return gameInstall
}
}
return null
}
/**
* 判断包名是否可以更新

View File

@ -11,11 +11,9 @@ import com.gh.gamecenter.room.AppDatabase
import com.halo.assistant.HaloApp
import io.reactivex.schedulers.Schedulers
object PackageFilterManager {
class PackageFilterManager {
private var mPackageKey = "" // 用于下次获取包名-游戏信息的 key
private val mPendingPackageNameSet by lazy { hashSetOf<String>() } // 因遇到异常而等待下次操作更新的包名列表
private val mValidPackageNameSet by lazy { hashSetOf<String>() } // 已被收录的游戏包名列表
val packageKey: String
get() = mPackageKey
@ -34,7 +32,7 @@ object PackageFilterManager {
) {
if (appendOnly) {
// 添加因为异常而没能正常更新的包名列表
packageList.addAll(mPendingPackageNameSet)
packageList.addAll(PackageRepository.mPendingPackageNameSet)
}
RetrofitManager.getInstance()
@ -44,12 +42,12 @@ object PackageFilterManager {
.subscribe(object : BiResponse<PackageFilter>() {
override fun onSuccess(data: PackageFilter) {
mPackageKey = data.key
mPendingPackageNameSet.clear()
PackageRepository.mPendingPackageNameSet.clear()
val partialPackageList = arrayListOf<String>()
if (!appendOnly) {
mValidPackageNameSet.clear()
PackageRepository.mValidPackageNameSet.clear()
tryWithDefaultCatch {
AppDatabase.getInstance().packageFilterDao().deleteAllPackageName()
}
@ -63,13 +61,13 @@ object PackageFilterManager {
}
partialPackageList.add(packageName)
mValidPackageNameSet.add(packageName)
PackageRepository.mValidPackageNameSet.add(packageName)
}
if (appendOnly) {
callbackClosure?.invoke(ArrayList(partialPackageList))
} else {
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
}
}
@ -77,7 +75,7 @@ object PackageFilterManager {
super.onFailure(exception)
if (appendOnly) {
mPendingPackageNameSet.addAll(packageList)
PackageRepository.mPendingPackageNameSet.addAll(packageList)
} else {
if (exception is retrofit2.HttpException && exception.code() == 403) {
// 403 代表 key 过期,需要重新获取
@ -91,28 +89,13 @@ object PackageFilterManager {
for (packageEntity in packageEntityList) {
// 依然为已安装状态才加入到有效包名列表中
if (PackageUtils.isInstalled(HaloApp.getInstance(), packageEntity.packageName)) {
mValidPackageNameSet.add(packageEntity.packageName)
PackageRepository.mValidPackageNameSet.add(packageEntity.packageName)
}
}
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
}
}
})
}
/**
* 该包名是否有效 (即是否已被光环收录)
*/
fun isPackageValid(packageName: String): Boolean {
return mValidPackageNameSet.contains(packageName)
}
/**
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
*/
fun hasPendingPackage(): Boolean {
return mPendingPackageNameSet.isEmpty()
}
}

View File

@ -6,7 +6,6 @@ import android.text.TextUtils
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.common.util.GameUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
@ -16,19 +15,29 @@ 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.entity.AppEntity
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.PackageGame
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository.gameInstalled
import com.gh.gamecenter.packagehelper.PackageRepository.gameUpdate
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.SingleObserver
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
@ -74,6 +83,10 @@ object PackageRepository {
fun changeRecentVaPlayed() {
_recentVaPlayedChanged.postValue(Unit)
}
val packageFilterManager = PackageFilterManager()
var mPendingPackageNameSet = hashSetOf<String>() // 因遇到异常而等待下次操作更新的包名列表
var mValidPackageNameSet = hashSetOf<String>() // 已被收录的游戏包名列表
/**
* 预留方法,如果想手动初始化可以调用
@ -89,18 +102,51 @@ object PackageRepository {
if (gameUpdate.isNotEmpty()) gameUpdate.clear()
if (mInstalledPkgSet.isNotEmpty()) mInstalledPkgSet.clear()
val list = PackageUtils.getAllPackageName(mApplication)
Single.zip<Result<Any?>, Result<Any?>, Result<Any?>>(
Single.create { emitter ->
val list = PackageUtils.getAllPackageName(mApplication)
uploadAppList()
initFilterPackage(list) { filteredList ->
emitter.onSuccess(Result.success(null))
uploadAppList()
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
loadInstalledGameDigestAndNotifyData(packageFilterManager.packageKey, filteredList)
}
initFilterPackage(list) { filteredList ->
mIsInitialisingData = false
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
},
Single.create { emitter ->
// 畅玩游戏更新
var allGames = VHelper.getAllVGameSnapshots()
if (allGames.isEmpty()) {
VHelper.refreshVGameSnapshot()
allGames = VHelper.getAllVGameSnapshots()
}
val allGamePkgNames = allGames.map { it.packageName }.toArrayList()
if (allGamePkgNames.isNotEmpty()) {
val packageFilterManager = PackageFilterManager()
updateFilterPackage(packageFilterManager, allGamePkgNames) {
emitter.onSuccess(Result.success(null))
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = allGamePkgNames,
isVGame = true
)
}
}
}
) { t1, t2 -> Result.success(null) }.subscribe(object : SingleObserver<Result<Any?>> {
override fun onSubscribe(d: Disposable) {
}
loadInstalledGameDigestAndNotifyData(filteredList)
}
override fun onSuccess(t: Result<Any?>) {
mIsInitialisingData = false
}
override fun onError(e: Throwable) {
}
})
loadGhzsUpdate()
}
@ -113,18 +159,15 @@ object PackageRepository {
list: MutableList<String>,
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
) {
PackageFilterManager.updateFilterPackages(list, false) {
callbackClosure?.invoke(it)
}
packageFilterManager.updateFilterPackages(list, false, callbackClosure)
}
private fun updateFilterPackage(
packageFilterManager: PackageFilterManager,
list: MutableList<String>,
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
) {
PackageFilterManager.updateFilterPackages(list, true) {
callbackClosure?.invoke(it)
}
packageFilterManager.updateFilterPackages(list, true, callbackClosure)
}
/**
@ -135,7 +178,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>() {
@ -208,6 +252,7 @@ object PackageRepository {
*/
@SuppressLint("CheckResult")
private fun loadInstalledGameDigestAndNotifyData(
packageKey: String,
filteredList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false
@ -222,7 +267,7 @@ object PackageRepository {
}, Any())
while (++page <= maxPageCount) {
var observable = mNewApi.getPackageGames(PackageFilterManager.packageKey, page, PAGE_SIZE)
var observable = mNewApi.getPackageGames(packageKey, page, PAGE_SIZE)
.subscribeOn(Schedulers.io())
observable.subscribe(object : BiResponse<List<PackageGame>>() {
@ -238,7 +283,8 @@ object PackageRepository {
}
for (game in validGames) {
val shouldNotifyChanges = validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
val shouldNotifyChanges =
validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
if (!isNotifyUpdate && shouldNotifyChanges) {
isNotifyUpdate = true
}
@ -259,11 +305,13 @@ object PackageRepository {
/**
* 验证游戏并且更新数据
*/
private fun validateGameAndPostChanges(ghId: Any?,
game: GameEntity,
pkgName: String,
isVGame: Boolean,
updateInstallStatus: Boolean): Boolean {
private fun validateGameAndPostChanges(
ghId: Any?,
game: GameEntity,
pkgName: String,
isVGame: Boolean,
updateInstallStatus: Boolean
): Boolean {
if (ghId == null || ghId == game.id) {
gameInstalled.add(GameInstall.transformGameInstall(game, pkgName, isVGame))
mInstalledGameList.add(game)
@ -272,7 +320,8 @@ object PackageRepository {
addCurrentlyInstalledVersionIfValid(game)
if (updateInstallStatus) {
EventBus.getDefault().post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
EventBus.getDefault()
.post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
}
if (isCanUpdate || isCanPluggable) {
@ -356,7 +405,8 @@ object PackageRepository {
) {
// 使用了镜像的游戏;插件化关闭的游戏;无需插件化
if (game.shouldUseMirrorInfo()
|| apk.plugin == "close") {
|| apk.plugin == "close"
) {
return false
}
@ -416,8 +466,11 @@ object PackageRepository {
* @param pkgName 已安装的游戏包名
* @param cachedGameEntity 缓存的游戏实体,若存在免去再请求接口的过程
*/
fun addInstalledGame(pkgName: String,
cachedGameEntity: GameEntity? = null) {
fun addInstalledGame(
packageFilterManager: PackageFilterManager,
pkgName: String,
cachedGameEntity: GameEntity? = null
) {
mInstalledPkgSet.add(pkgName)
notifyInstallPkgData()
@ -428,7 +481,8 @@ object PackageRepository {
game = cachedGameEntity,
pkgName = pkgName,
isVGame = false,
updateInstallStatus = false)
updateInstallStatus = false
)
if (containsUpdate) {
notifyGameUpdateData()
@ -436,8 +490,9 @@ object PackageRepository {
} else {
val list = arrayListOf(pkgName)
updateFilterPackage(list) {
updateFilterPackage(packageFilterManager, list) {
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = list,
updateInstallStatus = false
)
@ -453,9 +508,11 @@ object PackageRepository {
* @param isVGame 是否为畅玩游戏
* @param updateInstallStatus 是否更新安装状态
*/
fun addInstalledGames(pkgNameList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
fun addInstalledGames(
packageFilterManager: PackageFilterManager,
pkgNameList: ArrayList<String>,
isVGame: Boolean = false,
updateInstallStatus: Boolean = false,
) {
// 畅玩游戏不添加至本地的已安装包名列表中
if (!isVGame) {
@ -465,8 +522,9 @@ object PackageRepository {
}
notifyInstallPkgData()
updateFilterPackage(pkgNameList) {
updateFilterPackage(packageFilterManager, pkgNameList) {
loadInstalledGameDigestAndNotifyData(
packageKey = packageFilterManager.packageKey,
filteredList = pkgNameList,
isVGame = isVGame,
updateInstallStatus = updateInstallStatus
@ -526,6 +584,13 @@ object PackageRepository {
changeRecentVaPlayed()
}
/**
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
*/
fun hasPendingPackage(): Boolean {
return mPendingPackageNameSet.isEmpty()
}
private fun notifyGameInstallData() {
PackagesManager.initGameInstall(ArrayList(gameInstalled))
gameInstalledLiveData.postValue(ArrayList(gameInstalled))

View File

@ -63,7 +63,13 @@ class PackageViewModel(
* @param cachedGameEntity 缓存的游戏实体
*/
fun addInstalledGame(pkgName: String?, cachedGameEntity: GameEntity? = null) {
if (!TextUtils.isEmpty(pkgName)) mRepository.addInstalledGame(pkgName!!, cachedGameEntity)
if (!TextUtils.isEmpty(pkgName)) {
mRepository.addInstalledGame(
mRepository.packageFilterManager,
pkgName!!,
cachedGameEntity
)
}
}
/**
@ -81,9 +87,7 @@ class PackageViewModel(
// 未同意获取已安装应用权限时不进行数据初始化
if (!PackageHelper.isGetInstalledPackagesAgreed()) return
if (mRepository.gameInstalled.size == 0
|| PackageFilterManager.hasPendingPackage()
) {
if (mRepository.gameInstalled.size == 0 || mRepository.hasPendingPackage()) {
PackageHelper.initPackageRelatedData();
}
}

View File

@ -699,6 +699,8 @@ class HaloPersonalFragment : BaseLazyFragment() {
} catch (e: Exception) {
} as? ITestCase)?.apply {
addInstallExternalGameButton(mStubBinding.otherItems)
addInstallPluginButton(mStubBinding.otherItems)
addInstallPlugin32Button(mStubBinding.otherItems)
}
}

View File

@ -16,11 +16,12 @@ import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.databinding.PieceBottomTabBinding
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.feature.utils.SentryHelper
/**
* 底部Tab 基类
*/
abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
protected var mViewPager: ViewPager2? = null
protected var mBottomTabContainer: LinearLayout? = null
@ -44,6 +45,7 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
protected fun initBottomTab(bottomTabList: List<BottomTab>) {
mBottomTabBindingList.clear()
mBottomTabContainer?.run {
visibility = View.VISIBLE
removeAllViews()
bottomTabList.forEachIndexed { index, bottomTab ->
addView(
@ -61,6 +63,13 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
.apply {
tabTv.text = bottomTab.name
if (!TextUtils.isEmpty(bottomTab.jsCode)) {
tabLottie.setFailureListener {
SentryHelper.onEvent(
SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED,
BOTTOM_TAB_ID, bottomTab.id,
BOTTOM_TAB_NAME, bottomTab.name
)
}
tabLottie.setAnimationFromJson(bottomTab.jsCode, bottomTab.id + bottomTab.name)
}
if (bottomTab.iconSelector != 0) {
@ -122,6 +131,13 @@ abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
protected open fun onPageSelected(position: Int) {
checkIndex(position)
}
protected open fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
protected open fun onPageScrollStateChanged(state: Int) {}
companion object {
private const val SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED = "bottom_tab_lottie_load_failed"
private const val BOTTOM_TAB_ID = "bottom_tab_id"
private const val BOTTOM_TAB_NAME = "bottom_tab_name"
}
}

View File

@ -13,6 +13,8 @@ import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.color
import androidx.core.view.doOnNextLayout
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.ethanhua.skeleton.Skeleton
import com.ethanhua.skeleton.SkeletonScreen
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.BottomTabGuideHandler
import com.gh.common.prioritychain.GlobalPriorityChainHelper
@ -48,6 +50,8 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
private var mBottomTabGuideHandler: BottomTabGuideHandler? = null
private var mBottomTabGuidePopupWindow: PopupWindow? = null
private var mSkeletonScreen: SkeletonScreen? = null
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View = mBinding.root
override fun provideAdapter(): FragmentStateAdapter = mAdapter
@ -63,10 +67,13 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
super.onCreate(savedInstanceState)
DisplayUtils.transparentStatusBar(requireActivity())
initSkeleton()
mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE
buildPriorityChain()
mViewModel?.bottomTabListLiveData?.observe(this) {
mSkeletonScreen?.hide()
mBottomTabList.clear()
mBottomTabList.addAll(it)
mViewPager?.offscreenPageLimit = it.size
@ -84,6 +91,18 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
mPriorityChain.addHandler(mBottomTabGuideHandler!!)
}
private fun initSkeleton() {
mSkeletonScreen = Skeleton.bind(mBinding.skeleton)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
.color(R.color.ui_skeleton_highlight)
.duration(Constants.SHIMMER_DURATION)
.maskWidth(Constants.MASK_WIDTH)
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.load(R.layout.fragment_main_skeleton)
.show()
}
private fun showBottomTabGuideIfExists() {
val guidePosition = mBottomTabList.indexOfFirst { it.guide != null }
if (guidePosition != -1) {

View File

@ -3,10 +3,10 @@ package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.HomeBottomBarHelper
import com.gh.common.util.HomeBottomBarHelper.isDefaultHomeBottomTabDataExist
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.common.entity.LaunchRedirect
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SingletonHolder
import com.gh.gamecenter.entity.BottomTab
@ -17,6 +17,7 @@ import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
@ -134,7 +135,13 @@ class MainWrapperRepository {
@SuppressLint("CheckResult")
fun getDataUnion() {
processBottomTabData(emptyList())
// 若历史 tab 数据存在,优先使用作为占位
if (isDefaultHomeBottomTabDataExist()) {
processBottomTabData(emptyList())
} else {
// 若 timeout 后数据未加载完成,则即便还没回调 onFailure 也生成两个底部 tab
emitDefaultTabDataAfterTimeout()
}
mNewApi.dataUnion
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<DataUnionEntity>() {
@ -232,6 +239,15 @@ class MainWrapperRepository {
multiTabNavLiveData.postValue(multiTabNav)
}
private fun emitDefaultTabDataAfterTimeout() {
CoroutineScope(SupervisorJob()).launch {
delay(3000)
if (!mMainDataIsLoaded) {
processBottomTabData(emptyList())
}
}
}
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() })
}

View File

@ -58,6 +58,7 @@ import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.time.TimeUtil
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.gh.gamecenter.feature.utils.SentryHelper
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
import com.gh.gamecenter.home.custom.CustomPageFragment
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
@ -89,7 +90,18 @@ import kotlin.math.roundToInt
* @see Style
*/
class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbarTab, ISmartRefresh, ISuperiorChain {
private val mBinding by lazy { FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater) }
private val mBinding by lazy {
try {
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
} catch (e: Exception) {
SentryHelper.onEvent("VIEW_BINDING_BIND_ERROR",
"digest", e.localizedMessage,
"gid", HaloApp.getInstance().gid
)
// 玄学,重试一次,该闪退闪退
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
}
}
private val mViewModel: SearchToolbarTabWrapperViewModel by lazy {
viewModelProviderFromParent(
SearchToolbarTabWrapperViewModel.Factory(mMultiTabNavId, mNoTabLinkEntity?.link ?: ""),

View File

@ -14,9 +14,11 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.core.app.NotificationCompat;
import com.blankj.utilcode.util.ThreadUtils;
import com.gh.download.DownloadManager;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.ndownload.suspendwindow.NDownloadDrawOverlayPermissionWindowController;
import com.gh.ndownload.suspendwindow.NDownloadSuspendWindowController;
import com.gh.ndownload.suspendwindow.utils.NDownloadSuspendWindowHelper;
@ -282,6 +284,14 @@ public class NDownloadService extends Service {
}
private void showDownloadSuspendWindowIfNeeded(DownloadEntity entry) {
if (ThreadUtils.isMainThread()) {
showDownloadSuspendWindowIfNeededInner(entry);
} else {
AppExecutor.getUiExecutor().execute(() -> showDownloadSuspendWindowIfNeededInner(entry));
}
}
private void showDownloadSuspendWindowIfNeededInner(DownloadEntity entry) {
if (!NDownloadSuspendWindowHelper.shouldSkipDownloadEntity(getApplicationContext(), entry)) {
if (NDownloadSuspendWindowHelper.canDrawOverLayer(getApplicationContext())) {// 已开启悬浮窗权限,直接显示悬浮窗
showDownloadSuspendWindow();

View File

@ -4,6 +4,7 @@ import android.app.Activity
import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Bundle
import androidx.core.view.ViewCompat
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.asVGame
@ -45,7 +46,9 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
}
fun show(activity: Activity) {
if (isStarted) return
val decorView = activity.window.decorView
val isAttachedToWindow = ViewCompat.isAttachedToWindow(decorView)
if (isStarted || !isAttachedToWindow) return
isStarted = true
currentActivityRef = WeakReference(activity)
onAttachToUi(activity)
@ -124,10 +127,10 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
SensorsBridge.trackDownloadSuspendedWindowGuideShow(
gameId = downloadEntity.gameId,
gameName = downloadEntity.name,
gameSchemeType = if(downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.categoryChinese,
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
downloadType = if(downloadEntity.asVGame()) "畅玩下载" else "本地下载"
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载"
)
}
}

View File

@ -33,7 +33,7 @@ import org.greenrobot.eventbus.ThreadMode
* 下载悬浮窗-控制器
*/
class NDownloadSuspendWindowController(private val application: Application) :
NDownloadSuspendIconLayout.OnDragListener, Application.ActivityLifecycleCallbacks,
NDownloadSuspendIconLayout.OnDragCallback, Application.ActivityLifecycleCallbacks,
PackageInstaller.OnInstallListener, NDownloadSuspendIconView.OnIconClickListener {
private var isStarted = false
@ -75,22 +75,24 @@ class NDownloadSuspendWindowController(private val application: Application) :
downloadCount = 0
currentDownloadEntity = null
onDetachFromUi()
NDataChanger.deleteObserver(downloadObserver)
PackageInstaller.unregisterOnInstallListener(this)
EventBus.getDefault().unregister(this)
application.unregisterActivityLifecycleCallbacks(this)
onDetachFromUi()
isStarted = false
}
private fun onAttachToUi() {
closeWindow.attach()
iconWindow.attach()
iconWindow.setOnDragListener(this)
iconWindow.setOnDragCallback(this)
iconWindow.setOnIconClickListener(this)
}
private fun onDetachFromUi() {
iconWindow.setOnDragCallback(null)
iconWindow.setOnIconClickListener(null)
closeWindow.detach()
iconWindow.detach()
}

View File

@ -12,6 +12,7 @@ import com.gh.download.DownloadManager
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.asVGame
import com.gh.gamecenter.common.utils.getExtension
import com.gh.gamecenter.common.utils.isLocalDownloadInDualDownloadMode
import com.gh.gamecenter.common.utils.isSimulatorDownload
import com.gh.gamecenter.common.utils.isSimulatorGame
import com.gh.gamecenter.core.utils.SPUtils
@ -184,6 +185,7 @@ object NDownloadSuspendWindowHelper {
|| downloadEntity.isPluggable
|| downloadEntity.isSimulatorDownload()
|| downloadEntity.isSimulatorGame()
|| downloadEntity.isLocalDownloadInDualDownloadMode()
@JvmStatic
fun isDrawOverlayPermissionWindowShown(): Boolean =

View File

@ -26,7 +26,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
ViewConfiguration.get(context).scaledTouchSlop
}
private var onDragListener: OnDragListener? = null
private var onDragCallback: OnDragCallback? = null
val icon = NDownloadSuspendIconView(context).also {
it.clipChildren = false
@ -39,12 +39,12 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
addView(it, LayoutParams(size, size))
}
fun setOnDragListener(onDragListener: OnDragListener) {
this.onDragListener = onDragListener
fun setOnDragCallback(onDragCallback: OnDragCallback?) {
this.onDragCallback = onDragCallback
}
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
val onDragListener = this.onDragListener
val onDragCallback = this.onDragCallback
?: return super.onInterceptTouchEvent(ev)
if (ev.action == MotionEvent.ACTION_DOWN) {
@ -66,7 +66,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
val dragging = xDiff > touchSlop || yDiff > touchSlop
if (dragging) {
isDragging = true
onDragListener.onDragStart(this)
onDragCallback.onDragStart(this)
return true
}
return super.onInterceptTouchEvent(ev)
@ -81,7 +81,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
@SuppressLint("ClickableViewAccessibility")
override fun onTouchEvent(ev: MotionEvent): Boolean {
val onDragListener = this.onDragListener
val onDragListener = this.onDragCallback
?: return super.onInterceptTouchEvent(ev)
when(ev.action) {
@ -93,7 +93,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
return super.onTouchEvent(ev)
}
interface OnDragListener {
interface OnDragCallback {
fun onDragStart(v: View)

View File

@ -109,7 +109,7 @@ class NDownloadSuspendIconView @JvmOverloads constructor(
addView(it, layoutParams)
}
fun setOnIconClickListener(listener: OnIconClickListener) {
fun setOnIconClickListener(listener: OnIconClickListener?) {
this.onIconClickListener = listener
}

View File

@ -56,6 +56,13 @@ class NDownloadSuspendCloseWindow(context: Context, suspend: Boolean = true) :
it.visibility = View.GONE
}
override fun onDetach() {
popupAnimator.removeListener(hiddenListener)
popupAnimator.removeListener(shownListener)
popupAnimator.cancel()
root.visibility = View.GONE
}
override fun onViewCreated(view: NDownloadSuspendCloseLayout) {
view.apply {
popupAnimator.addUpdateListener { value ->

View File

@ -19,7 +19,7 @@ import com.gh.ndownload.suspendwindow.view.NDownloadSuspendIconView
*/
class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, private val draggable: Boolean = true) :
NDownloadSuspendWindow<NDownloadSuspendIconLayout>(context, suspend),
NDownloadSuspendIconLayout.OnDragListener {
NDownloadSuspendIconLayout.OnDragCallback {
companion object {
private const val TYPE_DRAGGING = 0
@ -40,7 +40,7 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
/**
* 图标拖动回调事件
*/
private var onDragListener: NDownloadSuspendIconLayout.OnDragListener? = null
private var onDragCallback: NDownloadSuspendIconLayout.OnDragCallback? = null
/**
* 手指松开拖动的悬浮图标时,自动吸附到屏幕侧边的动画
@ -67,20 +67,30 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
get() = root.getLocationOnScreen(screenLocation)
.let { screenLocation[1] - layoutParams.y }
fun setOnDragListener(onDragListener: NDownloadSuspendIconLayout.OnDragListener?) {
this.onDragListener = onDragListener
fun setOnDragCallback(onDragCallback: NDownloadSuspendIconLayout.OnDragCallback?) {
this.onDragCallback = onDragCallback
}
override fun detach() {
super.detach()
root.removeCallbacks(initRunnable)
override fun onAttach() {
if (draggable) {
root.post(initRunnable)
root.setOnDragCallback(this)
}
}
override fun onDetach() {
if (draggable) {
root.removeCallbacks(initRunnable)
root.setOnDragCallback(null)
}
edgeAnimator.removeAllUpdateListeners()
edgeAnimator.removeAllListeners()
edgeAnimator.cancel()
}
@SuppressLint("InflateParams")
override fun onCreateView(context: Context): NDownloadSuspendIconLayout =
NDownloadSuspendIconLayout(context).also {
if (draggable) it.setOnDragListener(this)
it.clipChildren = false
it.clipToPadding = false
}
@ -96,7 +106,6 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
override fun onViewCreated(view: NDownloadSuspendIconLayout) {
super.onViewCreated(view)
view.setBackgroundResource(R.drawable.shape_download_suspend_icon_idle_right)
if (draggable) view.post(initRunnable)
}
override fun onConfigurationChanged(configuration: Configuration) {
@ -135,7 +144,7 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
root.icon.setDownloadIconUrl(url)
}
fun setOnIconClickListener(listener: NDownloadSuspendIconView.OnIconClickListener) {
fun setOnIconClickListener(listener: NDownloadSuspendIconView.OnIconClickListener?) {
root.icon.setOnIconClickListener(listener)
}
@ -203,16 +212,16 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
override fun onDragStart(v: View) {
setIconLayoutStyleByType(TYPE_DRAGGING)
onDragListener?.onDragStart(v)
onDragCallback?.onDragStart(v)
}
override fun onDragMove(v: View, x: Float, y: Float) {
moveToPosition(v, x, y)
onDragListener?.onDragMove(v, x, y)
onDragCallback?.onDragMove(v, x, y)
}
override fun onDragEnd(v: View, x: Float, y: Float) {
animateToEdge(v, x, y)
onDragListener?.onDragEnd(v, x, y)
onDragCallback?.onDragEnd(v, x, y)
}
}

View File

@ -46,22 +46,35 @@ abstract class NDownloadSuspendWindow<T : View>(
onViewCreated(it)
}
open fun attach() {
fun attach() {
if (!isAttached) {
_isAttached = true
windowManager.addView(root, layoutParams)
context.registerComponentCallbacks(this)
try {
_isAttached = true
windowManager.addView(root, layoutParams)
context.registerComponentCallbacks(this)
onAttach()
} catch (e: Throwable) {
// 处理“Unable to add window android.view.ViewRootImpl$W@7bc9502 -- permission denied for window type 2038”的异常
// 目前Sentry上仅Z10型号的手机报这个错误Android 8.1.0这里明明已经授予了SYSTEM_ALERT_WINDOW权限但是就是会抛异常= =,只能暴力捕获处理了。。。
// 相关异常的链接https://sentry.shanqu.cc/organizations/lightgame/issues/407275/?project=22
_isAttached = false
}
}
}
open fun detach() {
fun detach() {
if (isAttached) {
_isAttached = false
onDetach()
context.unregisterComponentCallbacks(this)
windowManager.removeView(root)
}
}
protected open fun onAttach() = Unit
protected open fun onDetach() = Unit
protected open fun onViewCreated(view: T) = Unit
override fun onConfigurationChanged(configuration: Configuration) = Unit

View File

@ -8,4 +8,6 @@ public interface ITestCase {
void addInstallExternalGameButton(@NonNull ViewGroup viewParent);
void addInstallPluginButton(@NonNull ViewGroup viewParent);
void addInstallPlugin32Button(@NonNull ViewGroup viewParent);
}

View File

@ -56,6 +56,7 @@ import com.gh.gamecenter.history.HistoryActivity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.mygame.MyGameActivity
import com.gh.gamecenter.packagehelper.PackageFilterManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.subject.SubjectActivity
@ -1997,7 +1998,7 @@ object VHelper {
validInstalledPackageList.add(packageName)
}
}
PackageRepository.addInstalledGames(validInstalledPackageList, true)
PackageRepository.addInstalledGames(PackageFilterManager(), validInstalledPackageList, true)
}
/**
@ -2277,6 +2278,13 @@ object VHelper {
val token = UserManager.getInstance().token
val userName = UserManager.getInstance().userInfoEntity?.name
val userAvatar = UserManager.getInstance().userInfoEntity?.icon
if (token == null) {
SentryHelper.onEvent("LAUNCH_SUCCESS_BUT_TOKEN_EMPTY", "gid", HaloApp.getInstance().gid)
Utils.log(LOG_TAG, "登录成功,但 token 为空")
return@runOnIoThread
}
Utils.log(
LOG_TAG,
"登录成功,插入用户信息:token=${token},userName=${userName}, uri=${va.getUriAuthorizationString()}"

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

View File

@ -0,0 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<shape xmlns:android="http://schemas.android.com/apk/res/android"
android:shape="rectangle">
<corners android:radius="20dp" />
<solid android:color="@color/ui_skeleton_frame" />
</shape>

View File

@ -142,7 +142,14 @@
android:focusable="true"
android:gravity="center_vertical"
android:orientation="horizontal"
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent" />
<FrameLayout
android:id="@+id/skeleton"
android:layout_width="match_parent"
android:layout_height="match_parent" />
</com.gh.gamecenter.common.view.MaterializedConstraintLayout>

View File

@ -0,0 +1,316 @@
<?xml version="1.0" encoding="utf-8"?>
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_above="@id/b"
android:orientation="vertical">
<com.gh.gamecenter.common.view.StatusBarView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="48dp"
android:paddingStart="16dp"
android:paddingEnd="16dp">
<View
android:id="@+id/sb"
android:layout_width="0dp"
android:layout_height="32dp"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_skeleton_radius_40"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ic1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/ic1"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_skeleton_radius_4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/ic2"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/ic2"
android:layout_width="24dp"
android:layout_height="24dp"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_skeleton_radius_4"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintTop_toTopOf="parent" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="28dp"
android:gravity="bottom"
android:paddingTop="8dp">
<View
android:layout_width="40dp"
android:layout_height="20dp"
android:layout_marginStart="16dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="64dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="64dp"
android:layout_height="16dp"
android:layout_marginStart="24dp"
android:background="@drawable/bg_skeleton_radius_4" />
</LinearLayout>
<ImageView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_marginStart="29dp"
android:layout_marginTop="7dp"
android:src="@drawable/ic_home_tab_indicator_skeleton" />
<androidx.constraintlayout.widget.ConstraintLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="10dp"
android:paddingHorizontal="16dp"
android:paddingVertical="8dp">
<View
android:id="@+id/square"
android:layout_width="0dp"
android:layout_height="140dp"
android:layout_marginEnd="8dp"
android:background="@drawable/bg_skeleton_radius_10"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/rect1"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<View
android:id="@+id/rect1"
android:layout_width="0dp"
android:layout_height="66dp"
android:background="@drawable/bg_skeleton_radius_10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toEndOf="@id/square"
app:layout_constraintTop_toTopOf="@id/square" />
<View
android:id="@+id/rect2"
android:layout_width="0dp"
android:layout_height="66dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_10"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintHorizontal_bias="0"
app:layout_constraintHorizontal_chainStyle="packed"
app:layout_constraintStart_toStartOf="@id/rect1"
app:layout_constraintTop_toBottomOf="@id/rect1" />
</androidx.constraintlayout.widget.ConstraintLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="87dp"
android:orientation="horizontal"
android:paddingHorizontal="18dp"
android:paddingVertical="16dp">
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1"
android:background="@drawable/bg_skeleton_radius_8" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="26dp"
android:layout_weight="1"
android:background="@drawable/bg_skeleton_radius_8" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="26dp"
android:layout_weight="1"
android:background="@drawable/bg_skeleton_radius_8" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="26dp"
android:layout_weight="1"
android:background="@drawable/bg_skeleton_radius_8" />
<View
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_marginStart="26dp"
android:layout_weight="1"
android:background="@drawable/bg_skeleton_radius_8" />
</LinearLayout>
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="vertical"
android:paddingTop="16dp"
android:paddingBottom="16dp">
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="18dp">
<View
android:layout_width="54dp"
android:layout_height="18dp"
android:layout_marginStart="16dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="12dp"
android:layout_alignParentEnd="true"
android:layout_marginEnd="16dp"
android:background="@drawable/bg_skeleton_radius_4" />
</RelativeLayout>
<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginTop="16dp">
<View
android:layout_width="match_parent"
android:layout_height="1dp"
android:layout_marginTop="53dp"
android:background="@drawable/bg_skeleton_radius_4" />
<HorizontalScrollView
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:scrollbars="none">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginStart="8dp"
android:orientation="horizontal">
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
</LinearLayout>
</HorizontalScrollView>
</RelativeLayout>
</LinearLayout>
<include layout="@layout/fragment_main_skeleton_item_game" />
<include layout="@layout/fragment_main_skeleton_item_game" />
<include layout="@layout/fragment_main_skeleton_item_game" />
</LinearLayout>
<LinearLayout
android:id="@+id/b"
android:layout_width="match_parent"
android:layout_height="54dp"
android:layout_alignParentBottom="true"
android:orientation="horizontal">
<include
layout="@layout/fragment_main_skeleton_item_bottom_tab"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<include
layout="@layout/fragment_main_skeleton_item_bottom_tab"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<include
layout="@layout/fragment_main_skeleton_item_bottom_tab"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<include
layout="@layout/fragment_main_skeleton_item_bottom_tab"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
<include
layout="@layout/fragment_main_skeleton_item_bottom_tab"
android:layout_width="0dp"
android:layout_height="match_parent"
android:layout_weight="1" />
</LinearLayout>
</RelativeLayout>

View File

@ -0,0 +1,26 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="match_parent"
android:layout_height="match_parent"
android:gravity="center"
android:orientation="vertical">
<View
android:layout_width="22dp"
android:layout_height="22dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="22dp"
android:layout_height="10dp"
android:layout_marginTop="6dp"
android:background="@drawable/bg_skeleton_radius_4" />
</LinearLayout>
</LinearLayout>

View File

@ -0,0 +1,47 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:paddingLeft="16dp"
android:paddingTop="8dp"
android:paddingRight="16dp"
android:paddingBottom="8dp">
<View
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@drawable/bg_skeleton_radius_14" />
<LinearLayout
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginLeft="12dp"
android:layout_marginTop="4dp"
android:layout_weight="1"
android:orientation="vertical">
<View
android:layout_width="72dp"
android:layout_height="16dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="160dp"
android:layout_height="12dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="32dp"
android:layout_height="12dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4" />
</LinearLayout>
<View
android:layout_width="56dp"
android:layout_height="28dp"
android:layout_gravity="center_vertical"
android:background="@drawable/bg_skeleton_radius_999" />
</LinearLayout>

View File

@ -0,0 +1,49 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
<LinearLayout
android:layout_width="80dp"
android:layout_height="wrap_content"
android:gravity="center_horizontal"
android:orientation="vertical">
<View
android:layout_width="28dp"
android:layout_height="11dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="44dp"
android:layout_height="11dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4"/>
<View
android:layout_width="5dp"
android:layout_height="5dp"
android:layout_marginTop="5dp"
android:layout_marginBottom="5dp"
android:background="@drawable/bg_skeleton_radius_40"/>
<View
android:layout_width="64dp"
android:layout_height="64dp"
android:background="@drawable/bg_skeleton_radius_14" />
<View
android:layout_width="64dp"
android:layout_height="16dp"
android:layout_marginTop="8dp"
android:background="@drawable/bg_skeleton_radius_4" />
<View
android:layout_width="56dp"
android:layout_height="28dp"
android:layout_marginTop="12dp"
android:background="@drawable/bg_skeleton_radius_40" />
</LinearLayout>
</LinearLayout>

View File

@ -17,8 +17,8 @@ buildscript {
password("u9gZYH4MQEwLLQZK")
}
}
maven { url 'https://jitpack.io' }
maven { url "https://maven.google.com" }
maven { url 'https://jitpack.io' }
}
dependencies {
@ -41,7 +41,8 @@ allprojects {
google()
jcenter()
mavenCentral()
maven { url 'https://jitpack.io' }
maven { url 'https://maven.aliyun.com/repository/public' }
maven { url 'https://maven.aliyun.com/repository/central'}
maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
maven { url 'https://artifact.bytedance.com/repository/Volcengine/'}
maven { url 'https://artifact.bytedance.com/repository/pangle' }
@ -53,6 +54,7 @@ allprojects {
// 配置HMS Core SDK的Maven仓地址。
maven { url 'https://developer.huawei.com/repo/' }
maven { url 'https://developer.hihonor.com/repo' }
maven { url 'https://jitpack.io' }
}
}
task clean(type: Delete) {

View File

@ -7,8 +7,8 @@ ext {
targetSdkVersion = 30
// application info (每个大版本之间的 versionCode 增加 20)
versionCode = 1110
versionName = "5.38.0"
versionCode = 1095
versionName = "5.37.5"
applicationId = "com.gh.gamecenter"
applicationIdGat = "com.gh.gamecenter.intl"

View File

@ -1,5 +1,5 @@
ext {
vaCompileSdkVersion = 33
vaCompileSdkVersion = 34
vaMinSdkVersion = 21
vaTargetSdkVersion = 28
}

View File

@ -323,14 +323,11 @@ class HelpAndFeedbackFragment : BaseLazyFragment() {
mPopupWindow = null
}
}
popupWindow.width = mBinding.helpCenterTv.width
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = 0
popupWindow.showAsDropDown(mBinding.helpCenterTv, 0, 0)
binding.root.layoutParams = (binding.root.layoutParams as MarginLayoutParams).apply {
leftMargin = 16F.dip2px()
rightMargin = 16F.dip2px()
}
}
private fun toggleHighlightedView(targetView: View, highlightIt: Boolean) {

View File

@ -4,7 +4,6 @@ import androidx.fragment.app.Fragment
import androidx.recyclerview.widget.DiffUtil
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.core.AppExecutor
/**
* 支持diff刷新的FragmentStateAdapter
@ -64,7 +63,7 @@ abstract class BaseDiffFragmentStateAdapter<T>(fragment: Fragment) : FragmentSta
abstract fun createFragment(data: T?, position: Int): Fragment?
override fun getItemId(position: Int): Long {
if (mDataList.size < position) {
if (mDataList.size <= position) {
return 0L
}
return mDataList[position].hashCode().toLong()

View File

@ -14,11 +14,11 @@ import android.view.ViewGroup
import androidx.annotation.RequiresApi
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.databinding.DialogAlertDefaultBinding
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IAppProvider
import com.lightgame.dialog.BaseDialogFragment
@RequiresApi(Build.VERSION_CODES.R)
class ManageExternalStoragePermissionDialogFragment : BaseDialogFragment() {

View File

@ -26,7 +26,7 @@ object SensorsBridge {
private const val KEY_LAST_PAGE_BUSINESS_ID = "last_page_business_id"
private const val KEY_DOWNLOAD_STATUS = "download_status"
private const val KEY_DOWNLOAD_TYPE = "download_type"
private const val KEY_GAME_SCHEME_TYPE = "game_scheme_type"
private const val KEY_GAME_SCHEMA_TYPE = "game_schema_type"
private const val KEY_GAME_TYPE = "game_type"
const val KEY_POSITION = "position"
const val KEY_TAB_CONTENT = "tab_content"
@ -4070,7 +4070,7 @@ object SensorsBridge {
* 触发时机:当下载悬浮窗引导图展示时触发上报
* @param gameId 游戏ID
* @param gameName 游戏名称
* @param gameSchemeType 游戏架构类型64位/32位
* @param gameSchemaType 游戏架构类型64位/32位
* @param downloadStatus 游戏下载状态
* @param gameType 游戏的类型:单机、网游等
* @param downloadType 实际下载方式:本地下载/畅玩下载
@ -4079,7 +4079,7 @@ object SensorsBridge {
fun trackDownloadSuspendedWindowGuideShow(
gameId: String,
gameName: String,
gameSchemeType: String,
gameSchemaType: String,
downloadStatus: String,
gameType: String,
downloadType: String
@ -4087,7 +4087,7 @@ object SensorsBridge {
val json = json {
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_TYPE to gameType
KEY_DOWNLOAD_TYPE to downloadType
@ -4126,7 +4126,7 @@ object SensorsBridge {
* @param source 来源:游戏下载\重启APP
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
* @param downloadStatus 所上报游戏下载状态
* @param gameSchemeType 所上报游戏架构类型64位/32位
* @param gameSchemaType 所上报游戏架构类型64位/32位
* @param gameType 游戏的类型:单机、网游等
* @param gameId 游戏ID
* @param gameName 游戏名称
@ -4135,7 +4135,7 @@ object SensorsBridge {
source: String,
downloadType: String,
downloadStatus: String,
gameSchemeType: String,
gameSchemaType: String,
gameType: String,
gameId: String,
gameName: String
@ -4144,7 +4144,7 @@ object SensorsBridge {
KEY_SOURCE to source
KEY_DOWNLOAD_TYPE to downloadType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_GAME_TYPE to gameType
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
@ -4159,7 +4159,7 @@ object SensorsBridge {
* @param source 来源:游戏下载\重启APP
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
* @param downloadStatus 所上报游戏下载状态
* @param gameSchemeType 所上报游戏架构类型64位/32位
* @param gameSchemaType 所上报游戏架构类型64位/32位
* @param gameType 游戏的类型:单机、网游等
* @param gameId 游戏ID
* @param gameName 游戏名称
@ -4168,7 +4168,7 @@ object SensorsBridge {
source: String,
downloadType: String,
downloadStatus: String,
gameSchemeType: String,
gameSchemaType: String,
gameType: String,
gameId: String,
gameName: String
@ -4177,7 +4177,7 @@ object SensorsBridge {
KEY_SOURCE to source
KEY_DOWNLOAD_TYPE to downloadType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_GAME_TYPE to gameType
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
@ -4191,7 +4191,7 @@ object SensorsBridge {
* 触发时机:触发自动下载提示条展示时上报
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
* @param downloadStatus 所上报游戏下载状态
* @param gameSchemeType 所上报游戏架构类型64位/32位
* @param gameSchemaType 所上报游戏架构类型64位/32位
* @param gameType 游戏的类型:单机、网游等
* @param gameId 游戏ID
* @param gameName 游戏名称
@ -4199,7 +4199,7 @@ object SensorsBridge {
fun trackAutomaticInstallationPromptBarShow(
downloadType: String,
downloadStatus: String,
gameSchemeType: String,
gameSchemaType: String,
gameType: String,
gameId: String,
gameName: String
@ -4207,7 +4207,7 @@ object SensorsBridge {
val json = json {
KEY_DOWNLOAD_TYPE to downloadType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_GAME_TYPE to gameType
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
@ -4221,7 +4221,7 @@ object SensorsBridge {
* 触发时机:触发自动下载提示条点击时上报
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
* @param downloadStatus 所上报游戏下载状态
* @param gameSchemeType 所上报游戏架构类型64位/32位
* @param gameSchemaType 所上报游戏架构类型64位/32位
* @param gameType 游戏的类型:单机、网游等
* @param gameId 游戏ID
* @param gameName 游戏名称
@ -4229,7 +4229,7 @@ object SensorsBridge {
fun trackAutomaticInstallationPromptBarClick(
downloadType: String,
downloadStatus: String,
gameSchemeType: String,
gameSchemaType: String,
gameType: String,
gameId: String,
gameName: String
@ -4237,7 +4237,7 @@ object SensorsBridge {
val json = json {
KEY_DOWNLOAD_TYPE to downloadType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_GAME_TYPE to gameType
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
@ -4251,7 +4251,7 @@ object SensorsBridge {
* 触发时机:下载组件展示时上报
* @param gameId 游戏ID
* @param gameName 游戏名称
* @param gameSchemeType 游戏架构类型64位/32位
* @param gameSchemaType 游戏架构类型64位/32位
* @param downloadStatus 所上报游戏下载状态
* @param gameType 游戏的类型:单机、网游等
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
@ -4259,7 +4259,7 @@ object SensorsBridge {
fun trackDownloadComponentsShow(
gameId: String,
gameName: String,
gameSchemeType: String,
gameSchemaType: String,
downloadStatus: String,
gameType: String,
downloadType: String,
@ -4267,7 +4267,7 @@ object SensorsBridge {
val json = json {
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_TYPE to gameType
KEY_DOWNLOAD_TYPE to downloadType
@ -4281,7 +4281,7 @@ object SensorsBridge {
* 触发时机:下载组件点击时上报
* @param gameId 游戏ID
* @param gameName 游戏名称
* @param gameSchemeType 游戏架构类型64位/32位
* @param gameSchemaType 游戏架构类型64位/32位
* @param downloadStatus 所上报游戏下载状态
* @param gameType 游戏的类型:单机、网游等
* @param downloadType 所上报游戏的实际下载方式:本地下载/畅玩下载
@ -4291,7 +4291,7 @@ object SensorsBridge {
fun trackDownloadComponentsContentClick(
gameId: String,
gameName: String,
gameSchemeType: String,
gameSchemaType: String,
downloadStatus: String,
gameType: String,
downloadType: String,
@ -4301,7 +4301,7 @@ object SensorsBridge {
val json = json {
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
KEY_GAME_SCHEME_TYPE to gameSchemeType
KEY_GAME_SCHEMA_TYPE to gameSchemaType
KEY_DOWNLOAD_STATUS to downloadStatus
KEY_GAME_TYPE to gameType
KEY_DOWNLOAD_TYPE to downloadType

View File

@ -6,6 +6,8 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import java.io.BufferedReader;
import java.io.FileReader;
import java.lang.reflect.Method;
public class ProcessUtil {
@ -20,13 +22,25 @@ public class ProcessUtil {
return currentProcessName;
}
//1)通过Application的API获取当前进程名
//0)通过反射 ActivityThread 获取实例再获取当前进程名
currentProcessName = getCurrentProcessNameByActivityThreadInstance();
if (!TextUtils.isEmpty(currentProcessName)) {
return currentProcessName;
}
//1)通过 CMD 获取当前进程名
currentProcessName = getCurrentProcessNameByCMD(android.os.Process.myPid());
if (!TextUtils.isEmpty(currentProcessName)) {
return currentProcessName;
}
//2)通过 Application 的 API 获取当前进程名
currentProcessName = getCurrentProcessNameByApplication();
if (!TextUtils.isEmpty(currentProcessName)) {
return currentProcessName;
}
//2)通过反射ActivityThread获取当前进程名
//3)通过反射 ActivityThread 获取当前进程名
currentProcessName = getCurrentProcessNameByActivityThread();
if (!TextUtils.isEmpty(currentProcessName)) {
return currentProcessName;
@ -38,7 +52,7 @@ public class ProcessUtil {
/**
* 通过Application新的API获取进程名无需反射无需IPC效率最高。
*/
public static String getCurrentProcessNameByApplication() {
private static String getCurrentProcessNameByApplication() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
return Application.getProcessName();
}
@ -48,7 +62,7 @@ public class ProcessUtil {
/**
* 通过反射ActivityThread获取进程名避免了ipc
*/
public static String getCurrentProcessNameByActivityThread() {
private static String getCurrentProcessNameByActivityThread() {
String processName = null;
try {
final Method declaredMethod = Class.forName("android.app.ActivityThread", false, Application.class.getClassLoader())
@ -64,4 +78,52 @@ public class ProcessUtil {
return processName;
}
private static String getCurrentProcessNameByActivityThreadInstance() {
try {
// Get ActivityThread class
Class<?> activityThreadClass = Class.forName("android.app.ActivityThread",false, Application.class.getClassLoader());
// Get the current ActivityThread instance
Method currentActivityThreadMethod = activityThreadClass.getDeclaredMethod("currentActivityThread");
currentActivityThreadMethod.setAccessible(true);
Object activityThread = currentActivityThreadMethod.invoke(null);
// Get the getProcessName method
Method getProcessNameMethod = activityThreadClass.getDeclaredMethod("getProcessName");
getProcessNameMethod.setAccessible(true);
// Call the getProcessName method
return (String) getProcessNameMethod.invoke(activityThread);
} catch (Exception e) {
e.printStackTrace();
return null;
}
}
private static String getCurrentProcessNameByCMD(int pid) {
//get from /proc/PID/cmdline
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader("/proc/" + pid + "/cmdline"));
String processName = br.readLine();
if (!TextUtils.isEmpty(processName)) {
processName = processName.trim();
if (!TextUtils.isEmpty(processName)) {
return processName; //OK
}
}
} catch (Exception ignored) {
} finally {
try {
if (br != null) {
br.close();
}
} catch (Exception ignored) {
}
}
//failed
return null;
}
}

View File

@ -62,16 +62,14 @@ class AboutFragment : ToolbarFragment() {
ARouter.getInstance().build(RouteConsts.provider.updateManager).navigation() as? IUpdateManagerProvider
updateManager?.checkUpdate(requireActivity(), true) // 检查更新
if (PackageFlavorHelper.IS_TEST_FLAVOR) {
var clickCount = 0
mBinding.aboutGhIcon.setOnClickListener {
if (clickCount == 5) {
mBinding.aboutGhIcon.setOnClickListener(null)
mBinding.deviceContent.visibility = View.VISIBLE
mBinding.deviceContent.text.toString().copyTextAndToast("复制成功!")
} else {
clickCount++
}
var clickCount = 0
mBinding.aboutGhIcon.setOnClickListener {
if (clickCount == 5) {
mBinding.aboutGhIcon.setOnClickListener(null)
mBinding.deviceContent.visibility = View.VISIBLE
mBinding.deviceContent.text.toString().copyTextAndToast("复制成功!")
} else {
clickCount++
}
}

View File

@ -131,6 +131,7 @@ class ComposeAboutActivity : ComposeBaseActivity() {
}
},
onPress = {
if (deviceContent.isNotEmpty()) return@detectTapGestures
if (clickCount == 5) {
val sb = StringBuilder()
val meta = MetaUtil.getMeta()

View File

@ -49,6 +49,18 @@ android {
publish {
dimension "env"
}
tea {
dimension "env"
}
kuaishou {
dimension "env"
}
gdt {
dimension "env"
}
sm {
dimension "env"
}
}
}

View File

@ -3,15 +3,9 @@ package com.gh.gamecenter.va
import android.app.Application
import android.content.Context
import android.content.res.Configuration
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.iinterface.IApplication
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.ISentryProvider
import com.google.auto.service.AutoService
import com.lg.vspace.common.CommonApp
import com.lody.virtual.client.core.VirtualCore
import com.lody.virtual.client.core.VirtualCore.VirtualInitializer
@AutoService(IApplication::class)
class HaloApp : IApplication {
@ -23,20 +17,6 @@ class HaloApp : IApplication {
}
override fun onCreate(application: Application) {
VirtualCore.get().initialize(object : VirtualInitializer() {
override fun onServerProcess() {
(ARouter.getInstance().build(RouteConsts.provider.app)
.navigation() as? IAppProvider)?.let { appProvider ->
(ARouter.getInstance().build(RouteConsts.provider.sentry).navigation() as? ISentryProvider)?.init(
application,
appProvider.getChannel(),
appProvider.getFlavor(),
appProvider.getAppVersion()
)
}
}
})
commonApp.onCreate(application)
}

2
vasdk

Submodule vasdk updated: e64cec2695...ff118306e4