Compare commits

...

32 Commits

Author SHA1 Message Date
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
55 changed files with 1479 additions and 581 deletions

View File

@ -401,7 +401,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 +409,13 @@ dependencies {
}
internalImplementation(project(':module_internal_test'))
// 根据BUILD_PUSH_TYPE决定使用哪个推送SDK目前默认使用阿里云推送
def pushProject = findProperty('BUILD_PUSH_TYPE') == 'jg'
? project(':feature:jg_push') : project(':feature:acloud_push')
implementation(pushProject) {
exclude group: 'androidx.swiperefreshlayout'
}
// 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'
// }
}
File propFile = file('sign.properties')

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
/**
* 是否需要显示下载管理广告

View File

@ -45,6 +45,14 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
}
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) {
@ -84,10 +92,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

@ -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;
@ -28,6 +29,7 @@ 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.vspace.VHelper;
import com.halo.assistant.HaloApp;
@ -94,8 +96,7 @@ public class Config {
getPreferences().edit().putString(SETTINGS_KEY, GsonUtils.toJson(settingsEntity)).apply();
mSettingsEntity = settingsEntity;
// 加载完设置后刷新下
PackageHelper.initList();
PackageHelper.refreshList();
}
@Nullable
@ -332,8 +333,31 @@ public class Config {
if (mNewApiSettingsEntity.getGameShieldContents() != null) {
ContentBlockedHelper.INSTANCE.init(mNewApiSettingsEntity.getGameShieldContents());
}
// 更新安装列表是否开启的配置
// if (mNewApiSettingsEntity.getInstalledComplianceSwitch() != null) {
// PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(mNewApiSettingsEntity.getInstalledComplianceSwitch());
// } else {
// PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(false);
// }
// 更新包名监听是否开启
if (mNewApiSettingsEntity.isPackageObserveEnable()) {
observePackageChange(mNewApiSettingsEntity.getPackageObserveActions());
}
}
});
}
}
public static void observePackageChange(NewApiSettingsEntity.PackageObserveActions packageObserveActions) {
PackageChangeBroadcastReceiver receiver = new PackageChangeBroadcastReceiver(packageObserveActions);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(packageObserveActions.getAdd());
intentFilter.addAction(packageObserveActions.getRem());
intentFilter.addAction(packageObserveActions.getRep());
intentFilter.addDataScheme("package");
HaloApp.getInstance().registerReceiver(receiver, intentFilter);
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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

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

View File

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

View File

@ -0,0 +1,136 @@
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
/**
* 添加一个等待中,待确定是否已成功安装的应用
*/
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 包名等待安装更新成功")
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
PackageRepository.addInstalledGame(packageName)
performInstallSuccessAction(packageName)
} else if (isUninstallPending && !isInstalled) {
pendingPackagePair = null
performUninstallSuccessAction(packageName)
} else if (isUpdatePending
&& installedVersionName != pendingVersion) {
pendingPackagePair = null
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName)
}
}
}
fun addInstall(packageName: String) {
performInstallSuccessAction(packageName)
}
/**
* 对应包名安装成功后的操作,继承至 PackageChangeBroadcastObserver
*/
private fun performInstallSuccessAction(packageName: String, withLog: Boolean = true) {
Utils.log(TAG, "安装了: $packageName 包名的程序")
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
val gameId = if (downloadEntity != null && downloadEntity.gameId != null) downloadEntity.gameId else ""
val gameName = if (downloadEntity != null && downloadEntity.name != null) downloadEntity.name else ""
if (withLog) {
NewFlatLogUtils.logGameInstallComplete(gameId, gameName)
SensorsBridge.trackInstallGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeInstall(packageName)
PackageHelper.refreshLocalPackageList()
val versionName = PackageUtils.getVersionNameByPackageName(packageName)
val installEb = EBPackage(EBPackage.TYPE_INSTALLED, packageName, versionName)
PackageObserver.onPackageChanged(installEb)
EventBus.getDefault().post(installEb)
}
fun addUninstall(packageName: String) {
performUninstallSuccessAction(packageName)
}
/**
* 对应包名卸载成功后的操作,继承至 PackageChangeBroadcastObserver
*/
private fun performUninstallSuccessAction(packageName: String, withLog: Boolean = true) {
Utils.log(TAG, "卸载了: $packageName 包名的程序")
val install = PackagesManager.getInstalledData(packageName)
val gameId = if (install?.id != null) install.id else ""
val gameName = if (install?.name != null) install.name else ""
if (withLog) {
NewFlatLogUtils.logGameUninstallComplete(gameId!!, gameName!!)
SensorsBridge.trackUnloadGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeUninstall(packageName)
PackageHelper.refreshLocalPackageList()
val uninstallEb = EBPackage(EBPackage.TYPE_UNINSTALLED, packageName, "")
PackageObserver.onPackageChanged(uninstallEb)
EventBus.getDefault().post(uninstallEb)
}
}

View File

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

View File

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

View File

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

View File

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

View File

@ -556,7 +556,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);
}
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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,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]冲突的方法
@ -18,11 +19,11 @@ object PackagesManager {
private val mInstalledList = ArrayList<GameInstall>()
// 实时更新(已安装到本地的包名列表,不包括畅玩游戏)
private val mInstalledPkgList = Collections.synchronizedList(ArrayList<String>())
private val mInstalledPkgSet = Collections.synchronizedSet(HashSet<String>())
fun initInstallPkgList(list: List<String>) {
mInstalledPkgList.clear()
mInstalledPkgList.addAll(list)
fun initInstallPkgSet(set: Set<String>) {
mInstalledPkgSet.clear()
mInstalledPkgSet.addAll(set)
}
/**
@ -86,9 +87,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

@ -20,9 +20,9 @@ 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
@ -30,6 +30,7 @@ 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.*
@ -54,7 +55,7 @@ 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
@ -86,7 +87,7 @@ object PackageRepository {
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,7 +96,7 @@ object PackageRepository {
initFilterPackage(list) { filteredList ->
mIsInitialisingData = false
mInstalledPkgList.addAll(filteredList)
mInstalledPkgSet.addAll(filteredList)
notifyInstallPkgData()
loadInstalledGameDigestAndNotifyData(filteredList)
@ -203,12 +204,14 @@ 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
) {
var isNotifyUpdate = false
val maxPageCount = (filteredList.size / PAGE_SIZE) + 1
@ -243,9 +246,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 +254,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 +405,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 +424,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 +455,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 +512,7 @@ object PackageRepository {
}
private fun notifyInstallPkgData() {
PackagesManager.initInstallPkgList(mInstalledPkgList)
PackagesManager.initInstallPkgSet(mInstalledPkgSet)
}
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -3,7 +3,6 @@ package com.halo.assistant;
import android.annotation.SuppressLint;
import android.app.Application;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.content.pm.PackageInfo;
import android.net.ConnectivityManager;
@ -39,14 +38,15 @@ import com.gh.common.util.DownloadNotificationHelper;
import com.gh.common.util.DownloadObserver;
import com.gh.common.util.LogUtils;
import com.gh.common.util.LunchType;
import com.gh.common.util.PackageChangeHelper;
import com.gh.common.util.PackageHelper;
import com.gh.common.util.PackageUtils;
import com.gh.common.util.SignatureRepository;
import com.gh.common.videolog.VideoRecordUtils;
import com.gh.download.DownloadManager;
import com.gh.download.simple.DownloadMessageHandler;
import com.gh.download.simple.SimpleDownloadDatabase;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.Injection;
import com.gh.gamecenter.common.constant.Config;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.RouteConsts;
@ -73,7 +73,6 @@ import com.gh.gamecenter.packagehelper.PackageRepository;
import com.gh.gamecenter.provider.FlavorProviderImp;
import com.gh.gamecenter.receiver.ActivitySkipReceiver;
import com.gh.gamecenter.receiver.DownloadReceiver;
import com.gh.gamecenter.receiver.InstallAndUninstallReceiver;
import com.gh.gamecenter.receiver.InstallReceiver;
import com.gh.gamecenter.receiver.NetworkStateReceiver;
import com.gh.gamecenter.wrapper.MainWrapperRepository;
@ -81,6 +80,8 @@ 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.lightgame.utils.Utils;
import com.llew.huawei.verifier.LoadedApkHuaWei;
import com.shuyu.gsyvideoplayer.cache.CacheFactory;
@ -120,7 +121,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;
@ -331,6 +332,9 @@ public class HaloApp extends MultiDexApplication {
// 获取 settings 配置
com.gh.common.constant.Config.getGhzsSettings();
// 5 秒获取后台配置的获取应用列表限制接口结果超时后,回落到关闭状态
// PackageHelper.INSTANCE.fallbackInstalledPackageApiSwitchAfterTimeout(5000L);
String localTemporaryDeviceId = SPUtils.getString(Constants.SP_TEMPORARY_DEVICE_ID);
if (!TextUtils.isEmpty(localTemporaryDeviceId)) {
HaloApp.getInstance().setLocalTemporaryDeviceId(localTemporaryDeviceId);
@ -340,7 +344,7 @@ public class HaloApp extends MultiDexApplication {
// 必须放在外面,否则不能及时刷新用户数据
UserRepository.getInstance().getLoginUserInfo();
GlobalPriorityChainHelper.INSTANCE.preStart();
GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);
MainWrapperRepository.Companion.getInstance().getDataUnion();
@ -351,7 +355,7 @@ public class HaloApp extends MultiDexApplication {
PackageRepository.initData();
PackageHelper.refreshLocalPackageList();
PackageHelper.initList();
PackageHelper.refreshList();
initReceiver();
initPackageChangesReceiver();
@ -367,6 +371,9 @@ public class HaloApp extends MultiDexApplication {
// 初始化畅玩相关数据
retrieveVGameInfoIfNeeded();
// 移除已安装但还在本地数据库中的包(避免因为没有监听到安装结果导致安装包没有删除的问题)
removeInstalledButRemainedPackages();
// 开发环境不要强制捕获相关异常,这些异常通常是需要处理的
if (!BuildConfig.DEBUG) {
RxJavaPlugins.setErrorHandler(throwable -> {
@ -497,13 +504,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() {
@ -575,6 +576,32 @@ public class HaloApp extends MultiDexApplication {
VHelper.init(this);
}
/**
* 移除已安装但还在本地数据库中的包
*/
private void removeInstalledButRemainedPackages() {
AppExecutor.getIoExecutor().execute(() -> {
for (DownloadEntity downloadEntity : DownloadManager.getInstance().getAllDownloadEntity()) {
if (downloadEntity.getStatus() != DownloadStatus.done) {
continue;
}
try {
String versionName = downloadEntity.getVersionName();
String packageName = downloadEntity.getPackageName();
// 这里暴力删除会影响畅玩游戏快速安装功能,但先不管了
if (versionName != null
&& packageName != null
&& versionName.equals(PackageUtils.getVersionNameByPackageName(packageName))) {
DownloadManager.getInstance().cancel(downloadEntity.getUrl());
}
} catch (Exception ignored) {
// 不在乎删除的结果,尝试即可
}
}
});
}
public boolean isReinstallTheSameVersion() {
return mIsReinstallTheSameVersion;
}
@ -608,12 +635,6 @@ 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);

Binary file not shown.

After

Width:  |  Height:  |  Size: 341 B

View File

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

View File

@ -7,8 +7,8 @@ ext {
targetSdkVersion = 30
// application info (每个大版本之间的 versionCode 增加 20)
versionCode = 1050
versionName = "5.35.0"
versionCode = 1051
versionName = "5.35.1"
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"

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

@ -186,6 +186,9 @@ public class Constants {
// 用户是否曾经永久拒绝过存储权限
public static final String SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION = "user_has_permanently_denied_storage_permission";
// 用户是否曾经永久拒绝过获取应用安装列表权限
public static final String SP_USER_HAS_PERMANENTLY_DENIED_GET_INSTALL_LIST_PERMISSION = "user_has_permanently_denied_get_install_list_permission";
// 是否已经填写邀请码
public static final String SP_HAS_COMPLETE_INVITE_CODE = "has_complete_invite_code";

View File

@ -18,11 +18,10 @@ import android.text.Spanned
import android.view.Gravity
import android.view.LayoutInflater
import android.view.Window
import android.view.View
import android.widget.FrameLayout
import androidx.appcompat.app.AppCompatActivity
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintLayout
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
@ -51,41 +50,50 @@ object PermissionHelper {
*/
@SuppressLint("CheckResult")
fun requestGetInstalledAppsListPermission(
context: FragmentActivity,
ignoreResult: Boolean = false,
emptyCallback: EmptyCallback
activity: FragmentActivity,
ignorePermanentlyDenied: Boolean = false,
resultCallback: (Boolean) -> Unit
) {
try {
val rxPermission = RxPermissions(context)
val rxPermission = RxPermissions(activity)
rxPermission
.requestEachCombined(PERMISSION_GET_INSTALLED_LIST)
.subscribe { permission ->
when {
permission.granted -> {
emptyCallback.onCallback()
resultCallback.invoke(true)
}
permission.shouldShowRequestPermissionRationale -> {
if (ignoreResult) {
emptyCallback.onCallback()
}
resultCallback.invoke(false)
}
else -> {
if (ignoreResult) {
emptyCallback.onCallback()
// 忽略永久拒绝的提示跳转响应
if (ignorePermanentlyDenied) {
resultCallback.invoke(false)
return@subscribe
}
if (SPUtils.getBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_GET_INSTALL_LIST_PERMISSION)) {
showPermissionPermanentlyDeniedDialog(
activity = activity,
permissionName = "获取已安装应用列表权限"
) {
resultCallback.invoke(false)
}
} else {
val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS)
intent.data = Uri.parse("package:" + context.packageName)
context.startActivity(intent)
SPUtils.setBoolean(
Constants.SP_USER_HAS_PERMANENTLY_DENIED_GET_INSTALL_LIST_PERMISSION,
true
)
resultCallback.invoke(false)
}
}
}
}
} catch (e: Exception) {
if (ignoreResult) {
emptyCallback.onCallback()
}
resultCallback.invoke(false)
}
}
@ -155,7 +163,10 @@ object PermissionHelper {
else -> {
if (SPUtils.getBoolean(Constants.SP_USER_HAS_PERMANENTLY_DENIED_STORAGE_PERMISSION)) {
showStoragePermissionPermanentlyDeniedDialog(activity = activity) {
showPermissionPermanentlyDeniedDialog(
activity = activity,
permissionName = "存储权限"
) {
resultCallback.invoke(false)
}
} else {
@ -168,11 +179,15 @@ object PermissionHelper {
}
}
private fun showStoragePermissionPermanentlyDeniedDialog(activity: Activity, callback: () -> Unit) {
private fun showPermissionPermanentlyDeniedDialog(
activity: Activity,
permissionName: String,
callback: () -> Unit
) {
DialogHelper.showDialog(
context = activity,
title = "权限申请",
content = "在设置-应用-光环助手-权限中开启存储权限,以保证能正常使用相关功能",
content = "在设置-应用-光环助手-权限中开启${permissionName},以保证能正常使用相关功能",
confirmText = "去设置",
cancelText = "放弃",
confirmClickCallback = {
@ -239,9 +254,67 @@ object PermissionHelper {
}
/**
* 展示获取存储权限提示弹窗并请求存储权限
* 展示获取已安装列表权限提示弹窗并请求权限
*/
private fun showStoragePermissionDialogAndRequestPermission(activity: FragmentActivity, emptyCallback: EmptyCallback) {
fun showGetInstalledAppsListPermissionDialog(
activity: FragmentActivity,
requestPermission: Boolean,
ignorePermanentlyDenied: Boolean = false,
resultCallback: (Boolean) -> Unit
): Dialog? {
val solidContext = activity as? Activity ?: AppManager.getInstance().currentActivity() ?: return null
val dialog = Dialog(solidContext, R.style.GhAlertDialog)
val binding = DialogExternalStoragePermissionIntroBinding.inflate(LayoutInflater.from(solidContext))
binding.container.updateLayoutParams<FrameLayout.LayoutParams> {
height = 100F.dip2px()
}
val spannableBuilder = SpannableStringBuilder("当您使用APP时以下场景向您申请已安装列表权限\n")
val liStringList = arrayListOf("游戏更新提醒")
for (li in liStringList) {
val liSpanned = SpannableStringBuilder(li).apply {
setSpan(
ImprovedBulletSpan(bulletRadius = 1.5F.dip2px(), gapWidth = 14F.dip2px()),
0,
li.length,
Spanned.SPAN_INCLUSIVE_EXCLUSIVE
)
}
spannableBuilder.append(liSpanned)
}
binding.contentTv.text = spannableBuilder
binding.titleTv.text = "权限使用说明"
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE)
dialog.setContentView(binding.root)
dialog.window?.setDimAmount(0F)
dialog.window?.setGravity(Gravity.TOP)
dialog.window?.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
dialog.show()
if (requestPermission) {
requestGetInstalledAppsListPermission(activity, ignorePermanentlyDenied) { isGranted ->
resultCallback.invoke(isGranted)
dialog.dismiss()
}
}
return dialog
}
/**
* 展示获取提示弹窗并请求存储权限
*/
private fun showStoragePermissionDialogAndRequestPermission(
activity: FragmentActivity,
emptyCallback: EmptyCallback
) {
val solidContext = activity as? Activity ?: AppManager.getInstance().currentActivity() ?: return
val dialog = Dialog(solidContext, R.style.GhAlertDialog)
@ -336,18 +409,18 @@ object PermissionHelper {
@JvmStatic
fun checkGetInstalledAppsListBeforeAction(
context: Context,
emptyCallback: EmptyCallback
resultCallback: (Boolean) -> Unit
) {
if (context is AppCompatActivity && isGetInstalledListPermissionDisabled(context)) {
showDialogBeforeRequestingGetInstalledListDialog(context, emptyCallback)
showDialogBeforeRequestingGetInstalledListDialog(context, resultCallback)
} else {
emptyCallback.onCallback()
resultCallback.invoke(true)
}
}
private fun showDialogBeforeRequestingGetInstalledListDialog(
context: FragmentActivity,
emptyCallback: EmptyCallback
resultCallback: (Boolean) -> Unit
) {
DialogHelper.showDialog(
context,
@ -356,7 +429,7 @@ object PermissionHelper {
cancelText = "放弃",
confirmText = "去授权",
cancelClickCallback = null,
confirmClickCallback = { requestGetInstalledAppsListPermission(context, false, emptyCallback) },
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

@ -26,6 +26,7 @@ import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.alibaba.android.arouter.launcher.ARouter;
import com.alibaba.android.arouter.utils.PackageUtils;
import com.gh.gamecenter.common.R;
import com.gh.gamecenter.common.callback.BiCallback;
import com.gh.gamecenter.common.constant.EntranceConsts;
@ -264,17 +265,10 @@ public class ShareUtils {
//检查是否安装手机QQ
public static boolean isQQClientAvailable(Context context) {
IPackageUtilsProvider packageUtilsConfig = (IPackageUtilsProvider) ARouter.getInstance().build(RouteConsts.provider.packageUtils).navigation();
List<PackageInfo> pinfo = packageUtilsConfig.getInstalledPackages(context, 0);
if (pinfo != null) {
for (int i = 0; i < pinfo.size(); i++) {
String pn = pinfo.get(i).packageName;
if ("com.tencent.mobileqq".equals(pn) || "com.tencent.tim".equals(pn)) {
return true;
}
}
}
return false;
IPackageUtilsProvider packageUtilsImpl = (IPackageUtilsProvider) ARouter.getInstance().build(RouteConsts.provider.packageUtils).navigation();
return packageUtilsImpl.isInstalledWithLauncherIcon(context, "com.tencent.mobileqq")
|| packageUtilsImpl.isInstalledWithLauncherIcon(context, "com.tencent.tim");
}
// 邀请好友(专属链接)

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

@ -12,6 +12,8 @@ interface IPackageUtilsProvider : IProvider {
fun getGhVersionCode(): Int
fun isInstalledWithLauncherIcon(context: Context, packageName: String): Boolean
fun getInstalledPackages(context: Context, flag: Int): List<PackageInfo>
fun getApkSignatureByPackageName(context: Context, packageName: String): Array<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