Compare commits

..

65 Commits

Author SHA1 Message Date
e1e4355491 feat: 【光环助手】极光推送接入VA合并包 https://jira.shanqu.cc/browse/GHZSCY-5621 2024-06-04 14:06:24 +08:00
843214e070 fix: 修复32位畅玩游戏助手被卸载的云存档交互崩溃 https://sentry.shanqu.cc/organizations/lightgame/issues/355607/?project=22 2024-06-03 14:46:49 +08:00
3adfc797b8 fix:去掉英文 2024-05-31 14:27:09 +08:00
87d434f4ad fix: remove启动服务导致的崩溃,由于包渠道不会触发SensorLogService的业务逻辑,先不启动。 2024-05-31 13:11:19 +08:00
777ce3fe35 fix: remove启动服务导致的崩溃,由于包渠道不会触发SensorLogService的业务逻辑,先不启动。 2024-05-31 11:57:29 +08:00
22d19c35e1 fix: 获取网络状态报错 & 资源覆盖导致深色主题UI异常 2024-05-30 16:54:23 +08:00
0780ba5941 fix:史宾格检测隐私政策前读取了系统属性 2024-05-30 11:17:23 +08:00
0a3235db35 refactor: sync id with 光环助手5.35.5 2024-05-29 18:07:43 +08:00
3e4d1e2bf0 refactor: sync id with 光环助手5.35.5 2024-05-29 18:04:27 +08:00
d502453420 refactor: sync vasdk 2024-05-29 17:28:15 +08:00
97ff960dde refactor: 取消assumenosideeffects注释 & 修改版本号 & update git submodule 2024-05-29 16:27:38 +08:00
4b0f4d1545 fix: 上传云存档弹窗时间1970年的问题 2024-05-29 14:56:20 +08:00
d231e73f44 refactor: 删除插件文件,自动打包会下载 2024-05-29 14:15:05 +08:00
8c1145c301 refactor: sync va commit id & upload plugin files. 2024-05-29 11:43:15 +08:00
8246cae6af refactor: sync commit id. 2024-05-28 17:22:15 +08:00
2c02f601a5 refactor: 插件删除实名部分,实名在va实现 2024-05-28 16:00:25 +08:00
18f213ec7d refactor: upgrade va version 2024-05-27 21:01:37 +08:00
9d7c533207 feat: merge va 2024-05-27 20:47:18 +08:00
54ee8a9c69 chore: 版本更新至 5.35.5 2024-05-16 11:36:57 +08:00
2b44efd6b3 Merge branch 'hotfix/v5.35.4-1054/wrong_install_status' into 'release'
fix: 修复安装游戏回到光环时的安装状态更新问题

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

4
.gitignore vendored
View File

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

View File

@ -71,7 +71,7 @@ android_build:
exit_codes: 137
only:
- dev
- feat/GHZSCY-5250
- release
# 代码检查
sonarqube_analysis:
@ -152,4 +152,4 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail-jira-comment.py
only:
- dev
- feat/GHZSCY-5250
- release

3
.gitmodules vendored
View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -23,6 +23,7 @@ import com.gh.vspace.VArchiveHelper
import com.gh.vspace.VHelper
import com.lg.download.DownloadError
import com.lg.download.DownloadStatus
import com.lody.virtual.client.core.VirtualCore
import io.reactivex.schedulers.Schedulers
import org.greenrobot.eventbus.EventBus
import splitties.systemservices.layoutInflater
@ -50,8 +51,6 @@ object ArchiveDownloadButtonHelper {
}
downloadBtn.setOnClickListener {
when {
// 检查是否已安装畅玩助手
!VHelper.isVSpaceInstalled(context) -> showVSpaceTipDialog(context, gameEntity)
// 检查是否已安装游戏
!VHelper.isInstalled(packageName) -> {
// 检查游戏是否在安装中
@ -273,14 +272,18 @@ object ArchiveDownloadButtonHelper {
R.string.archive_apply.toResString(),
R.string.cancel.toResString(),
{
applyArchive(context, entrance, packageName, config, archiveEntity, gameEntity)
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "使用")
SensorsBridge.trackEvent(
"CloudSaveOverwriteDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"button_name", "使用"
)
if(VHelper.isInnerInstalled(packageName) && !VirtualCore.get().isExtPackageInstalled) {
VHelper.newCwValidateVspaceBeforeAction(context, gameEntity) {}
} else {
applyArchive(context, entrance, packageName, config, archiveEntity, gameEntity)
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "使用")
SensorsBridge.trackEvent(
"CloudSaveOverwriteDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"button_name", "使用"
)
}
},
{
NewFlatLogUtils.logCloudArchiveApplyDialogRelated("cloud_save_overwrite_dialog_click", "取消")

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,5 +1,6 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
@ -7,12 +8,29 @@ import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.os.Build
import android.provider.Settings
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.constant.Config
import com.gh.common.prioritychain.GlobalPriorityChainHelper
import com.gh.common.prioritychain.RequestInstalledListPermissionHandler
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.common.utils.PermissionHelper.isGetInstalledListPermissionDisabled
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.WhitePackageListEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
@ -29,10 +47,15 @@ object PackageHelper {
private const val UNSUPPORTED = 0
private const val SUPPORTED = 1
private const val ENABLED = 2
private const val DISABLED = 3
private var lastInstalledPackageListTime = 0L
private var installedPackageList: List<PackageInfo> = arrayListOf()
private var isGetInstalledPackagesApiAgreed = false
private var isGetInstalledListPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表。-1 代表支持情况未知0 代表不支持, 1 代表支持
private var cachedInstalledPackagesList: List<PackageInfo> = arrayListOf()
private var isGetInstalledPackagesApiAgreed = true // 用户是否已经同意使用已安装应用列表 API
private var isGetInstalledPackagesApiAgreedRequired = DISABLED // 需要用户手动授权才获取已安装应用列表的功能的开关
private var isGetInstalledPackagesPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
private var _commentPackageNameBlackList = arrayListOf<String>()
@ -50,6 +73,10 @@ object PackageHelper {
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
// 本地已安装包的列表
var localPackageNameSet = hashSetOf<String>()
get() {
@ -61,6 +88,22 @@ object PackageHelper {
}
}
/**
* 获取已安装的白名单列表(为了在没有已安装应用列表获取能力的时候也能正常判断更新、插件化)
*/
@SuppressLint("CheckResult")
fun getInstalledWhiteList() {
RetrofitManager.getInstance().newApi.installWhitelist
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<WhitePackageListEntity>() {
override fun onSuccess(data: WhitePackageListEntity) {
data.data?.let {
addInstalledButMissingPackages(it)
}
}
})
}
@JvmStatic
fun refreshLocalPackageList() {
localPackageNameSet = getAllPackageName(HaloApp.getInstance().application)
@ -109,6 +152,44 @@ object PackageHelper {
lastInstalledPackageListTime = 0
}
/**
* 在超时后,若后台没有开启获取已安装应用列表的功能,默认以接口不控制的方式获取已安装应用列表
*/
fun fallbackInstalledPackageApiSwitchAfterTimeout(timeout: Long) {
CoroutineScope(SupervisorJob()).launch {
delay(timeout)
if (isGetInstalledPackagesApiAgreedRequired == UNKNOWN) {
Utils.log(TAG, "后台没有开启获取已安装应用列表的功能,超时后默认以接口不控制的方式获取已安装应用列表")
updateIsGetInstalledPackagesApiAgreedRequired(false)
}
}
}
/**
* 更新已安装应用列表获取开关状态
*/
fun updateIsGetInstalledPackagesApiAgreedRequired(isEnabled: Boolean) {
// 若状态不为 unknown 或者用户已经同意使用了,无需再更新
if (isGetInstalledPackagesApiAgreedRequired != UNKNOWN || isGetInstalledPackagesApiAgreed()) {
Utils.log(TAG, "installedPackageApiSwitchStatus 不为 UNKNOWN无需再更新")
return
}
if (isEnabled) {
getInstalledWhiteList()
_installedPackageApiSwitchStatusLiveData.postValue(true)
isGetInstalledPackagesApiAgreedRequired = ENABLED
} else {
isGetInstalledPackagesApiAgreedRequired = DISABLED
if (isSupportGetInstalledAppsPermission(HaloApp.getInstance())) {
GlobalPriorityChainHelper.queueNewHandler(RequestInstalledListPermissionHandler())
} else {
agreeOnGetInstalledPackagesApi()
}
}
}
/**
* 用户是否已经允许了调用获取已安装应用列表接口
* 优先用内存的值,没有再从 SP 中获取并更新
@ -118,12 +199,18 @@ object PackageHelper {
|| (SPUtils.getBoolean(SP_GET_INSTALLED_API_AGREED).also { isGetInstalledPackagesApiAgreed = it })
}
fun isGetInstalledPackagesApiAgreedRequired(): Boolean {
return isGetInstalledPackagesApiAgreedRequired == ENABLED
}
/**
* 同意使用已安装应用列表 API
*/
fun agreeOnGetInstalledPackagesApi() {
private fun agreeOnGetInstalledPackagesApi() {
isGetInstalledPackagesApiAgreed = true
SPUtils.setBoolean(SP_GET_INSTALLED_API_AGREED, true)
_installedPackageApiSwitchStatusLiveData.postValue(false)
}
/**
@ -132,18 +219,16 @@ object PackageHelper {
fun getInstalledPackages(context: Context?, flags: Int): List<PackageInfo> {
Utils.log(TAG, "即将获取已安装应用列表")
// Utils.log(TAG, "即将获取已安装应用列表" + Thread.currentThread().getStackTrace().contentToString().replace( ',', '\n' ))
// 用户未同意使用已安装应用列表 API返回空列表
if (!isGetInstalledPackagesApiAgreed()) {
Utils.log(TAG, "用户未同意使用已安装应用列表 API返回空列表")
return installedPackageList
return cachedInstalledPackagesList
}
// 简单 debounce 过于频繁的获取已安装应用列表调用
if (System.currentTimeMillis() - lastInstalledPackageListTime < 3000 && installedPackageList.isNotEmpty()) {
if (System.currentTimeMillis() - lastInstalledPackageListTime < 3000 && cachedInstalledPackagesList.isNotEmpty()) {
Utils.log(TAG, "使用了缓存的已安装应用列表")
return installedPackageList
return cachedInstalledPackagesList
}
var shouldGetNewInstalledPackagedList = false
@ -165,10 +250,96 @@ object PackageHelper {
if (shouldGetNewInstalledPackagedList) {
lastInstalledPackageListTime = System.currentTimeMillis()
installedPackageList = getInstalledPackagesInternal(context, flags)
cachedInstalledPackagesList = getInstalledPackagesInternal(context, flags)
}
return installedPackageList
return cachedInstalledPackagesList
}
/**
* 显示获取已安装应用列表的对话框并请求权限
*/
fun showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity: FragmentActivity,
ignorePermanentlyDenied: Boolean = false,
resultClosure: (Boolean) -> Unit
) {
val globalOnPermissionGrantedClosure = {
agreeOnGetInstalledPackagesApi()
// 进行包名初始化相关的操作
PackageRepository.initData()
refreshLocalPackageList()
refreshList()
}
if (isSupportGetInstalledAppsPermission(activity)) {
// 若系统已经授予了获取应用列表的权限,直接进行授权成功回调
if (!isGetInstalledListPermissionDisabled(activity)) {
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
return
}
PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = true,
ignorePermanentlyDenied = ignorePermanentlyDenied
) { isGranted ->
if (isGranted) {
SensorsBridge.trackInstalledListPermissionsResult("成功")
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
} else {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsResult("拒绝")
}
}
} else {
val hintDialog = PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = false,
) {
// do nothing
}
SensorsBridge.trackInstalledListPermissionsCustomDialogShow()
val noticeDialog = DialogHelper.showGuideDialog(
context = activity,
title = "权限申请",
content = "是否允许“光环助手”获取已安装的应用信息",
confirmText = "开启",
cancelText = "拒绝",
confirmClickCallback = {
SensorsBridge.trackInstalledListPermissionsCustomClick("开启")
globalOnPermissionGrantedClosure.invoke()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
},
cancelClickCallback = {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsCustomClick("拒绝")
}
)
noticeDialog?.setOnDismissListener {
hintDialog?.dismiss()
}
}
}
/**
* 延迟5秒后上报已安装应用列表
*/
private fun trackInstalledListAfterDelay() {
CoroutineScope(SupervisorJob()).launch {
delay(5000)
SensorsBridge.trackNumberOfInstalledList(localPackageNameSet.size, localPackageNameSet)
}
}
/**
@ -176,8 +347,8 @@ object PackageHelper {
*/
fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
// 若存在缓存,直接返回缓存结果。
if (isGetInstalledListPermissionSupported != UNKNOWN) {
return isGetInstalledListPermissionSupported != UNSUPPORTED
if (isGetInstalledPackagesPermissionSupported != UNKNOWN) {
return isGetInstalledPackagesPermissionSupported != UNSUPPORTED
}
try {
@ -185,7 +356,7 @@ object PackageHelper {
val flag =
Settings.Secure.getInt(context.contentResolver, "oem_installed_apps_runtime_permission_enable", 0)
if (flag == 1) {
isGetInstalledListPermissionSupported = SUPPORTED
isGetInstalledPackagesPermissionSupported = SUPPORTED
return true
}
@ -194,22 +365,97 @@ object PackageHelper {
val permissionInfo = packageManager.getPermissionInfo("com.android.permission.GET_INSTALLED_APPS", 0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS) {
isGetInstalledListPermissionSupported = SUPPORTED
isGetInstalledPackagesPermissionSupported = SUPPORTED
return true
} else {
isGetInstalledListPermissionSupported = UNSUPPORTED
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
} else {
isGetInstalledListPermissionSupported = UNSUPPORTED
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
} catch (e: PackageManager.NameNotFoundException) {
isGetInstalledListPermissionSupported = UNSUPPORTED
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
return false
}
}
/**
* 确保指定包名的应用在已安装了的情况下能正常收录
*/
fun addInstalledButMissingPackages(packageNameSet: HashSet<String>) {
Utils.log(TAG, "addInstalledButMissingPackages 检查已安装但未收录的应用")
val installedPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
if (!PackagesManager.isInstalled(packageName)
&& PackageUtils.getVersionNameByPackageName(packageName) != null
) {
installedPackageNameSet.add(packageName)
}
}
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
PackageRepository.addInstalledGames(
pkgNameList = ArrayList(installedPackageNameSet),
updateInstallStatus = true
)
}
fun refreshWrongInstallStatus(packageNameSet: MutableSet<String>) {
runOnIoThread {
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
val installedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
if (!PackagesManager.isInstalled(packageName)
&& installedVersionName != null
) {
installedButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName == null) {
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName != null
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)) {
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
}
}
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已安装状态的包数量为 ${installedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}")
runOnUiThread {
if (installedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in installedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addInstall(packageName)
}
}
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUninstall(packageName)
}
}
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUpdate(packageName)
}
}
}
}
}
/**
* 在5.1系统手机使用PackageManager获取已安装应用容易发生Package manager has died异常
* https://stackoverflow.com/questions/13235793/transactiontoolargeeception-when-trying-tÏo-get-a-list-of-applications-installed/30062632#30062632

View File

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

View File

@ -7,14 +7,12 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -29,7 +27,6 @@ import com.gh.common.xapk.XapkInstaller;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.PermissionHelper;
import com.gh.gamecenter.core.utils.MD5Utils;
import com.gh.gamecenter.core.utils.SentryHelper;
import com.gh.gamecenter.feature.entity.ApkEntity;
@ -48,12 +45,10 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@ -74,7 +69,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).applicationInfo.sourceDir;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}
@ -312,7 +307,7 @@ public class PackageUtils {
return new String[]{null, null};
}
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return new String[]{null, null};
}
@ -592,7 +587,7 @@ public class PackageUtils {
.getPackageInfo(packageName, 0);
return packageInfo.firstInstallTime;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
}
@ -619,7 +614,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager()
.getPackageInfo(BuildConfig.APPLICATION_ID, 0).lastUpdateTime;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
@ -633,7 +628,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}
@ -646,7 +641,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return 0;
}
@ -660,7 +655,7 @@ public class PackageUtils {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
e.printStackTrace();
// do nothing
}
return null;
}

View File

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

View File

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

View File

@ -197,7 +197,15 @@ class SplashScreenActivity : BaseActivity() {
// 尝试获取安装应用列表权限并启动首页(不在乎结果)
private fun requestGetInstallListPermissionAndLaunchMainActivity() {
launchMainActivity()
if (PackageHelper.isSupportGetInstalledAppsPermission(this)
&& PermissionHelper.isGetInstalledListPermissionDisabled(this)
) {
PermissionHelper.requestGetInstalledAppsListPermission(this, true) {
launchMainActivity()
}
} else {
launchMainActivity()
}
}
// 删除更新后的光环助手包

View File

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

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter.download
import android.view.View
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DirectUtils
import com.gh.common.util.PackageHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
@ -94,18 +95,19 @@ class UpdatableGameFragment : LazyFragment() {
noDataContainer.reuseResetLoadTv.layoutParams = layoutParam
noDataContainer.reuseResetLoadTv.visibility = View.VISIBLE
noDataContainer.reuseNoneDataDescTv.visibility = View.VISIBLE
if (PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
val isGetInstalledListDisagreed = !PackageHelper.isGetInstalledPackagesApiAgreed()
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
noDataContainer.reuseNoneDataIv.visibility = View.GONE
noDataContainer.reuseNoneDataTv.text = "开启应用列表权限"
noDataContainer.reuseNoneDataDescTv.text = "及时获悉游戏最新的更新消息"
noDataContainer.reuseResetLoadTv.text = "去开启"
noDataContainer.reuseResetLoadTv.setOnClickListener {
PermissionHelper.showGetInstalledAppsListPermissionDialogAndRequestPermission(
requireActivity()
) { isGranted ->
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
if (isGranted) {
updateNoDataView()
PackageRepository.initData()
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -10,7 +10,6 @@ import androidx.annotation.ColorRes
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.text.color
import androidx.core.view.doOnNextLayout
import androidx.fragment.app.FragmentActivity
import androidx.viewpager2.adapter.FragmentStateAdapter
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.R
@ -28,7 +27,6 @@ import com.gh.gamecenter.databinding.FragmentMainBinding
import com.gh.gamecenter.databinding.PieceBottomTabBinding
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.login.entity.UserInfoEntity
import com.gh.gamecenter.packagehelper.PackageRepository
import com.halo.assistant.HaloApp
import com.lightgame.listeners.OnBackPressedListener
import org.greenrobot.eventbus.EventBus
@ -67,8 +65,6 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
setCurrentItem(mViewModel!!.defaultBottomTabIndex)
}
}
PackageRepository.addInstalledGame("com.woobest.sgwg.aligames")
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
@ -85,8 +81,13 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
updateRealNameErrorContainer()
}
if (!PackageHelper.isGetInstalledPackagesApiAgreed()) {
showInstallApiHintView(mBinding)
PackageHelper.installedPackageApiSwitchStatusLiveData.observe(viewLifecycleOwner) {
if (it) {
showInstallApiHintView(mBinding)
SensorsBridge.trackInstalledListPermissionsDialogShow()
} else {
mBinding.installApiContainer.visibility = View.GONE
}
}
}
@ -280,53 +281,22 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
binding.installApiContentTv.text = contentText
binding.installApiCloseIv.setOnClickListener {
binding.installApiContainer.visibility = View.GONE
SensorsBridge.trackInstalledListPermissionsClick("关闭")
}
binding.installApiBtn.setOnClickListener {
val grantedClosure = {
binding.installApiContainer.visibility = View.GONE
PackageHelper.agreeOnGetInstalledPackagesApi()
// 进行包名初始化相关的操作
SensorsBridge.trackInstalledListPermissionsClick("去开启")
PackageRepository.initData()
PackageHelper.refreshLocalPackageList()
PackageHelper.refreshList()
}
if (PackageHelper.isSupportGetInstalledAppsPermission(requireContext())) {
// 若系统已经授予了获取应用列表的权限,直接进行授权成功回调
if (!PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
grantedClosure.invoke()
return@setOnClickListener
}
PermissionHelper.showGetInstalledAppsListPermissionDialogAndRequestPermission(requireContext() as FragmentActivity) { isGranted ->
// TODO 处理回调,上报日志等
if (isGranted) {
grantedClosure.invoke()
} else {
// TODO 处理拒绝权限的情况
}
}
} else {
val hintDialog = PermissionHelper.showGetInstalledAppsListPermissionDialogAndRequestPermission(requireContext() as FragmentActivity) {
// do nothing
}
val noticeDialog = DialogHelper.showDialog(
context = requireContext(),
title = "权限申请",
content = "是否允许“光环助手”获取已安装的应用信息",
confirmText = "开启",
cancelText = "拒绝",
confirmClickCallback = {
grantedClosure.invoke()
}
)
noticeDialog?.setOnDismissListener {
hintDialog?.dismiss()
val callbackClosure: (Boolean) -> Unit = { isGranted ->
if (isGranted) {
binding.installApiContainer.visibility = View.GONE
}
}
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity = requireActivity(),
ignorePermanentlyDenied = false,
resultClosure = callbackClosure
)
}
}

View File

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

View File

@ -2,8 +2,11 @@ package com.gh.vspace
import android.annotation.SuppressLint
import android.content.Context
import android.content.Intent
import android.database.sqlite.SQLiteDiskIOException
import android.os.*
import androidx.annotation.WorkerThread
import com.blankj.utilcode.util.LogUtils
import com.gh.download.simple.DownloadMessageHandler
import com.gh.download.simple.SimpleDownloadManager
import com.gh.gamecenter.common.retrofit.BiResponse
@ -22,7 +25,11 @@ import com.lg.download.DownloadStatus
import com.lg.download.httpclient.DefaultHttpClient
import com.lg.ndownload.DownloadConfigBuilder
import com.lg.vspace.VirtualAppManager
import com.lg.vspace.archive.data.Const
import com.lg.vspace.archive.ui.ArchiveManager
import com.lg.vspace.archive.ui.interfaces.ArchiveProgressPageListener
import com.lightgame.utils.Utils
import com.lody.virtual.client.core.VirtualCore
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import java.io.File
@ -43,6 +50,31 @@ object VArchiveHelper {
// 本地存档存放的位置
val archivePath by lazy { HaloApp.getInstance().filesDir.absolutePath + File.separator }
private var messenger: Messenger? = null
private val handler = object : Handler(Looper.getMainLooper()) {
override fun handleMessage(msg: Message) {
when (msg.what) {
Const.MSG_USE_ARCHIVE -> {
msg.data?.let {
onApplyGameArchiveFinished(
packageName = it.getString(Const.EXTRA_PACKAGE_NAME, ""),
isSuccess = it.getBoolean(Const.EXTRA_IS_SUCCESS, false)
)
}
}
Const.MSG_GENERATE_ARCHIVE -> {
msg.data?.let {
onSaveGameArchiveFinished(
packageName = it.getString(Const.EXTRA_PACKAGE_NAME, ""),
isSuccess = it.getBoolean(Const.EXTRA_IS_SUCCESS, false)
)
}
}
}
messenger = null
}
}
fun init() {
DownloadMessageHandler.registerGlobalStatusChangedListener { simpleDownloadEntity, downloadStatus ->
when (downloadStatus) {
@ -162,8 +194,26 @@ object VArchiveHelper {
mSaveArchiveListener = saveArchiveListener
// 记录最近一次创建的存档文件
mLatestArchiveFile = archiveFile
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
context.startActivity(intent)
if (VHelper.isLegacyInstalled(packageName)) {
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
context.startActivity(intent)
} else {
if (VirtualCore.get().isRunInExtProcess(packageName)) {
val intent = VirtualAppManager.getGenerateArchiveIntent(context, config, packageName, archiveFile)
prepareExtIntent(intent)
context.startActivity(intent)
} else {
ArchiveManager.generatePackage(context, packageName, config, archiveFile,
object : ArchiveProgressPageListener {
override fun onCancel() {
}
override fun completed(isSuccess: Boolean) {
onSaveGameArchiveFinished(packageName, isSuccess)
}
})
}
}
}
/**
@ -179,8 +229,43 @@ object VArchiveHelper {
applyArchiveListener: ((String, Boolean) -> Unit)? = null
) {
mApplyArchiveListener = applyArchiveListener
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
context.startActivity(intent)
if (VHelper.isLegacyInstalled(packageName)) {
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
context.startActivity(intent)
} else {
if (VirtualCore.get().isRunInExtProcess(packageName)) {
val intent = VirtualAppManager.getUseArchiveIntent(context, packageName, config, archiveFile)
prepareExtIntent(intent)
context.startActivity(intent)
} else {
ArchiveManager.useArchive(context, packageName, archiveFile, config,
object : ArchiveProgressPageListener {
override fun onCancel() {
}
override fun completed(isSuccess: Boolean) {
LogUtils.d("存档使用 completed : $isSuccess")
if (applyArchiveListener != null) {
applyArchiveListener(packageName, isSuccess)
}
}
})
}
}
}
private fun prepareExtIntent(intent: Intent) {
intent.setClassName(
com.lg.vspace.BuildConfig.EXT_PACKAGE_NAME,
"com.lg.vspace.addon.launcher.RemoteGuideActivity"
)
intent.putExtra("callback", Bundle().also {
messenger = Messenger(handler)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
it.putBinder("messenger", messenger!!.binder)
}
})
}
/**

File diff suppressed because it is too large Load Diff

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

30
dependencies_vasdk.gradle Normal file
View File

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

View File

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

View File

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

View File

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

View File

@ -63,6 +63,7 @@ public class Constants {
public static final String GHZS_GAME_ID = "5ae4462c2924bc7936438d07";
public static final String HALO_FUN_GAME_ID = "62bd412bbbf04747cd3de539"; // 畅玩助手ID
public static final String HALO_FUN_NEW_32_GAME_ID = "eeeeeeeeeeeeeeeeeeeeeeee"; // 32位新畅玩助手ID
public static final String EXTRA_DOWNLOAD_TYPE = "extra_download_type";
public static final String EXTRA_IS_MODDED_GAME = "extra_is_modded_game"; // 是否是修改版游戏
@ -201,6 +202,10 @@ public class Constants {
// 畅玩组件的配置
public static final String SP_V_SETTINGS = "v_settings";
// 畅玩,插件的配置
public static final String SP_V_NEW_SETTINGS = "v_new_settings";
// 头像挂件ID
public static final String SP_CHOOSE_AVATAR_ID = "choose_avatar_id";
@ -426,6 +431,7 @@ public class Constants {
public static final String SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME = "last_download_manager_ad_show_time";
public static final String SP_LAST_GAME_SEARCH_AD_SHOW_TIME = "last_game_search_ad_show_time";
public static final String SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME = "last_helper_launch_ad_show_time";
public static final String SP_NEW_FIRST_LAUNCH_VERSION = "isNewFirstLaunchV";
public static final String SP_PLUGIN_AREA_SHOWED_LAUNCH_ID = ""; // 插件化区域显示过的 launchId Tracker.launchId

View File

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

View File

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

View File

@ -18,8 +18,10 @@ import android.text.Spanned
import android.view.Gravity
import android.view.LayoutInflater
import android.view.Window
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.ContextCompat
import androidx.core.view.updateLayoutParams
import androidx.fragment.app.FragmentActivity
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.R
@ -49,6 +51,7 @@ object PermissionHelper {
@SuppressLint("CheckResult")
fun requestGetInstalledAppsListPermission(
activity: FragmentActivity,
ignorePermanentlyDenied: Boolean = false,
resultCallback: (Boolean) -> Unit
) {
try {
@ -66,6 +69,12 @@ object PermissionHelper {
}
else -> {
// 忽略永久拒绝的提示跳转响应
if (ignorePermanentlyDenied) {
resultCallback.invoke(false)
return@subscribe
}
if (SPUtils.getBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_GET_INSTALL_LIST_PERMISSION)) {
showPermissionPermanentlyDeniedDialog(
activity = activity,
@ -247,8 +256,10 @@ object PermissionHelper {
/**
* 展示获取已安装列表权限提示弹窗并请求权限
*/
fun showGetInstalledAppsListPermissionDialogAndRequestPermission(
fun showGetInstalledAppsListPermissionDialog(
activity: FragmentActivity,
requestPermission: Boolean,
ignorePermanentlyDenied: Boolean = false,
resultCallback: (Boolean) -> Unit
): Dialog? {
val solidContext = activity as? Activity ?: AppManager.getInstance().currentActivity() ?: return null
@ -256,6 +267,10 @@ object PermissionHelper {
val binding = DialogExternalStoragePermissionIntroBinding.inflate(LayoutInflater.from(solidContext))
binding.container.updateLayoutParams<FrameLayout.LayoutParams> {
height = 100F.dip2px()
}
val spannableBuilder = SpannableStringBuilder("当您使用APP时以下场景向您申请已安装列表权限\n")
val liStringList = arrayListOf("游戏更新提醒")
@ -281,13 +296,15 @@ object PermissionHelper {
dialog.window?.setGravity(Gravity.TOP)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
requestGetInstalledAppsListPermission(activity) { granted ->
resultCallback.invoke(granted)
dialog.dismiss()
}
dialog.show()
if (requestPermission) {
requestGetInstalledAppsListPermission(activity, ignorePermanentlyDenied) { isGranted ->
resultCallback.invoke(isGranted)
dialog.dismiss()
}
}
return dialog
}
@ -412,7 +429,7 @@ object PermissionHelper {
cancelText = "放弃",
confirmText = "去授权",
cancelClickCallback = null,
confirmClickCallback = { requestGetInstalledAppsListPermission(context, resultCallback) },
confirmClickCallback = { requestGetInstalledAppsListPermission(context, false, resultCallback) },
extraConfig = DialogHelper.Config(hint = HINT_CHECK_USAGE),
uiModificationCallback = {
it.hintTv.setTextColor(ContextCompat.getColor(context, R.color.text_theme))

View File

@ -103,6 +103,8 @@ object SensorsBridge {
private const val KEY_LINK_CONTENT_COLLECTIONE_PATTERN = "link_content_collectione_pattern"
private const val KEY_COLUMN_COLLECTIONE_PATTERN = "column_collectione_pattern"
private const val KEY_SEQUENCE = "sequence"
private const val KEY_NUMBER = "number"
private const val KEY_APP_LIST = "app_list"
private const val EVENT_GAME_DETAIL_PAGE_TAB_SELECT = "GameDetailPageTabSelect"
@ -216,6 +218,13 @@ object SensorsBridge {
private const val EVENT_SUSPENDED_WINDOW_SHOW = "SuspendedWindowShow"
private const val EVENT_SUSPENDED_WINDOW_CLICK = "SuspendedWindowClick"
private const val EVENT_INSTALLED_LIST_PERMISSIONS_DIALOG_SHOW = "InstalledListPermissionsDialogShow"
private const val EVENT_INSTALLED_LIST_PERMISSIONS_CLICK = "InstalledListPermissionsClick"
private const val EVENT_INSTALLED_LIST_PERMISSIONS_CUSTOM_DIALOG_SHOW = "InstalledListPermissionsCustomDialogShow"
private const val EVENT_INSTALLED_LIST_PERMISSIONS_CUSTOM_CLICK = "InstalledListPermissionsCustomClick"
private const val EVENT_INSTALLED_LIST_PERMISSIONS_RESULT = "InstalledListPermissionsResult"
private const val EVENT_NUMBER_OF_INSTALLED_LIST = "NumberOfInstalledList"
private var mIsSensorsEnabled = false
private val mSensor by lazy {
@ -3515,4 +3524,73 @@ object SensorsBridge {
}
trackEvent(EVENT_SUSPENDED_WINDOW_CLICK, json)
}
/**
* 事件IDInstalledListPermissionsDialogShow
* 事件名称:已安装列表权限提示条展示事件
* 触发时机: 授权提示条展示时触发 提示条在底部TAB主页面常驻各TAB切换时不重复上报展示事件
*/
fun trackInstalledListPermissionsDialogShow() {
trackEvent(EVENT_INSTALLED_LIST_PERMISSIONS_DIALOG_SHOW)
}
/**
* 事件IDInstalledListPermissionsClick
* 事件名称:已安装列表权限提示条点击事件
* 触发时机:点击对应按钮时触发上报
*/
fun trackInstalledListPermissionsClick(btnName: String) {
val json = json {
KEY_BUTTON_NAME to btnName
}
trackEvent(EVENT_INSTALLED_LIST_PERMISSIONS_CLICK, json)
}
/**
* 事件IDInstalledListPermissionsCustomDialogShow
* 事件名称:已安装列表权限自定义弹窗展示事件
* 触发时机:自定义授权提示弹窗展示时触发
*/
fun trackInstalledListPermissionsCustomDialogShow() {
trackEvent(EVENT_INSTALLED_LIST_PERMISSIONS_CUSTOM_DIALOG_SHOW)
}
/**
* 事件IDInstalledListPermissionsCustomClick
* 事件名称:已安装列表权限自定义弹窗点击事件
* 触发时机:点击对应按钮时触发上报
*/
fun trackInstalledListPermissionsCustomClick(btnName: String) {
val json = json {
KEY_BUTTON_NAME to btnName
}
trackEvent(EVENT_INSTALLED_LIST_PERMISSIONS_CUSTOM_CLICK, json)
}
/**
* 事件IDInstalledListPermissionsResult
* 事件名称:已安装列表权限获取结果事件
* 触发时机1、用户点击已安装列表自定义弹窗“开启”后上报权限获取的结果
* 2、用户点击授权提示条“去开启”后上报权限获取的结果
*/
fun trackInstalledListPermissionsResult(result: String) {
val json = json {
KEY_RESULT to result
}
trackEvent(EVENT_INSTALLED_LIST_PERMISSIONS_RESULT, json)
}
/**
* 事件IDNumberOfInstalledList
* 事件名称:已安装列表数量
* 触发时机客户端检测到获取已安装列表权限5s上报所读取到的已安装应用的数量
*/
fun trackNumberOfInstalledList(number: Int, packageSet: Set<String>) {
val json = json {
KEY_NUMBER to number
KEY_APP_LIST to packageSet
}
trackEvent(EVENT_NUMBER_OF_INSTALLED_LIST, json)
}
}

View File

@ -6,6 +6,7 @@
android:background="@color/transparent">
<FrameLayout
android:id="@+id/container"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"

View File

@ -39,4 +39,6 @@ interface IAppProvider : IProvider {
fun getIsBrandNewInstall(): Boolean
fun setSkippingThirdParty(isSkippingThirdParty: Boolean)
fun getPluginVersion(): String
}

View File

@ -401,9 +401,9 @@ data class GameEntity(
@IgnoredOnParcel
var name: String?
get() = if (shouldUseMirrorInfo()) {
obtainMirrorData()?.mName?.removeSuffix(".")
obtainMirrorData()?.mName?.removeSuffix(".") ?: "mirror unknown"
} else {
mName?.removeSuffix(".")
mName?.removeSuffix(".") ?: "unknown"
}
set(value) {
mName = value

View File

@ -26,11 +26,11 @@ import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.EnvHelper;
import com.gh.gamecenter.common.utils.PackageFlavorHelper;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.provider.IPushProvider;
import com.gh.gamecenter.core.provider.IAppProvider;
import com.gh.gamecenter.core.provider.IDataUtilsProvider;
import com.gh.gamecenter.core.provider.IDownloadManagerProvider;
import com.gh.gamecenter.core.provider.IErrorHelperProvider;
import com.gh.gamecenter.core.provider.IPushProvider;
import com.gh.gamecenter.core.provider.IReservationRepositoryProvider;
import com.gh.gamecenter.core.provider.IWechatBindHelperProvider;
import com.gh.gamecenter.core.utils.GsonUtils;
@ -136,11 +136,13 @@ public class UserRepository {
.getInstance()
.build(RouteConsts.provider.push)
.navigation();
pushProvider
.unbindAlias(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((s) -> {}, (e) -> {});
if (pushProvider != null) {
pushProvider
.unbindAlias(userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((s) -> {}, (e) -> {});
}
}
mLoginObsResponseUserInfo.postValue(null);
@ -535,11 +537,13 @@ public class UserRepository {
.getInstance()
.build(RouteConsts.provider.push)
.navigation();
pushProvider
.bindAlias(response.getUserId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((s) -> {}, (e) -> {});
if (pushProvider != null) {
pushProvider
.bindAlias(response.getUserId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((s) -> {}, (e) -> {});
}
}
if (UserManager.getInstance().getLoginTokenEntity() != null) {

View File

@ -102,6 +102,16 @@ class AboutFragment : ToolbarFragment() {
}
}
}
mBinding.aboutTvVersion.setOnLongClickListener {
appProvider?.let {
val pluginVersion = it.getPluginVersion()
if(pluginVersion.isNotEmpty()) {
toast("插件V${pluginVersion}")
}
}
false
}
}
@SuppressLint("SetTextI18n")

View File

@ -6,7 +6,8 @@ git_sha=`git rev-parse --short HEAD`
versionName=$(awk -v FS="versionName = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g")
versionCode=$(awk -v FS="versionCode = " 'NF>1{print $2}' dependencies.gradle | sed "s/\"//g")
build_time=$(TZ=Asia/Shanghai date +'%Y-%m%d-%H%M')
PACKAGE_NAME=$(sed -n "$(sed -n "/^[[:blank:]]*applicationId[[:blank:]]*=/=" dependencies.gradle)p" dependencies.gradle | awk -F '=' '{print $NF}' | sed "s/[[:blank:]]*[\"']*//g")
MODULE_VERSION=$(sed -n "$(sed -n "/^[[:blank:]]*VA_VERSION[^_]/=" vasdk/VaConfig.gradle)p" vasdk/VaConfig.gradle | awk -F '=' '{print $NF}' | sed "s/[[:blank:]]*[\"']*//g")
cwd=$(cd "$(dirname "$0")"; pwd)
apk_release_path=""
@ -26,12 +27,19 @@ else
sed -i 's/var isTestBuild = true/var isTestBuild = false/g' module_sensors_data/build.gradle
fi
if [[ $MODULE_VERSION == *"debug"* ]]; then
build_time_without_divider=$(TZ=Asia/Shanghai date +'%Y%m%d%H%M')L
if [[ "$OSTYPE" == "darwin"* ]]; then
sed -i '' 's/buildConfigField "long", "BUILD_TIME", "0"/buildConfigField "long", "BUILD_TIME", '"\"${build_time_without_divider}\""'/g' module_common/build.gradle
else
sed -i 's/buildConfigField "long", "BUILD_TIME", "0"/buildConfigField "long", "BUILD_TIME", '"\"${build_time_without_divider}\""'/g' module_common/build.gradle
fi
fi
./gradlew --stop
./gradlew clean
mkdir -p release/
OPTIONS=$(getopt -o '' -l config_id:,sdk_platform:,sdk_version:,app_id:,app_name:,channel:,activate_reporting_ratio:,first_launch_jump:,output:,unix_timestamp:,sdk_type:,keypoint_action_reporting:, -- "$@")
OPTIONS=$(getopt -o '' -l config_id:,sdk_platform:,sdk_version:,app_id:,app_name:,channel:,activate_reporting_ratio:,first_launch_jump:,output:,unix_timestamp:,sdk_type:,keypoint_action_reporting:,va_version:,va_url:, -- "$@")
eval set -- "$OPTIONS"
@ -49,11 +57,33 @@ while true; do
--app_name) app_name="$2"; shift 2;;
--sdk_type) sdk_type="$2"; shift 2;;
--keypoint_action_reporting) keypoint_action_reporting="$2"; shift 2;;
--va_version) va_version="$2"; shift 2;;
--va_url) va_url="$2"; shift 2;;
--) shift; break;;
*) echo "Invalid option: $1" >&2; exit 1;;
esac
done
echo "==================== 打包配置的一些变量 ============================="
echo "git_sha=$git_sha"
echo "versionName=$versionName"
echo "versionCode=$versionCode"
echo "build_time=$build_time"
echo "PACKAGE_NAME=$PACKAGE_NAME"
echo "MODULE_VERSION=$MODULE_VERSION 这个是va组件版本号"
echo "va_version=$va_version 这个是va插件版本"
echo "cwd=$cwd"
echo "=================================================================="
if [ "${va_url}" != "" ]; then
echo "======================== 下载插件 =================================="
curl -o app/src/main/assets/artifacts.zip "$va_url"
echo "=================================================================="
fi
mkdir -p $output
function updateChannelIfNeeded {
if [ "${channel}" != "" ]; then
java -jar ${cwd}/ApkChannelPackage.jar put -c $channel $1 release
@ -63,10 +93,7 @@ function updateChannelIfNeeded {
fi
}
# 保存 output 文件名
if [ "${output}" != "" ]; then
apk_release_path="$output"
fi
# 保存 config_id
if [ "${config_id}" != "" ]; then
@ -100,6 +127,7 @@ fi
# 是否选择了 sdk 类型
if [ "${sdk_platform}" != "" ]; then
apk_release_path="${output}/${PACKAGE_NAME}_${versionName}_${versionCode}_${channel}_${MODULE_VERSION}_${va_version}_${sdk_type}_${sdk_platform}_${sdk_version}_${unix_timestamp}.apk"
if [ "${activate_reporting_ratio}" == "" ]; then
activate_reporting_ratio="100"
fi
@ -148,7 +176,19 @@ if [ "${sdk_platform}" != "" ]; then
echo "光环助手_${versionName}_${versionCode}_神马推广包_${git_sha}_${build_time}"
cp -R app/build/outputs/apk/smCn/release/app-sm-cn-release.apk "${apk_release_path}"
fi
updateChannelIfNeeded ${apk_release_path}
updateChannelIfNeeded "${output}/${apk_release_path}"
else
apk_release_path="${output}/${PACKAGE_NAME}_${versionName}_${versionCode}_${MODULE_VERSION}_${va_version}_${unix_timestamp}.apk"
if [[ $MODULE_VERSION == *"debug"* ]]; then
./gradlew :app:assembleInternalCnRelease -I init.gradle
cp -R app/build/outputs/apk/internalCn/release/app-internal-cn-release.apk "${apk_release_path}"
else
./gradlew :app:assemblePublishCnRelease -I init.gradle
cp -R app/build/outputs/apk/publishCn/release/app-publish-cn-release.apk "${apk_release_path}"
fi
fi
# 重置 app build.gradle

57
setting_vasdk.gradle Normal file
View File

@ -0,0 +1,57 @@
include "va-main"
project(":va-main").projectDir = file("vasdk/app")
include "va-lib"
project(":va-lib").projectDir = file("vasdk/lib")
include "va-lib-res"
project(":va-lib-res").projectDir = file("vasdk/lib-res")
include "va-ext"
project(":va-ext").projectDir = file("vasdk/app-ext")
include "va-ext-lib"
project(":va-ext-lib").projectDir = file("vasdk/lib-ext")
include "va-sandhook"
project(":va-sandhook").projectDir = file("vasdk/sandhook")
include "va-library-commons"
project(":va-library-commons").projectDir = file("vasdk/commons")
include "va-feature-realname"
project(":va-feature-realname").projectDir = file("vasdk/feature/realname-window")
include ":ndownload"
include "va-feature-cloud"
project(":va-feature-cloud").projectDir = file("vasdk/feature/cloud")
include "va-feature-ads"
project(":va-feature-ads").projectDir = file("vasdk/feature/ads")
include "va-feature-floatingwindow"
project(":va-feature-floatingwindow").projectDir = file("vasdk/feature/floating-window")
include "va-core"
project(":va-core").projectDir = file("vasdk/core")
include "va-common"
project(":va-common").projectDir = file("vasdk/common")
include "va-flavor"
project(":va-flavor").projectDir = file("vasdk/flavor")
include "va-archive"
project(":va-archive").projectDir = file("vasdk/archive")
include "va-library-network"
project(":va-library-network").projectDir = file("vasdk/library/network")
include "aar-beizi_ad_sdk"
project(":aar-beizi_ad_sdk").projectDir = file("vasdk/aar/beizi_ad_sdk")
include "aar-beizi_fusion_sdk"
project(":aar-beizi_fusion_sdk").projectDir = file("vasdk/aar/beizi_fusion_sdk")
include "va-plugin-host-lib"
project(":va-plugin-host-lib").projectDir = file("vasdk/plugin/host-lib")
include "va-plugin-floating"
project(":va-plugin-floating").projectDir = file("vasdk/plugin/floating")
include "va-plugin-manager"
project(":va-plugin-manager").projectDir = file("vasdk/plugin/manager")
include "va-plugin-constant"
project(":va-plugin-constant").projectDir = file("vasdk/plugin/constant")
include "va-plugin-runtime"
project(":va-plugin-runtime").projectDir = file("vasdk/plugin/runtime")
include "va-plugin-loader"
project(":va-plugin-loader").projectDir = file("vasdk/plugin/loader")
include "va-plugin-host"
project(":va-plugin-host").projectDir = file("vasdk/host")
include "va-plugin-base"
project(":va-plugin-base").projectDir = file("vasdk/plugin/base")
include "va-plugin-library-easyfloat"
project(":va-plugin-library-easyfloat").projectDir = file("vasdk/plugin/library/easyfloat")

View File

@ -2,7 +2,8 @@ include ':app'
include ':libraries:LGLibrary'
include ':libraries:QQShare'
include ':libraries:Matisse'
include ':vspace-bridge:vspace'
include "vspace-bridge"
project(":vspace-bridge").projectDir = file("vspace-bridge/vspace")
include ':module_core'
include ':module_common'
include ':module_login'
@ -28,3 +29,5 @@ include ':module_internal_test'
include ':feature:jg_push'
include ':feature:jg_push_lib'
include ':feature:acloud_push'
apply from: 'setting_vasdk.gradle'

1
vasdk Submodule

Submodule vasdk added at 4df098feaa