Compare commits

..

2 Commits

Author SHA1 Message Date
0a9b49c330 fix: va_core npe导致的32位游戏闪退 2024-08-09 17:02:15 +08:00
5b212bec2a chore: 组件升级 2.0.5-debug 2024-08-09 14:08:36 +08:00
301 changed files with 5230 additions and 6553 deletions

View File

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

View File

@ -107,6 +107,7 @@ android {
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
// 一体包的32位畅玩游戏助手包名
buildConfigField "String", "EXT_PACKAGE_NAME", "\"${rootProject.ext.EXT_PACKAGE_NAME}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
}
// gradle 2.2以上默认同时启用v1和v2优先用于Android N
@ -217,9 +218,6 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${DEV_QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${DEV_CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}-debug\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}-debug")
}
// publish 发布时候使用的 flavor接口仅包含正式环境
@ -233,9 +231,6 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
tea {
@ -249,10 +244,7 @@ android {
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("APPLOG_SCHEME", "rangersapplog.byAx6uYt".toLowerCase())
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
kuaishou {
@ -265,9 +257,6 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
gdt {
@ -280,9 +269,6 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
sm {
@ -295,9 +281,6 @@ android {
buildConfigField "String", "QUICK_LOGIN_APPKEY", "\"${QUICK_LOGIN_APPKEY}\""
buildConfigField "String", "DEV_CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "CSJ_APPID", "\"${CSJ_APPID}\""
buildConfigField "String", "VA_VERSION_NAME", "\"${rootProject.ext.VA_VERSION}\""
manifestPlaceholders.put("VA_VERSION_NAME", "${rootProject.ext.VA_VERSION}")
}
// 港澳台
@ -388,7 +371,7 @@ dependencies {
implementation "com.lg:easyfloat:${easyFloat}"
implementation("com.lg:apksig:${apksig}") {
implementation ("com.lg:apksig:${apksig}") {
exclude group: 'com.google.protobuf'
}
@ -404,7 +387,7 @@ dependencies {
implementation project(':vspace-bridge:vspace')
implementation(project(':feature:xapk-installer'))
implementation(project(':module_common')) {
implementation (project(':module_common')) {
exclude group: 'androidx.swiperefreshlayout'
}
@ -479,11 +462,9 @@ dependencies {
implementation(project(':feature:sentry'))
}
implementation(project(':feature:media_select'))
implementation(project(":module_va_api"))
implementation(project(":va-archive-common"))
if (!gradle.ext.excludeOptionalModules || gradle.ext.enableVa) {
if(!gradle.ext.excludeOptionalModules || gradle.ext.enableVa) {
implementation(project(":module_va_impl"))
}
debugImplementation "com.bytedance.tools.codelocator:codelocator-core:2.0.3"

View File

@ -32,11 +32,43 @@ class ExternalGameUsage : ITestCase {
it.titleTv.text = context.getString(R.string.title_install_external_game)
it.iconIv.setImageResource(R.drawable.ic_personal_my_game)
it.root.setOnClickListener {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
VHelper.connectService {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
}
}
}
}
override fun addInstallPluginButton(viewParent: ViewGroup) {
buttonTemplate(viewParent, R.id.install_plugin) {
it.titleTv.text = "安装64位插件"
it.root.setOnClickListener {
val file = File("/data/local/tmp/gh-plugins/artifacts.zip")
if (file.exists()) {
Utils.log(VHelper.LOG_TAG, "有本地更新文件: 64位插件")
// TODO: 补充debug插件更新
ToastUtils.showToast("暂未实现debug功能")
} else {
ToastUtils.showToast("data/local/tmp没有push文件")
}
}
}
}
override fun addInstallPlugin32Button(viewParent: ViewGroup) {
buttonTemplate(viewParent, R.id.install_plugin_32) {
it.titleTv.text = "安装32位插件"
it.root.setOnClickListener {
val file = File("/data/local/tmp/gh-plugins/artifacts32.zip")
if (file.exists()) {
// TODO: 补充debug插件更新
ToastUtils.showToast("暂未实现debug功能")
} else {
ToastUtils.showToast("data/local/tmp没有push文件")
}
}
}
}
}

View File

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

View File

@ -2,7 +2,6 @@
<resources>
<string name="title_install_external_game">從SD卡安裝</string>
<string name="text_install">安裝</string>
<string name="text_update">更新</string>
<string name="text_uninstall">卸載</string>
<string name="text_start">啟動</string>
</resources>

View File

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

View File

@ -185,8 +185,6 @@
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
<meta-data android:name="module_version" android:value="${VA_VERSION_NAME}" />
<service android:name="com.gh.ndownload.NDownloadService" />
<activity
@ -335,6 +333,14 @@
android:screenOrientation="portrait"
android:windowSoftInputMode="stateHidden" />
<activity
android:name=".category.CategoryDirectoryActivity"
android:screenOrientation="portrait" />
<activity
android:name=".category.CategoryListActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.UserInfoActivity"
android:screenOrientation="portrait" />
@ -456,6 +462,9 @@
android:name="com.gh.gamecenter.video.game.GameVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.qa.editor.LocalMediaActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.servers.GameServersActivity"
@ -553,6 +562,14 @@
android:name=".simulatorgame.SimulatorManagementActivity"
android:screenOrientation="portrait" />
<activity
android:name=".catalog.CatalogActivity"
android:screenOrientation="portrait" />
<activity
android:name=".catalog.NewCatalogListActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.search.ForumOrUserSearchActivity"
android:screenOrientation="portrait" />
@ -573,6 +590,10 @@
android:name=".personal.DeliveryInfoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.editor.PreviewVideoActivity"
android:screenOrientation="portrait" />
<activity
android:name=".qa.video.publish.VideoPublishActivity"
android:screenOrientation="portrait" />

View File

@ -227,7 +227,18 @@ object AdDelegateHelper {
/**
* 是否大于开屏广告展示间隔时长
*/
private fun isMatchStartUpAdDisplayRule(): Boolean = isMatchAdDisplayRule(mSplashAd, Constants.SP_LAST_SPLASH_AD_SHOW_TIME)
private fun isMatchStartUpAdDisplayRule(): Boolean {
mSplashAd?.displayRule?.run {
if (adDisplayInterval > 0) {
val lastShowTime = SPUtils.getLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, 0L)
val durationInMinutes = (System.currentTimeMillis() - lastShowTime).toFloat() / 1000 / 60
return durationInMinutes > adDisplayInterval
} else {
return true
}
}
return true
}
/**
* 是否大于广告管理展示间隔时长
@ -281,7 +292,6 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -303,7 +313,6 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -321,7 +330,6 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -345,7 +353,6 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -367,7 +374,6 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -397,14 +403,13 @@ object AdDelegateHelper {
sdkStartAdContainer.visibility = View.VISIBLE
requestCsjSplashAd(
activity,
thirdPartyAd.slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
sdkStartAdContainer,
sdkJumpBtn,
timeout,
isHotLaunch,
sdkSplashCallback
)
}
@ -415,65 +420,27 @@ object AdDelegateHelper {
*/
private fun requestCsjSplashAd(
activity: Activity,
slotId: String,
adViewWidthInPx: Int,
adViewHeightInPx: Int,
adViewWidthInDp: Float,
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkJumpBtn: TextView,
timeout: Int,
isHotLaunch: Boolean,
callback: (isSuccess: Boolean) -> Unit,
) {
val thirdPartyAd = if (isHotLaunch) mSplashAd?.hotStartThirdPartyAd else mSplashAd?.thirdPartyAd
if (mCsjAdImpl == null || thirdPartyAd == null) {
if (mCsjAdImpl == null) {
callback.invoke(false)
} else {
sdkJumpBtn.setOnClickListener {
callback.invoke(true)
if (activity is BaseActivity) {
activity.baseHandler.removeMessages(MainActivity.COUNTDOWN_SDK_AD)
}
}
val onAdShowAction = {
sdkJumpBtn.visibility = View.VISIBLE
if (activity is BaseActivity) {
activity.baseHandler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_SDK_AD, 1000)
}
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", mSplashAd?.typeChinese ?: "",
"ad_placement", "光环启动",
"launch_type", if (isHotLaunch) "热启动" else "冷启动",
"ad_space_id", mSplashAd?.id ?: "",
"ad_space_name", mSplashAd?.name ?: ""
)
}
val onAdClickAction = {
callback.invoke(true)
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", mSplashAd?.typeChinese ?: "",
"ad_placement", "光环启动",
"launch_type", if (isHotLaunch) "热启动" else "冷启动",
"ad_space_id", mSplashAd?.id ?: "",
"ad_space_name", mSplashAd?.name ?: ""
)
}
mCsjAdImpl?.requestSplashAd(
activity,
thirdPartyAd.slotId,
slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
startAdContainer,
timeout,
onAdShowAction,
onAdClickAction,
callback,
)
}
@ -490,7 +457,6 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -508,7 +474,6 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -578,14 +543,15 @@ object AdDelegateHelper {
), null, null
)
adImage.visibleIf(true)
ImageUtils.display(adImage, ad.img)
if (ad.isImageType) {
adImage.visibleIf(true)
adVideo.visibleIf(false)
ImageUtils.display(adImage, ad.img)
} else {
adImage.visibleIf(false)
adVideo.visibleIf(true)
adVideo.startPlay(ad.video.url)
adVideo.startPlay(ad.img, ad.video.url)
}
startAdContainer.setOnClickListener {
// 拦截点击事件传递
@ -656,11 +622,9 @@ object AdDelegateHelper {
slotId: String,
adContainerView: ViewGroup,
expressViewWidth: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit,
) {
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, onAdShowAction, onAdClickAction, callback)
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, callback)
}
/**
@ -671,8 +635,6 @@ object AdDelegateHelper {
containerView: ViewGroup,
ad: AdConfig.ThirdPartyAd,
expressViewWidthInDp: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit
) {
@ -695,28 +657,6 @@ object AdDelegateHelper {
slotId,
expressViewWidthInDp,
expressViewHeightInDp,
onAdShowAction,
onAdClickAction,
callback
)
}
/**
* 获取第三方 全屏/插屏 广告
*/
fun requestFullScreenAd(
fragment: Fragment,
ad: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit
) {
val slotId = ad.slotId
mCsjAdImpl?.requestFullScreenAd(
fragment,
slotId,
onAdShowAction,
onAdClickAction,
callback
)
}

View File

@ -3,105 +3,42 @@ package com.gh.ad
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.view.ViewStub
import androidx.fragment.app.Fragment
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.provider.ILaunchAd
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.AdConfig
@Route(path = RouteConsts.provider.vaAd, name = "畅玩启动页广告")
class LaunchAdImpl : ILaunchAd {
override fun init(context: Context?) {
}
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View, topViewStub: ViewStub, bottomViewStub: ViewStub, adClickAction: () -> Unit): View {
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View) {
if (AdDelegateHelper.shouldShowHelperLaunchAd()) {
val launchAd = AdDelegateHelper.vGameLaunchAd
val showThirdPartyAd = launchAd?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
val thirdPartyAd = launchAd?.thirdPartyAd
if (showThirdPartyAd && thirdPartyAd != null) {
val onAdShowAction = {
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", launchAd.typeChinese,
"ad_placement", AD_PLACEMENT,
"ad_space_id", launchAd.id,
"ad_space_name", launchAd.name
)
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", launchAd.typeChinese,
"ad_placement", AD_PLACEMENT,
"ad_space_id", launchAd.id,
"ad_space_name", launchAd.name
)
adClickAction.invoke()
}
if (launchAd.type == AdConfig.TYPE_BANNER) {
requestBannerAd(fragment, container, maskView, thirdPartyAd, onAdShowAction, onAdClickAction)
return topViewStub.inflate()
} else if (launchAd.type == AdConfig.TYPE_INTERSTITIAL) {
requestFullScreenAd(fragment, thirdPartyAd, onAdShowAction, onAdClickAction)
return bottomViewStub.inflate()
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
}
return bottomViewStub.inflate()
}
private fun requestBannerAd(
fragment: Fragment,
container: ViewGroup,
maskView: View,
thirdPartyAd: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
) {
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
onAdShowAction,
onAdClickAction
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
private fun requestFullScreenAd(
fragment: Fragment,
thirdPartyAd: AdConfig.ThirdPartyAd,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit
) {
AdDelegateHelper.requestFullScreenAd(
fragment,
thirdPartyAd,
onAdShowAction,
onAdClickAction
) { isSuccess ->
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
}
}
companion object {
private const val AD_PLACEMENT = "畅玩启动"
}
}

View File

@ -1,11 +1,12 @@
package com.gh.ad
import android.app.Activity
import android.content.Context
import android.util.AttributeSet
import android.view.WindowManager
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.video.detail.CustomManager
import com.shuyu.gsyvideoplayer.utils.Debuger
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
import com.shuyu.gsyvideoplayer.utils.GSYVideoType.SCREEN_TYPE_FULL
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
@ -17,14 +18,18 @@ class SplashAdVideoView @JvmOverloads constructor(
) :
StandardGSYVideoPlayer(context, attrs) {
fun startPlay(url: String) {
fun startPlay(cover: String, url: String) {
GSYVideoType.setShowType(SCREEN_TYPE_FULL)
GSYVideoType.setRenderType(GSYVideoType.SUFRACE)
CustomManager.getCustomManager(getKey()).isNeedMute = true
setUp(url, true, "")
val ivCover = findViewById<SimpleDraweeView>(R.id.thumbImage)
ImageUtils.display(ivCover, cover)
setNeedAutoAdaptation(false)
isLooping = true
startPlayLogic()
}
@ -44,31 +49,6 @@ class SplashAdVideoView @JvmOverloads constructor(
return R.layout.layout_splash_ad_video
}
override fun onAutoCompletion() {
setStateAndUi(CURRENT_STATE_AUTO_COMPLETE);
mSaveChangeViewTIme = 0
mCurrentPosition = 0
if (!mIfCurrentIsFullscreen) {
getGSYVideoManager().setLastListener(null)
}
mAudioManager.abandonAudioFocus(onAudioFocusChangeListener);
if (mContext is Activity) {
try {
(mContext as Activity).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
} catch (e: Exception) {
e.printStackTrace()
}
}
releaseNetWorkState()
if (mVideoAllCallBack != null && isCurrentMediaListener()) {
mVideoAllCallBack.onAutoComplete(mOriginUrl, mTitle, this)
}
mHadPlay = false
}
fun clearAll() {
GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_DEFAULT)
GSYVideoType.setRenderType(GSYVideoType.TEXTURE)

View File

@ -21,8 +21,6 @@ import com.gh.common.view.RichEditor
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
@ -32,7 +30,6 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.qa.editor.*
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.qa.entity.EditorInsertEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.UploadManager
@ -505,7 +502,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
ChooseType.VIDEO,
LocalMediaActivity.ChooseType.VIDEO,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_MEDIA_VIDEO_CODE
@ -534,7 +531,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
val maxChooseCount = if (imageCount + 10 <= MAX_IMAGE_COUNT) 10 else MAX_IMAGE_COUNT - imageCount
val intent = LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
ChooseType.IMAGE,
LocalMediaActivity.ChooseType.IMAGE,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
)

View File

@ -20,7 +20,7 @@ import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.entity.ForumDetailEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.RetrofitManager
@ -39,6 +39,8 @@ import retrofit2.HttpException
import java.io.File
import java.io.FileOutputStream
import java.util.*
import kotlin.collections.ArrayList
import kotlin.collections.HashMap
import kotlin.collections.LinkedHashMap
import kotlin.collections.set

View File

@ -268,6 +268,10 @@ object DefaultUrlHandler {
directToGameVideo(context, id, entrance, "")
}
EntranceConsts.HOST_CATEGORY -> {
val title = uri.getQueryParameter("title")
DirectUtils.directCategoryDirectory(context, id, title ?: "", entrance, "")
}
EntranceConsts.HOST_COLUMN_COLLECTION -> {
val name = uri.getQueryParameter("name")
DirectUtils.directToColumnCollection(context, id, -1, entrance, name ?: "")

View File

@ -6,8 +6,8 @@ class DownloadChainBuilder {
private var processEndCallback: ((asVGame: Boolean, Any?) -> Unit)? = null
fun setProcessEndCallback(gameId: String, callback: (asVGame: Boolean, Any?) -> Unit): DownloadChainBuilder {
processEndCallback = VaPluginDownloadWrapper(gameId = gameId, callback = callback) // 其他需要添加行为的装饰者可以一直包装A(B(C(callback))), 执行顺序 A->B->C->callback
fun setProcessEndCallback(callback: (asVGame: Boolean, Any?) -> Unit): DownloadChainBuilder {
processEndCallback = callback
return this
}

View File

@ -1,12 +0,0 @@
package com.gh.common.chain
import com.gh.vspace.VHelper
class VaPluginDownloadWrapper(val gameId: String, val callback: (Boolean, Any?) -> Unit) : (Boolean, Any?) -> Unit {
override fun invoke(asVGame: Boolean, any: Any?) {
callback.invoke(asVGame, any)
if (asVGame) {
VHelper.initVaPlugin(gameId)
}
}
}

View File

@ -7,7 +7,6 @@ import android.os.Build;
import android.preference.PreferenceManager;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.gh.common.util.PackageHelper;
@ -47,7 +46,6 @@ import org.json.JSONObject;
import java.io.IOException;
import java.util.Locale;
import io.reactivex.Observable;
import io.reactivex.Single;
import io.reactivex.SingleSource;
import io.reactivex.functions.Function;
@ -80,10 +78,10 @@ public class Config {
private static NewApiSettingsEntity.NightMode mNightModeSetting;
private static SimulatorEntity mNewSimulatorEntity;
private static VSetting mVSetting;
private volatile static VNewSetting mVNewSetting;
private static VNewSetting mVNewSetting;
private static AppEntity mNew32UpdateEntity;
private static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
public static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static GameGuidePopupEntity mGameGuidePopupEntity;
private static SharedPreferences mDefaultSharedPreferences;
@ -214,16 +212,6 @@ public class Config {
return mVNewSetting;
}
@NonNull
public static Observable<VNewSetting> getVNewSettingObservable() {
if (mVNewSetting != null) {
return Observable.just(mVNewSetting);
} else {
return vNewSettingSubject.hide();
}
}
@Nullable
public static AppEntity getNew32UpdateEntity() {
return mNew32UpdateEntity;

View File

@ -172,7 +172,7 @@ public class BindingAdapters {
builder.addHandler(new OverseaDownloadHandler());
builder.addHandler(new CheckDownloadHandler());
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
builder.setProcessEndCallback((asVGame, isSubscribe) -> {
download(v.getContext(),
progressBar,
gameEntity,
@ -197,7 +197,7 @@ public class BindingAdapters {
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new VersionNumberHandler());
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
builder.setProcessEndCallback((asVGame, isSubscribe) -> {
DownloadDialog.showDownloadDialog(
v.getContext(),
gameEntity,

View File

@ -1,52 +0,0 @@
package com.gh.common.prioritychain
import android.content.Context
import android.view.LayoutInflater
import android.widget.FrameLayout
import com.airbnb.lottie.LottieAnimationView
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
class CommunityHomeGuideHandler(
priority: Int,
private val context: Context,
private val decorView: FrameLayout?,
private val videoLottie: LottieAnimationView?
) : PriorityChainHandler(priority) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
return if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
showHomeVideoGuide(context, decorView, videoLottie)
processNext()
true
} else {
processNext()
false
}
}
companion object {
fun showHomeVideoGuide(context: Context, decorView: FrameLayout?, videoLottie: LottieAnimationView?) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(context),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
}
}

View File

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

View File

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

View File

@ -74,7 +74,7 @@ public class CheckLoginUtils {
Bundle bundle = new Bundle();
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance);
bundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivityCompat(context, bundle, nextToBundle, (resultCode, data) -> {
EntranceUtils.jumpActivity(context, nextToBundle, bundle, (resultCode, data) -> {
if (isTriggerNextStep && listener != null && isLogin()) {
listener.onLogin();
}

View File

@ -19,6 +19,8 @@ import com.gh.common.util.EntranceUtils.jumpActivity
import com.gh.common.util.EntranceUtils.jumpActivityCompat
import com.gh.gamecenter.*
import com.gh.gamecenter.amway.AmwayActivity
import com.gh.gamecenter.catalog.CatalogActivity
import com.gh.gamecenter.category.CategoryDirectoryActivity
import com.gh.gamecenter.category2.CategoryV2Activity
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout
@ -149,12 +151,14 @@ object DirectUtils {
"qqqun",
"tag",
"all_community_article",
"category",
"block",
"column_collection",
"server",
"top_game_comment",
"wechat_bind",
"video",
"catalog",
"category_v2",
"common_collection",
"game_list",
@ -281,6 +285,10 @@ object DirectUtils {
)
)
"category", "分类" -> directCategoryDirectory(context, linkEntity.link!!, linkEntity.text!!)
"catalog" -> directCatalog(context, linkEntity.link!!, linkEntity.text!!, entrance, path)
"category_v2" -> directCategoryV2(
context,
linkEntity.link!!,
@ -1415,6 +1423,47 @@ object DirectUtils {
}
}
/**
* 跳转分类
*/
@JvmStatic
fun directCategoryDirectory(
context: Context,
categoryId: String,
categoryTitle: String,
entrance: String? = null,
path: String? = ""
) {
if (categoryId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
bundle.putString(KEY_CATEGORY_ID, categoryId)
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_PATH, path)
jumpActivity(context, bundle)
}
/**
* 跳转新分类
*/
@JvmStatic
fun directCatalog(
context: Context,
catalogId: String,
catalogTitle: String,
entrance: String? = null,
path: String? = "",
) {
if (catalogId.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_TO, CatalogActivity::class.java.name)
bundle.putString(KEY_CATALOG_ID, catalogId)
bundle.putString(KEY_CATALOG_TITLE, catalogTitle)
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
jumpActivity(context, bundle)
}
/**
* 跳转新分类2.0
*/

View File

@ -37,10 +37,10 @@ import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
import com.gh.vspace.VHelper
import com.lightgame.download.DownloadConfig
@ -697,7 +697,7 @@ object DownloadItemUtils {
// 为 downloadButton 添加游戏实体,供点击的时候上报用
downloadBtn.putObject(gameEntity)
val gamePermissionDialogFragment = (context as? AppCompatActivity)?.supportFragmentManager?.findFragmentByTag(
val gamePermissionDialogFragment = (context as AppCompatActivity).supportFragmentManager.findFragmentByTag(
GamePermissionDialogFragment::class.java.name
) as GamePermissionDialogFragment?
gamePermissionDialogFragment?.dismissAllowingStateLoss()
@ -910,7 +910,7 @@ object DownloadItemUtils {
addHandler(CheckStoragePermissionHandler())
addHandler(VersionNumberHandler())
}
.setProcessEndCallback(gameEntity.id) { _, _ ->
.setProcessEndCallback { _, _ ->
DownloadDialog.showDownloadDialog(view.context, gameEntity, traceEvent, entrance, location)
}
.buildHandlerChain()
@ -953,7 +953,7 @@ object DownloadItemUtils {
addHandler(LandPageAddressHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
.setProcessEndCallback { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -972,7 +972,7 @@ object DownloadItemUtils {
addHandler(OverseaDownloadHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
.setProcessEndCallback { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -991,7 +991,7 @@ object DownloadItemUtils {
addHandler(ValidateVSpaceHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
.setProcessEndCallback { asVGame, isSubscribe ->
download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -1004,7 +1004,7 @@ object DownloadItemUtils {
addHandler(DownloadDialogHelperHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { _, isSubscribe ->
.setProcessEndCallback { _, isSubscribe ->
plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -1089,7 +1089,7 @@ object DownloadItemUtils {
DownloadChainBuilder()
.apply {
addHandler(LandPageAddressHandler())
}.setProcessEndCallback(gameEntity.id) { asVGame, _ ->
}.setProcessEndCallback { asVGame, _ ->
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk) {
DialogUtils.checkDownload(
context,

View File

@ -69,13 +69,11 @@ object DownloadObserver {
DownloadDataHelper.uploadDownloadEvent(downloadEntity)
}
val status = downloadEntity.status
if (DownloadStatus.hijack == status) {
if (DownloadStatus.hijack == downloadEntity.status) {
// 链接被劫持
processHijack(downloadEntity)
return
} else if (DownloadStatus.notfound == status) {
} else if (DownloadStatus.notfound == downloadEntity.status) {
// 404 Not Found
// 删除任务
downloadEntity.status = DownloadStatus.cancel
@ -129,9 +127,10 @@ object DownloadObserver {
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
)
return
} else if (DownloadStatus.neterror == status
|| DownloadStatus.diskioerror == status
|| DownloadStatus.timeout == status
} else if (DownloadStatus.neterror == downloadEntity.status
|| DownloadStatus.timeout == downloadEntity.status
|| DownloadStatus.diskioerror == downloadEntity.status
|| DownloadStatus.diskisfull == downloadEntity.status
) {
if (mRetryableHashMap[downloadEntity.url] == true
&& NetworkUtils.isWifiConnected(HaloApp.getInstance().application)
@ -140,7 +139,9 @@ object DownloadObserver {
mRetryableHashMap[downloadEntity.url] = false
Utils.log(TAG, "下载重试->" + downloadEntity.toJson())
} else {
if (DownloadStatus.diskioerror == status) {
if (DownloadStatus.diskisfull == downloadEntity.status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
} else if (DownloadStatus.diskioerror == downloadEntity.status) {
ToastUtils.toast("磁盘 IO 异常,请稍后重试")
} else {
ToastUtils.toast("网络不稳定,下载任务已暂停")
@ -149,21 +150,17 @@ object DownloadObserver {
DataLogUtils.uploadNeterrorLog(mApplication, downloadEntity)
Utils.log(TAG, "下载自动暂停->" + downloadEntity.toJson())
}
} else if (DownloadStatus.diskisfull == status) {
ToastUtils.toast("磁盘已满,请清理空间后重试下载")
downloadManager.pause(downloadEntity.url)
} else if (DownloadStatus.redirected == status) {
} else if (DownloadStatus.redirected == downloadEntity.status) {
Utils.log(TAG, "重定向完毕")
DownloadDataHelper.uploadRedirectEvent(downloadEntity)
} else if (DownloadStatus.unqualified == status) {
} else if (DownloadStatus.unqualified == downloadEntity.status) {
// 未成年
RealNameHelper.showRealNameUnqualifiedDialog(downloadEntity)
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.unavailable == status) {
} else if (DownloadStatus.unavailable == downloadEntity.status) {
// 未接入防沉迷系统
val currentActivity = AppManager.getInstance().currentActivity()
@ -182,7 +179,7 @@ object DownloadObserver {
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.isCertificating == status) {
} else if (DownloadStatus.isCertificating == downloadEntity.status) {
// 未接入防沉迷系统
val currentActivity = AppManager.getInstance().currentActivity()
@ -210,21 +207,21 @@ object DownloadObserver {
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.uncertificated == status) {
} else if (DownloadStatus.uncertificated == downloadEntity.status) {
// 未实名
RealNameHelper.showRealNameUncertificatedDialog(downloadEntity)
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
} else if (DownloadStatus.banned == status) {
} else if (DownloadStatus.banned == downloadEntity.status) {
ToastUtils.showToast("网络异常")
// 删除任务
downloadEntity.status = DownloadStatus.cancel
downloadManager.cancel(downloadEntity.url)
}
if (DownloadStatus.done == status) {
if (DownloadStatus.done == downloadEntity.status) {
if (mDoneDebouncePair?.first != downloadEntity.url) {
mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis())
performDownloadCompleteAction(downloadEntity, downloadManager)
@ -244,7 +241,7 @@ object DownloadObserver {
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
// 如果已下载大小发生变化,表示成功恢复下载,则重置重试标记
if (status == DownloadStatus.downloading) {
if (downloadEntity.status == DownloadStatus.downloading) {
mRetryableHashMap[downloadEntity.url] = true
}
}

View File

@ -27,6 +27,7 @@ import com.gh.gamecenter.core.utils.ClassUtils;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.halo.assistant.HaloApp;
import com.lightgame.utils.AppManager;
import com.lightgame.utils.Utils;
import org.json.JSONException;
@ -37,18 +38,7 @@ import java.util.Set;
public class EntranceUtils {
public static void jumpActivity(Context context, Bundle bundle) {
jumpActivityCompat(context, bundle, null, null);
}
public static void jumpActivityCompat(Context context, Bundle bundle) {
jumpActivityCompat(context, bundle, null,null);
}
public static void jumpActivityCompat(Context context,
Bundle bundle,
@Nullable Bundle nextToBundle,
@Nullable Callback callback) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (HaloApp.getInstance().isRunningForeground || HaloApp.getInstance().isAlreadyUpAndRunning) {
@ -58,18 +48,6 @@ public class EntranceUtils {
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else if (callback != null ) {
Intent intent1 = new Intent(context, clazz);
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtras(bundle);
if (context instanceof AppCompatActivity) {
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
} else {
// 不要回调,正常跳转
context.startActivity(intent1);
}
} else {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -80,6 +58,57 @@ public class EntranceUtils {
intent1.putExtras(bundle);
context.startActivity(intent1);
}
} else {
// 应用未在运行
context.startActivity(SplashScreenActivity.getSplashScreenIntent(context, bundle));
}
}
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (AppManager.getInstance().findActivity(MainActivity.class) != null) {
// 应用正在运行前台或后台且MainActivity在栈中
String to = bundle.getString(KEY_TO);
Class<?> clazz = ClassUtils.forName(to);
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else {
Intent intent1 = new Intent(context, clazz);
intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
// 如果 activity 名称有 singleton 的就添加 reorder_to_front 标签 (有点粗暴有点蠢,但暂时就先这样吧 :C )
if (clazz.getSimpleName().toLowerCase().contains("singleton")) {
intent1.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT);
}
intent1.putExtras(bundle);
context.startActivity(intent1);
}
} else {
// 应用未在运行
context.startActivity(SplashScreenActivity.getSplashScreenIntent(context, bundle));
}
}
public static void jumpActivity(Context context, Bundle nextToBundle, Bundle bundle, Callback callback) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (AppManager.getInstance().findActivity(MainActivity.class) != null) {
// 应用正在运行前台或后台且MainActivity在栈中
String to = bundle.getString(KEY_TO);
Class<?> clazz = ClassUtils.forName(to);
if (clazz == null) clazz = MainActivity.class;
if (ToolbarFragment.class.isAssignableFrom(clazz)) { // 兼容ToolbarFragment
ToolBarActivity.startFragmentNewTask(context, (Class<? extends ToolbarFragment>) clazz, bundle);
} else {
Intent intent1 = new Intent(context, clazz);
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
intent1.putExtras(bundle);
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
.startForResult(intent1, callback);
}
} else {
// 应用未在运行
if (nextToBundle != null) {

View File

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

View File

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

View File

@ -734,6 +734,18 @@ public class LogUtils {
LoghubUtils.log(object, LOG_STORE_EVENT, false);
}
public static void logNewCatalogAppearanceEvent(String entrance, String key) {
logCatalogEvent("access_to_classification", entrance, key, -1, -1, -1, -1);
}
public static void logSubCatalogClickEvent(String entrance, String key, int seq1) {
logCatalogEvent("click_first_classification", entrance, key, seq1, -1, -1, -1);
}
public static void logSubCatalogContentClickEvent(String entrance, String key, int seq1, int seq2) {
logCatalogEvent("click_secondary_classification", entrance, key, seq1, seq2, -1, -1);
}
public static void logSpecialCatalogContentClickEvent(String entrance, String key, int seq1, int seqContent) {
logCatalogEvent("click_content", entrance, key, seq1, -1, seqContent, -1);
}

View File

@ -30,9 +30,6 @@ object NewFlatLogUtils {
private const val EVENT_LOGIN_FROM_GHZS_SHOW = "halo_fun_login_from_ghzs_show"
private const val EVENT_LOGIN_FROM_GHZS_CLICK = "halo_fun_login_from_ghzs_click"
private const val EVENT_INSTALLED_LAUNCH_DIALOG_SHOW = "halo_fun_installed_launch_dialog_show"
private const val EVENT_INSTALLED_LAUNCH_DIALOG_CLICK = "halo_fun_installed_launch_dialog_click"
private fun log(jsonObject: JSONObject, logStore: String = "event", uploadImmediately: Boolean = false) {
Utils.log("NewFlatLogUtils", jsonObject.toString(4))
LoghubUtils.log(jsonObject, logStore, uploadImmediately, true)
@ -553,8 +550,6 @@ object NewFlatLogUtils {
// 搜索-点击搜索榜单内容
@JvmStatic
fun logSearchClickRankDetail(
key: String,
rankType: String,
rankName: String,
rankSequence: String,
linkId: String,
@ -563,8 +558,6 @@ object NewFlatLogUtils {
) {
val json = json {
KEY_EVENT to "search_click_rank_detail"
"key" to key
"rank_type" to rankType
"rank_name" to rankName
"rank_sequence" to rankSequence
"link_id" to linkId
@ -2695,73 +2688,4 @@ object NewFlatLogUtils {
parseAndPutMeta()(this)
}.let(::log)
}
fun logHaloFunInstalledLaunchDialogShow() {
json {
KEY_EVENT to EVENT_INSTALLED_LAUNCH_DIALOG_SHOW
parseAndPutMeta()(this)
}.let(::log)
}
fun logHaloFunInstalledLaunchDialogClick(
gameId: String,
gameName: String,
buttonType: String
) {
json {
KEY_EVENT to EVENT_INSTALLED_LAUNCH_DIALOG_CLICK
"game_id" to gameId
"game_name" to gameName
"button_type" to buttonType
parseAndPutMeta()(this)
}.let(::log)
}
// 用户访问分流器
fun logByPassBrowsing(
source: String,
bypassName: String,
bypassId: String,
branchId: String,
branchName: String,
inProcessTime: Int,
bypassVisitTime: Int,
linkType: String,
linkId: String,
linkText: String,
bypassStatus: Int
) {
json {
KEY_EVENT to "BypassBrowsing"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"branch_id" to branchId
"branch_name" to branchName
"inprocess_time" to inProcessTime
"bypass_visit_time" to bypassVisitTime
"link_type" to linkType
"link_id" to linkId
"link_text" to linkText
"bypass_status" to bypassStatus
parseAndPutMeta()(this)
}.let(::log)
}
// 分流失败
fun logFailByPass(
source: String,
bypassName: String,
bypassId: String,
defeatedReason: String
) {
json {
KEY_EVENT to "FailBypass"
"source" to source
"bypass_name" to bypassName
"bypass_id" to bypassId
"defeated_reason" to defeatedReason
parseAndPutMeta()(this)
}.let(::log)
}
}

View File

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

View File

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

View File

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

View File

@ -82,7 +82,6 @@ object ViewPagerFragmentHelper {
const val TYPE_TOOLKIT = "toolkit" // 工具箱
fun createFragment(parentFragment: Fragment?, bundle: Bundle, linkEntity: LinkEntity, isTabWrapper: Boolean): Fragment {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
return when (linkEntity.type) {
// 游戏详情页
TYPE_GAME -> {
@ -91,12 +90,11 @@ object ViewPagerFragmentHelper {
}
// 我的光环
TYPE_MY_HALO -> {
val superiorChain = if (parentFragment is ISuperiorChain) parentFragment else null
HaloPersonalFragment().setSuperiorChain(superiorChain).with(bundle)
}
// 社区首页
TYPE_COMMUNITY_HOME -> {
CommunityHomeFragment().setSuperiorChain(superiorChain).with(bundle)
}
TYPE_COMMUNITY_HOME -> CommunityHomeFragment().with(bundle)
// 视频信息流
TYPE_VIDEO_STREAM -> {
bundle.putBoolean(EntranceConsts.KEY_IS_HOME_VIDEO, true)
@ -150,11 +148,11 @@ object ViewPagerFragmentHelper {
NewQuestionDetailFragment().with(bundle)
}
// 其他原来带Toolbar的Fragment
else -> createToolbarWrapperFragment(parentFragment, bundle, linkEntity, isTabWrapper)
else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper)
}
}
private fun createToolbarWrapperFragment(parentFragment: Fragment?, bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
private fun createToolbarWrapperFragment(bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
var className = ReloadFragment::class.java.name
when (entity.type) {

View File

@ -0,0 +1,331 @@
package com.gh.common.view
import android.content.Context
import android.graphics.Color
import android.util.AttributeSet
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.ImageView
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.annotation.ColorInt
import androidx.core.content.ContextCompat
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.setDrawableEnd
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.visibleIf
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.google.android.flexbox.FlexboxLayout
class CatalogFilterView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : LinearLayout(context, attrs, defStyleAttr) {
private var mTypeTv: TextView
private var mCatalogTv: TextView
private var mSizeTv: TextView
private var mTypeContainer: View
private var mCatalogContainer: View
private var mSizeContainer: View
private var mTypeFilterArray = ArrayList<SortType>()
private var mCatalogFilterArray = ArrayList<CatalogEntity.SubCatalogEntity>()
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
private var mOnCatalogFilterSetupListener: OnCatalogFilterSetupListener? = null
private var mTypePopupWindow: PopupWindow? = null
private var mCatalogPopupWindow: PopupWindow? = null
private var mSizePopupWindow: PopupWindow? = null
init {
View.inflate(context, R.layout.layout_catalog_filter, this)
mTypeTv = findViewById(R.id.type_tv)
mCatalogTv = findViewById(R.id.catalog_tv)
mSizeTv = findViewById(R.id.size_tv)
mTypeContainer = findViewById(R.id.container_type)
mCatalogContainer = findViewById(R.id.container_catalog)
mSizeContainer = findViewById(R.id.container_size)
mTypeContainer.setOnClickListener {
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
}
mCatalogContainer.setOnClickListener {
showSelectCatalogPopupWindow(this, mCatalogTv, mCatalogTv.text.toString())
}
mSizeContainer.setOnClickListener {
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
}
}
fun setTypeList(switch: CatalogEntity.CatalogSwitch) {
switch.run {
if ("on" == hotSort) mTypeFilterArray.add(SortType.RECOMMENDED)
if ("on" == newSort) mTypeFilterArray.add(SortType.NEWEST)
if ("on" == starSort) mTypeFilterArray.add(SortType.RATING)
}
if (mTypeFilterArray.isNotEmpty()) mTypeTv.text = mTypeFilterArray[0].value
}
fun setCatalogList(subCatalogList: List<CatalogEntity.SubCatalogEntity>, initCatalogName: String) {
mCatalogFilterArray = ArrayList(subCatalogList)
mCatalogTv.text = initCatalogName
}
fun setOnConfigSetupListener(onCatalogFilterSetupListener: OnCatalogFilterSetupListener) {
mOnCatalogFilterSetupListener = onCatalogFilterSetupListener
}
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
if (highlightIt) {
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.bg_tag_text)
targetTextView.setTextColor(Color.WHITE)
} else {
targetTextView.background = null
targetTextView.setTextColor(R.color.text_757575.toColor(context))
}
}
private fun showSelectTypePopupWindow(containerView: View, typeTv: TextView, typeText: String) {
typeTv.setTextColor(R.color.text_theme.toColor(context))
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
val inflater = LayoutInflater.from(typeTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
).apply { mTypePopupWindow = this }
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
val backgroundView = layout.findViewById<View>(R.id.background)
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (type in mTypeFilterArray) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = typeTv.context.resources.displayMetrics.widthPixels / 3
val height = item.layoutParams.height
item.layoutParams = ViewGroup.LayoutParams(width, height)
flexboxLayout.addView(item)
val tv = item.findViewById<TextView>(R.id.size_tv)
tv.text = type.value
toggleHighlightedTextView(tv, typeText == type.value)
tv.tag = type.value
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
popupWindow.dismiss()
typeTv.text = type.value
mOnCatalogFilterSetupListener?.onSetupSortType(type)
}
}
popupWindow.setOnDismissListener {
typeTv.setTextColor(R.color.text_757575.toColor(context))
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
mTypePopupWindow = null
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = 0
popupWindow.showAsDropDown(containerView, 0, 0)
}
private fun showSelectCatalogPopupWindow(containerView: View, catalogTv: TextView, catalogText: String) {
catalogTv.setTextColor(R.color.text_theme.toColor(context))
catalogTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
val inflater = LayoutInflater.from(catalogTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
).apply { mCatalogPopupWindow = this }
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
val backgroundView = layout.findViewById<View>(R.id.background)
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (entity in mCatalogFilterArray) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = catalogTv.context.resources.displayMetrics.widthPixels / 3
val height = item.layoutParams.height
item.layoutParams = ViewGroup.LayoutParams(width, height)
flexboxLayout.addView(item)
val tv = item.findViewById<TextView>(R.id.size_tv)
val iv = item.findViewById<ImageView>(R.id.recommend_iv)
tv.text = entity.name
iv.visibleIf(entity.recommended)
toggleHighlightedTextView(tv, catalogText == entity.name)
tv.tag = entity.name
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
popupWindow.dismiss()
catalogTv.text = entity.name
mOnCatalogFilterSetupListener?.onSetupSortCatalog(entity)
}
}
popupWindow.setOnDismissListener {
catalogTv.setTextColor(R.color.text_757575.toColor(context))
catalogTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
mCatalogPopupWindow = null
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = 0
popupWindow.showAsDropDown(containerView, 0, 0)
}
private fun showSelectSizePopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
sizeTv.setTextColor(R.color.text_theme.toColor(context))
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
val inflater = LayoutInflater.from(sizeTv.context)
val layout = inflater.inflate(R.layout.layout_filter_size, null)
val popupWindow = PopupWindow(
layout,
LayoutParams.MATCH_PARENT,
LayoutParams.WRAP_CONTENT
).apply { mSizePopupWindow = this }
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
val backgroundView = layout.findViewById<View>(R.id.background)
sizeFilterArray = if (sizeFilterArray == null) {
getDefaultSizeFilterArray()
} else {
sizeFilterArray?.apply {
if (firstOrNull()?.text != "全部大小") {
add(0, SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
}
}
}
backgroundView.setOnClickListener {
popupWindow.dismiss()
}
for (size in sizeFilterArray!!) {
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
// 单列 3 个,强行设置宽度为屏幕的 1/3
val width = sizeTv.context.resources.displayMetrics.widthPixels / 3
val height = item.layoutParams.height
item.layoutParams = ViewGroup.LayoutParams(width, height)
flexboxLayout.addView(item)
val tv = item.findViewById<TextView>(R.id.size_tv)
tv.text = size.text
toggleHighlightedTextView(tv, sizeText == size.text)
tv.tag = size.text
item.setOnClickListener {
toggleHighlightedTextView(tv, true)
popupWindow.dismiss()
sizeTv.text = size.text
mOnCatalogFilterSetupListener?.onSetupSortSize(size)
}
}
popupWindow.setOnDismissListener {
sizeTv.setTextColor(R.color.text_757575.toColor(context))
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
mSizePopupWindow = null
}
popupWindow.isTouchable = true
popupWindow.isFocusable = true
popupWindow.animationStyle = 0
popupWindow.showAsDropDown(containerView, 0, 0)
}
private fun getDefaultSizeFilterArray(): ArrayList<SubjectSettingEntity.Size> {
return arrayListOf<SubjectSettingEntity.Size>().apply {
add(SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
add(SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"))
add(SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"))
add(SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"))
add(SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"))
add(SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上"))
}
}
fun setRootBackgroundColor(@ColorInt color: Int) {
findViewById<View>(R.id.config_controller).setBackgroundColor(color)
}
fun setItemTextColor(@ColorInt color: Int) {
mTypeTv.setTextColor(color)
mCatalogTv.setTextColor(color)
mSizeTv.setTextColor(color)
}
fun updatePopupWindow() {
when {
mTypePopupWindow != null && mTypePopupWindow!!.isShowing -> {
mTypePopupWindow?.dismiss()
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
}
mCatalogPopupWindow != null && mCatalogPopupWindow!!.isShowing -> {
mCatalogPopupWindow?.dismiss()
showSelectCatalogPopupWindow(this, mCatalogTv, mCatalogTv.text.toString())
}
mSizePopupWindow != null && mSizePopupWindow!!.isShowing -> {
mSizePopupWindow?.dismiss()
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
}
}
}
interface OnCatalogFilterSetupListener {
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
fun onSetupSortType(sortType: SortType)
fun onSetupSortCatalog(sortCatalog: CatalogEntity.SubCatalogEntity)
}
enum class SortType(val value: String) {
RECOMMENDED("热门推荐"),
NEWEST("最新上线"),
RATING("最高评分")
}
}

View File

@ -0,0 +1,64 @@
package com.gh.common.view
import android.content.Context
import android.util.AttributeSet
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
import com.gh.gamecenter.R
import com.gh.gamecenter.category.CategoryListActivity
import com.gh.gamecenter.entity.CategoryEntity
class SubCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
LinearLayout(context, attrs, defStyleAttr) {
var leftTv: TextView
var centerTv: TextView
var rightTv: TextView
var leftDivider: View
var rightDivider: View
var primeCategory: CategoryEntity? = null
var categoryTitle: String? = ""
init {
View.inflate(context, R.layout.layout_sub_category, this)
leftTv = findViewById(R.id.tv_left_sub_category)
centerTv = findViewById(R.id.tv_center_sub_category)
rightTv = findViewById(R.id.tv_right_sub_category)
leftDivider = findViewById(R.id.divider_left)
rightDivider = findViewById(R.id.divider_right)
}
fun setLeftCategory(category: CategoryEntity) {
setCategory(leftTv, category)
}
fun setCenterCategory(category: CategoryEntity) {
setCategory(centerTv, category)
leftDivider.visibility = View.VISIBLE
}
fun setRightCategory(category: CategoryEntity) {
setCategory(rightTv, category)
rightDivider.visibility = View.VISIBLE
}
private fun setCategory(tv: TextView, category: CategoryEntity) {
tv.text = category.name
tv.setOnClickListener {
tv.context.startActivity(
CategoryListActivity.getIntent(
tv.context,
categoryTitle!!,
primeCategory!!,
category.name!!
)
)
}
}
}

View File

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

View File

@ -48,7 +48,6 @@ import com.gh.gamecenter.common.entity.SensorsEvent
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.ImageUtils.getIdealImageUrl
import com.gh.gamecenter.common.view.DragListener
import com.gh.gamecenter.common.view.DraggableBigImageView
import com.gh.gamecenter.common.view.Gh_RelativeLayout
import com.gh.gamecenter.core.runOnIoThread
@ -816,18 +815,18 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
if (mBigImageView == null) {
mBigImageView = imageView
}
imageView.setDragListener(object : DragListener {
override fun onRelease(scale: Float) {
imageView.setDragListener(object : DraggableBigImageView.DragListener {
override fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float) {
updateOriginPosition(mViewPager.currentItem)
performExitAnimation(imageView, scale, isFadeOnly())
performExitAnimation(draggableBigImageView, scale, isFadeOnly())
}
override fun onDrag(fraction: Float) {
override fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float) {
mBackgroundView.alpha = 1 - fraction
mIndicatorMask.visibility = View.GONE
}
override fun onRestore(fraction: Float) {
override fun onRestore(draggableBigImageView: DraggableBigImageView, fraction: Float) {
mBackgroundView.alpha = 1F
// mIndicatorMask.goneIf(mUrlList?.size == 1)
if (mUrlList?.size != 1 || mAnswerEntity != null) {

View File

@ -138,6 +138,7 @@ import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
import kotlin.Unit;
import kotlin.jvm.functions.Function0;
import kotlin.jvm.functions.Function1;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
import retrofit2.HttpException;
@ -148,8 +149,6 @@ public class MainActivity extends BaseActivity {
public static final String SHOW_AD = "show_ad";
public static final int COUNTDOWN_AD = 100;
private int mCountdownMaxCount = 3;
public static final int COUNTDOWN_SDK_AD = 101;
public static final int COUNTDOWN_SDK_MAX_COUNT = 5;
private int mCountdownCount = 0;
private static final String CURRENT_PAGE = "current_page";
@ -491,7 +490,6 @@ public class MainActivity extends BaseActivity {
if (AdDelegateHelper.INSTANCE.shouldShowStartUpAd(false)) {
ViewGroup startAdContainer = findViewById(R.id.startAdContainer);
ViewGroup sdkStartAdContainer = findViewById(R.id.sdkStartAdContainer);
TextView sdkJumpBtn = findViewById(R.id.sdkJumpBtn);
FrameLayout adsFl = findViewById(R.id.adsFl);
View icpContainer = findViewById(R.id.sdkStartAdIcpContainer);
if (icpContainer != null) {
@ -520,7 +518,6 @@ public class MainActivity extends BaseActivity {
screenHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsFl,
(BaseHandler) mBaseHandler,
false,
@ -540,19 +537,13 @@ public class MainActivity extends BaseActivity {
@Override
protected void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == COUNTDOWN_AD || msg.what == COUNTDOWN_SDK_AD) {
if (msg.what == COUNTDOWN_AD) {
mCountdownCount++;
int maxCount;
if (msg.what == COUNTDOWN_AD) {
maxCount = mCountdownMaxCount;
} else {
maxCount = COUNTDOWN_SDK_MAX_COUNT;
}
if (maxCount < mCountdownCount) {
if (mCountdownMaxCount < mCountdownCount) {
AdDelegateHelper.INSTANCE.setShowingSplashAd(false);
hideSplashAd();
if (msg.what == COUNTDOWN_AD && msg.obj instanceof StartupAdEntity) {
if (msg.obj instanceof StartupAdEntity) {
StartupAdEntity ad = (StartupAdEntity) msg.obj;
LinkEntity linkEntity = ad.getJump();
SensorsBridge.trackEvent(
@ -565,26 +556,14 @@ public class MainActivity extends BaseActivity {
linkEntity.getLink(),
"link_text",
linkEntity.getText());
} else if (msg.what == COUNTDOWN_SDK_AD) {
SPUtils.setLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, System.currentTimeMillis());
}
} else {
if (msg.what == COUNTDOWN_AD) {
TextView jumpBtn = findViewById(R.id.jumpBtn);
if (jumpBtn != null) {
jumpBtn.setText(getString(R.string.splash_jump, maxCount - mCountdownCount));
}
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
} else {
TextView jumpBtn = findViewById(R.id.sdkJumpBtn);
if (jumpBtn != null) {
jumpBtn.setText(getString(R.string.splash_jump, maxCount - mCountdownCount));
}
mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_SDK_AD, 1000);
}
TextView jumpBtn = findViewById(R.id.jumpBtn);
jumpBtn.setText(getString(R.string.splash_jump, mCountdownMaxCount - mCountdownCount));
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
}
}
}
@ -622,11 +601,7 @@ public class MainActivity extends BaseActivity {
ExtensionsKt.removeFromParent(startSdkAdContainer, true);
AdDelegateHelper.INSTANCE.cancelSplashAd(this);
}
TextView jumpBtn = findViewById(R.id.sdkJumpBtn);
if (jumpBtn != null) {
jumpBtn.setVisibility(View.GONE);
ExtensionsKt.removeFromParent(jumpBtn, true);
}
View startSdkAdIcpContainer = findViewById(R.id.sdkStartAdIcpContainer);
if (startSdkAdIcpContainer != null) {
startSdkAdIcpContainer.setVisibility(View.GONE);

View File

@ -10,8 +10,6 @@ import android.view.inputmethod.EditorInfo
import android.widget.*
import androidx.core.widget.doAfterTextChanged
import androidx.core.widget.doOnTextChanged
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentTransaction
import com.gh.common.util.*
import com.gh.common.util.LogUtils
import com.gh.gamecenter.DisplayType.*
@ -120,7 +118,7 @@ open class SearchActivity : BaseActivity() {
trackSearchPageShow()
}
protected open fun trackSearchPageShow() {
protected open fun trackSearchPageShow(){
val bottomTab = intent.getStringExtra(EntranceConsts.KEY_BOTTOM_TAB_NAME) ?: ""
val multiTabId = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_ID) ?: ""
val multiTabName = intent.getStringExtra(EntranceConsts.KEY_MULTI_TAB_NAV_NAME) ?: ""
@ -181,11 +179,10 @@ open class SearchActivity : BaseActivity() {
open fun search(type: SearchType, key: String?) {
mSearchType = type
mIsAutoSearchDisabled = true
// 自动搜索,默认搜索,热门搜索,历史搜索,主动搜索,榜单搜索
// 自动搜索,默认搜索,热门搜索,历史搜索,主动搜索
when (type) {
SearchType.AUTO -> handleAutoSearch(key)
SearchType.DEFAULT -> handleDefaultSearch(key)
SearchType.RANK -> handleRankSearch(key)
SearchType.HOT -> handleHotSearch(key)
SearchType.HISTORY -> handleHistorySearch(key)
SearchType.MANUAL -> handleManualSearch()
@ -216,22 +213,6 @@ open class SearchActivity : BaseActivity() {
}
}
protected open fun handleRankSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
updateDisplayType(GAME_DETAIL)
LogUtils.uploadSearchGame("searching", "搜索页", key, "榜单搜索")
SensorsBridge.trackSearchButtonClick(
GlobalActivityManager.getCurrentPageEntity().pageId,
GlobalActivityManager.getCurrentPageEntity().pageName,
key ?: "",
TRACK_SEARCH_TYPE_DEFAULT,
mSourceEntrance
)
}
protected open fun handleDefaultSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
@ -305,72 +286,38 @@ open class SearchActivity : BaseActivity() {
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(this)
open fun updateDisplayType(type: DisplayType) {
val transaction = when (type) {
DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ SearchDefaultFragment() }
) {
it.arguments = Bundle().also { bundle ->
bundle.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true)
}
}
GAME_DIGEST -> showFragment(
SearchGameIndexFragment::class.java.name,
{ SearchGameIndexFragment() }
) {
removeFragment(SearchGameResultFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
GAME_DETAIL -> showFragment(
SearchGameResultFragment::class.java.name,
{ SearchGameResultFragment() }
) {
removeFragment(SearchGameIndexFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
else -> null
}
transaction?.let {
mDisplayType = type
it.commitAllowingStateLoss()
}
}
protected fun <T : Fragment> showFragment(
tag: String,
onFragmentCreate: () -> T,
onFragmentCreated: ((T) -> Unit)? = null,
): FragmentTransaction {
val transaction = supportFragmentManager.beginTransaction()
var createNewFragment = false
var fragment = supportFragmentManager
.findFragmentByTag(tag)
if (fragment == null) {
createNewFragment = true
fragment = onFragmentCreate.invoke()
}
onFragmentCreated?.invoke(fragment as T)
if (createNewFragment) {
transaction.add(R.id.search_result, fragment, tag)
transaction.addToBackStack(null)
} else if (!fragment.isAdded) {
transaction.show(fragment)
transaction.addToBackStack(null)
}
return transaction
}
when (type) {
DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: SearchDefaultFragment().apply {
arguments = Bundle().also { it.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true) }
}
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
}
protected fun removeFragment(tag: String) {
val fragment = supportFragmentManager
.findFragmentByTag(tag) ?: return
supportFragmentManager
.beginTransaction()
.remove(fragment)
.commit()
supportFragmentManager.popBackStack()
GAME_DIGEST -> {
val digestListFragment =
supportFragmentManager.findFragmentByTag(SearchGameIndexFragment::class.java.name) as? SearchGameIndexFragment
?: SearchGameIndexFragment()
digestListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, digestListFragment, SearchGameIndexFragment::class.java.name)
}
GAME_DETAIL -> {
val detailListFragment =
supportFragmentManager.findFragmentByTag(SearchGameResultFragment::class.java.name) as? SearchGameResultFragment
?: SearchGameResultFragment()
detailListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, detailListFragment, SearchGameResultFragment::class.java.name)
}
else -> {
//do nothing
}
}
mDisplayType = type
transaction.commitAllowingStateLoss()
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -378,7 +325,6 @@ open class SearchActivity : BaseActivity() {
when (search.type) {
SearchType.HISTORY.value -> search(SearchType.HISTORY, search.key)
SearchType.HOT.value -> search(SearchType.HOT, search.key)
SearchType.RANK.value -> search(SearchType.RANK, search.key)
"click" -> DataCollectionUtils.uploadSearchClick(
this, mSearchKey, mSearchType.value, "搜索页面",
@ -395,8 +341,9 @@ open class SearchActivity : BaseActivity() {
}
override fun handleBackPressed(): Boolean {
if (supportFragmentManager.fragments.size == 1) {
finish()
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
if (fragment == null) {
updateDisplayType(DEFAULT)
return true
}
return super.handleBackPressed()
@ -470,8 +417,7 @@ enum class SearchType(var value: String) {
DEFAULT("default"),
HISTORY("history"),
MANUAL("initiative"),
HOT("remen"),
RANK("rank");
HOT("remen");
fun toChinese() = when (this) {
AUTO -> "自动搜索"
@ -479,7 +425,6 @@ enum class SearchType(var value: String) {
HISTORY -> "历史搜索"
MANUAL -> "主动搜索"
HOT -> "热门搜索"
RANK -> "榜单搜索"
}
companion object {

View File

@ -5,6 +5,7 @@ import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_PUSH;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ANSWER;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARCHIVE_LOGIN;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARTICLE;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_CATEGORY;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COLUMN;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COLUMN_COLLECTION;
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COMMUNITY;
@ -314,6 +315,10 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToCommunityColumn(this, community, columnId, entrance, "");
break;
case HOST_CATEGORY:
title = uri.getQueryParameter("title");
DirectUtils.directCategoryDirectory(this, path, title, entrance, pathName);
break;
case HOST_COLUMN_COLLECTION:
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null,false);
break;
@ -419,7 +424,7 @@ public class SkipActivity extends BaseActivity {
} else {
Bundle newBundle = new Bundle();
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivityCompat(this, newBundle, null, (resultCode, data) -> {
EntranceUtils.jumpActivity(this, null, newBundle, (resultCode, data) -> {
if(CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
}
@ -442,7 +447,7 @@ public class SkipActivity extends BaseActivity {
break;
default:
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页
break;
return;
}
}
} else if ("market".equals(uri.getScheme())) {

View File

@ -12,10 +12,8 @@ import com.gh.ad.AdDelegateHelper
import com.gh.ad.AdDelegateHelper.requestSplashAd
import com.gh.ad.AdDelegateHelper.shouldShowStartUpAd
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import java.util.*
/**
@ -39,7 +37,6 @@ class SplashAdActivity : BaseActivity() {
if (shouldShowStartUpAd(true)) {
val startAdContainer = findViewById<ViewGroup>(R.id.startAdContainer)
val sdkStartAdContainer = findViewById<ViewGroup>(R.id.sdkStartAdContainer)
val sdkJumpBtn = findViewById<TextView>(R.id.sdkJumpBtn)
val adsFl = findViewById<FrameLayout>(R.id.adsFl)
val icpContainer = findViewById<View>(R.id.sdkStartAdIcpContainer)
@ -61,7 +58,6 @@ class SplashAdActivity : BaseActivity() {
screenHeightInDp,
startAdContainer!!,
sdkStartAdContainer!!,
sdkJumpBtn!!,
adsFl!!,
(mBaseHandler as BaseHandler),
true
@ -85,23 +81,15 @@ class SplashAdActivity : BaseActivity() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == MainActivity.COUNTDOWN_AD || msg.what == MainActivity.COUNTDOWN_SDK_AD) {
if (msg.what == MainActivity.COUNTDOWN_AD) {
mCountdownCount++
val maxCount = if (msg.what == MainActivity.COUNTDOWN_AD) {
COUNTDOWN_MAX_COUNT
} else {
MainActivity.COUNTDOWN_SDK_MAX_COUNT
}
if (maxCount < mCountdownCount) {
if (COUNTDOWN_MAX_COUNT < mCountdownCount) {
AdDelegateHelper.isShowingSplashAd = false
if (msg.what == MainActivity.COUNTDOWN_SDK_AD) {
SPUtils.setLong(Constants.SP_LAST_SPLASH_AD_SHOW_TIME, System.currentTimeMillis())
}
finishActivity()
} else {
val jumpBtn = findViewById<TextView>(if (msg.what == MainActivity.COUNTDOWN_AD) R.id.jumpBtn else R.id.sdkJumpBtn)
jumpBtn?.text = getString(R.string.splash_jump, maxCount - mCountdownCount)
mBaseHandler.sendEmptyMessageDelayed(msg.what, 1000)
val jumpBtn = findViewById<TextView>(R.id.jumpBtn)
jumpBtn.text = getString(R.string.splash_jump, COUNTDOWN_MAX_COUNT - mCountdownCount)
mBaseHandler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_AD, 1000)
}
}
}

View File

@ -17,11 +17,8 @@ import androidx.core.text.color
import com.alibaba.android.arouter.facade.annotation.Route
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.common.dialog.NewPrivacyPolicyDialogFragment
import com.gh.common.util.DeviceTokenUtils
import com.gh.common.util.DialogUtils
import com.gh.common.util.*
import com.gh.common.util.GameSubstituteRepositoryHelper.updateGameSubstituteRepository
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageUtils
import com.gh.common.util.UsageStatsHelper.checkAndPostUsageStats
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.base.activity.BaseActivity
@ -31,7 +28,6 @@ import com.gh.gamecenter.common.tracker.TrackerLogger
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.IPackageUtilsProvider
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
@ -59,9 +55,6 @@ class SplashScreenActivity : BaseActivity() {
mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this)
mIsNewForThisVersion = HaloApp.getInstance().isNewForThisVersion
HaloApp.getInstance().isBrandNewInstall = SPUtils.getBoolean(Constants.SP_BRAND_NEW_USER, true)
if (HaloApp.getInstance().isBrandNewInstall) {
SPUtils.setLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME, System.currentTimeMillis())
}
// 用户不是新版本,但应用最后更新时间不是上次的时间代表用户重新安装了当前版本
if (!mIsNewForThisVersion) {
@ -281,12 +274,6 @@ class SplashScreenActivity : BaseActivity() {
SensorsBridge.init(HaloApp.getInstance(), HaloApp.getInstance().channel)
SensorsBridge.setOAID(HaloApp.getInstance().oaid)
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
val registrationId = pushProvider?.getRegistrationId(this)
if (!registrationId.isNullOrEmpty()) {
SensorsBridge.profileAppend(KEY_REGISTRATION_ID, registrationId)
}
}
private fun prefetchData() {
@ -318,7 +305,6 @@ class SplashScreenActivity : BaseActivity() {
companion object {
private const val KEY_REGISTRATION_ID = "registration_id"
const val HONOR_CULPRIT_ID = 12324
const val HONOR_CULPRIT_CHANNEL = "荣耀通道"

View File

@ -24,7 +24,6 @@ import com.gh.gamecenter.R;
import com.gh.gamecenter.adapter.viewholder.KcSelectGameViewHolder;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.BitmapUtils;
import com.gh.gamecenter.common.utils.FileUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.TimeUtils;
import com.gh.gamecenter.core.utils.ToastUtils;
@ -86,7 +85,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
Observable.create(emitter -> {
// 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError
FindAllAPKPath(Environment.getExternalStorageDirectory());
FindAllAPKPath(new File(FileUtils.getDownloadDir(mContext)));
FindAllAPKPath(mContext.getFilesDir());
LoadApkData();
emitter.onComplete();
})

View File

@ -652,12 +652,12 @@ class DetailViewHolder(
builder.addHandler(LandPageAddressHandler())
builder.addHandler(OverseaDownloadHandler())
builder.addHandler(CheckDownloadHandler())
builder.setProcessEndCallback(mGameEntity.id) { asVGame: Boolean, isSubscribe: Any? ->
builder.setProcessEndCallback { asVGame: Boolean, isSubscribe: Any? ->
download(asVGame, isSubscribe as Boolean)
}
} else {
builder.addHandler(VersionNumberHandler())
builder.setProcessEndCallback(mGameEntity.id) { _: Boolean?, _: Any? ->
builder.setProcessEndCallback { _: Boolean?, _: Any? ->
DownloadDialog.showDownloadDialog(
mViewHolder.context,
mGameEntity,

View File

@ -4,10 +4,11 @@ import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.DisplayType
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.search.SearchDefaultFragment
@ -63,16 +64,19 @@ class AmwaySearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
val transaction = when (type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ AmwaySearchDefaultFragment() }
)
val transaction = supportFragmentManager.beginTransaction()
when (type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: AmwaySearchDefaultFragment()
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
}
else -> showFragment(
AmwaySearchListFragment::class.java.name,
{ AmwaySearchListFragment() }
)
else -> {
val fragment = supportFragmentManager.findFragmentByTag(AmwaySearchListFragment::class.java.name)
?: AmwaySearchListFragment()
transaction.replace(R.id.search_result, fragment, AmwaySearchListFragment::class.java.name)
}
}
mDisplayType = type
transaction.commitAllowingStateLoss()

View File

@ -33,7 +33,7 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
mViewModel.playedGames.observeNonNull(viewLifecycleOwner) {
defaultViewModel?.isExistHotSearch = it.isNotEmpty()
updateView()
mBinding.searchDiscoveryList.run {
mBinding.hotList.run {
layoutManager = LinearLayoutManager(context)
adapter = AmwaySearchAdapter(context, mViewModel, "安利墙搜索-最近玩过").apply { setData(it) }
}
@ -45,7 +45,7 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
override fun provideDao(): ISearchHistoryDao = AmwaySearchDao()
override fun initView() {
mBinding = mAmwayBinding.searchContent
mBinding.searchDiscoveryHeadContainer.headTitle.text = "最近玩过"
mBinding.hotHeadContainer.headTitle.text = "最近玩过"
mBinding.historyFlexContainer.setLimitHeight(mFlexMaxHeight)
updateHistorySearchView(null)

View File

@ -57,7 +57,6 @@ class AmwaySearchListFragment : ToolbarFragment() {
when (it) {
LoadStatus.INIT_LOADING -> {
hideError()
hideRvList()
showLoading()
hideNoDataHint()
}
@ -65,19 +64,16 @@ class AmwaySearchListFragment : ToolbarFragment() {
hideError()
hideLoading()
hideNoDataHint()
showRvList()
}
LoadStatus.INIT_FAILED -> {
hideLoading()
hideNoDataHint()
showError()
hideRvList()
}
LoadStatus.INIT_EMPTY -> {
showNoDataHint()
hideError()
hideLoading()
hideRvList()
}
else -> {
// do nothing
@ -86,14 +82,6 @@ class AmwaySearchListFragment : ToolbarFragment() {
}
}
private fun hideRvList() {
mBinding.recyclerView.visibility = View.GONE
}
private fun showRvList() {
mBinding.recyclerView.visibility = View.VISIBLE
}
private fun showLoading() {
mBinding.loadingContainer.visibility = View.VISIBLE
}

View File

@ -44,7 +44,6 @@ class AmwaySearchViewModel(application: Application) : AndroidViewModel(applicat
mTempSearchKey = searchKey
currentSearchKey = searchKey
loadStatus.postValue(LoadStatus.INIT_LOADING)
searchGames.postValue(emptyList())
RetrofitManager
.getInstance().api
.getSearchGame(Config.API_HOST + "games:search?keyword=" + searchKey + "&view=anliwall" + "&channel=" + HaloApp.getInstance().channel + "&version=" + BuildConfig.VERSION_NAME)

View File

@ -0,0 +1,43 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.base.DownloadToolbarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
class CatalogActivity : DownloadToolbarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun showDownloadMenu(): Boolean {
return true
}
override fun provideNormalIntent(): Intent {
return getTargetIntent(this, CatalogActivity::class.java, CatalogFragment::class.java)
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun isAutoResetViewBackgroundEnabled() = true
companion object {
fun getIntent(context: Context, catalogId: String, catalogTitle: String, entrance: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_CATALOG_ID, catalogId)
bundle.putString(EntranceConsts.KEY_CATALOG_TITLE, catalogTitle)
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
return getTargetIntent(context, CatalogActivity::class.java, CatalogFragment::class.java, bundle)
}
}
}

View File

@ -0,0 +1,53 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.View
import android.view.ViewGroup
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.CatalogItemBinding
import com.gh.gamecenter.entity.CatalogEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class CatalogAdapter(
context: Context,
private val mFragment: CatalogFragment,
private val mViewModel: CatalogViewModel,
private val mList: List<CatalogEntity.SubCatalogEntity>
) : BaseRecyclerAdapter<CatalogAdapter.CatalogItemViewHolder>(context) {
override fun getItemCount() = mList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
CatalogItemViewHolder(parent.toBinding())
override fun onBindViewHolder(holder: CatalogItemViewHolder, position: Int) {
holder.binding.run {
val catalogEntity = mList[position]
catalogName.text = catalogEntity.name
recommendTag.goneIf(!catalogEntity.recommended)
if (catalogEntity.name == mViewModel.selectedCatalogName) {
selectedTag.visibility = View.VISIBLE
catalogName.setTextColor(R.color.text_theme.toColor(mContext))
root.setBackgroundColor(R.color.ui_surface.toColor(mContext))
} else {
selectedTag.visibility = View.GONE
catalogName.setTextColor(R.color.text_primary.toColor(mContext))
root.background = null
}
root.setOnClickListener {
if (catalogEntity.name != mViewModel.selectedCatalogName) {
mViewModel.selectedCatalogName = catalogEntity.name
mViewModel.logSubCatalogClick(position)
mFragment.changeCatalog(position)
notifyDataSetChanged()
}
}
}
}
class CatalogItemViewHolder(val binding: CatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,240 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import androidx.core.os.bundleOf
import androidx.lifecycle.Observer
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.common.view.FixLinearLayoutManager
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.FragmentCatalogBinding
import com.gh.gamecenter.entity.CatalogEntity
class CatalogFragment : LazyFragment() {
private var mBinding: FragmentCatalogBinding? = null
private var mViewModel: CatalogViewModel? = null
private var mEntity: CatalogEntity? = null
private var mSpecialCatalogFragment: SpecialCatalogFragment? = null
private var mSubCatalogFragment: SubCatalogFragment? = null
private var mCatalogId: String = ""
private var mCatalogTitle: String = ""
private var mLastSelectedPosition = -1
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.run {
mLastSelectedPosition = getInt(EntranceConsts.KEY_LAST_SELECTED_POSITION)
}
}
override fun onSaveInstanceState(outState: Bundle) {
mViewModel?.run {
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, selectedCatalogPosition)
}
super.onSaveInstanceState(outState)
}
override fun getRealLayoutId() = R.layout.fragment_catalog
override fun onRealLayoutInflated(inflatedView: View) {
mBinding = FragmentCatalogBinding.bind(inflatedView)
}
override fun onFragmentFirstVisible() {
mCatalogId = arguments?.getString(EntranceConsts.KEY_CATALOG_ID) ?: ""
mCatalogTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
mViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId)
mViewModel?.validEntranceName = if (mEntrance.contains("首页")) "首页" else "板块"
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
mViewModel?.validEntranceName = "首页Tab栏"
}
mViewModel?.logAppearance()
super.onFragmentFirstVisible()
}
override fun initRealView() {
super.initRealView()
setNavigationTitle(mCatalogTitle)
mViewModel?.catalogs?.observe(viewLifecycleOwner, Observer {
mBinding?.run {
reuseLoading.root.visibility = View.GONE
if (it != null) {
reuseNoConnection.root.visibility = View.GONE
if (it.subCatalog.isNotEmpty()) {
containerCatalog.visibility = View.VISIBLE
reuseNoneData.root.visibility = View.GONE
mEntity = it
if (mEntity!!.hasSpecial) {
val specialEntity = CatalogEntity.SubCatalogEntity(name = "精选")
(mEntity!!.subCatalog as ArrayList).add(0, specialEntity)
}
initView()
} else {
containerCatalog.visibility = View.GONE
reuseNoneData.root.visibility = View.VISIBLE
}
} else {
containerCatalog.visibility = View.GONE
reuseNoneData.root.visibility = View.GONE
reuseNoConnection.root.visibility = View.VISIBLE
reuseNoConnection.root.setOnClickListener {
reuseNoConnection.root.visibility = View.GONE
reuseLoading.root.visibility = View.VISIBLE
mViewModel?.getCatalogs()
}
}
}
})
// 嵌入在首页时特殊处理
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
mBinding?.divider?.visibility = View.GONE
mBinding?.root?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
mBinding?.root?.setPadding(0, 8F.dip2px(), 0, 0)
mBinding?.rvCatalog?.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
}
}
private fun initView() {
mEntity?.run {
mViewModel?.run {
if (subCatalog.isNotEmpty()) {
if (mLastSelectedPosition != -1) {
selectedCatalogPosition = mLastSelectedPosition
selectedCatalogName = subCatalog[mLastSelectedPosition].name
} else {
selectedCatalogPosition = 0
selectedCatalogName = subCatalog[0].name
}
mBinding?.rvCatalog?.layoutManager = FixLinearLayoutManager(requireContext())
mBinding?.rvCatalog?.adapter =
CatalogAdapter(requireContext(), this@CatalogFragment, this, subCatalog)
if (hasSpecial && selectedCatalogPosition == 0) {
mSpecialCatalogFragment =
childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name) as? SpecialCatalogFragment
?: SpecialCatalogFragment()
mSpecialCatalogFragment?.arguments = bundleOf(
EntranceConsts.KEY_IS_CATEGORY_V2 to false,
EntranceConsts.KEY_CATALOG_ID to id,
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle,
EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(
EntranceConsts.KEY_EXPOSURE_SOURCE
)
)
childFragmentManager.beginTransaction().replace(
R.id.container_sub_catalog,
mSpecialCatalogFragment!!,
SpecialCatalogFragment::class.java.name
).commitAllowingStateLoss()
} else {
mSubCatalogFragment =
childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.name) as? SubCatalogFragment
?: SubCatalogFragment()
mSubCatalogFragment?.arguments = bundleOf(
EntranceConsts.KEY_CATALOG_ID to id,
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[selectedCatalogPosition].id,
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
)
childFragmentManager.beginTransaction().replace(
R.id.container_sub_catalog,
mSubCatalogFragment!!,
SubCatalogFragment::class.java.name
).commitAllowingStateLoss()
}
}
}
}
}
fun changeCatalog(position: Int) {
mEntity?.run {
mViewModel?.run {
if (hasSpecial) {
changeCatalogIfHasSpecial(position)
} else {
changeSubCatalogInSubCatalogFragment(position)
}
selectedCatalogPosition = position
}
}
}
private fun changeCatalogIfHasSpecial(position: Int) {
mViewModel?.run {
when {
selectedCatalogPosition == 0 -> changeToSubCatalogFragment(position)
position == 0 -> changeToSpecialCatalogFragment()
else -> changeSubCatalogInSubCatalogFragment(position)
}
}
}
private fun changeToSubCatalogFragment(position: Int) {
mEntity?.run {
mSubCatalogFragment =
childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.name) as? SubCatalogFragment
?: SubCatalogFragment()
mSubCatalogFragment?.arguments = bundleOf(
EntranceConsts.KEY_CATALOG_ID to id,
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[position].id,
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
)
childFragmentManager.beginTransaction().replace(
R.id.container_sub_catalog,
mSubCatalogFragment!!,
SubCatalogFragment::class.java.name
).commitAllowingStateLoss()
}
}
private fun changeToSpecialCatalogFragment() {
mEntity?.run {
mSpecialCatalogFragment =
childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name) as? SpecialCatalogFragment
?: SpecialCatalogFragment()
mSpecialCatalogFragment?.arguments = bundleOf(
EntranceConsts.KEY_IS_CATEGORY_V2 to false,
EntranceConsts.KEY_CATALOG_ID to id,
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle,
EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(
EntranceConsts.KEY_EXPOSURE_SOURCE
)
)
childFragmentManager.beginTransaction().replace(
R.id.container_sub_catalog,
mSpecialCatalogFragment!!,
SpecialCatalogFragment::class.java.name
).commitAllowingStateLoss()
}
}
private fun changeSubCatalogInSubCatalogFragment(position: Int) {
mEntity?.run {
if (mSubCatalogFragment?.isStateSaved == false) {
mSubCatalogFragment?.arguments = bundleOf(
EntranceConsts.KEY_CATALOG_ID to id,
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[position].id,
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
)
}
mSubCatalogFragment?.changeSubCatalog(subCatalog[position].id)
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mBinding?.rvCatalog?.adapter?.run {
notifyItemRangeChanged(0, itemCount)
}
}
}

View File

@ -0,0 +1,72 @@
package com.gh.gamecenter.catalog
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.common.util.LogUtils
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
class CatalogViewModel(application: Application, val catalogId: String, val catalogTitle: String) :
AndroidViewModel(application) {
private val api = RetrofitManager.getInstance().api
var catalogs = MutableLiveData<CatalogEntity>()
var selectedCatalogName: String = ""
var selectedCatalogPosition: Int = 0
var validEntranceName: String = ""
init {
getCatalogs()
}
@SuppressLint("CheckResult")
fun getCatalogs() {
api.getCatalogs(catalogId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<CatalogEntity>() {
override fun onSuccess(data: CatalogEntity) {
catalogs.postValue(data)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
catalogs.postValue(null)
}
})
}
fun logAppearance() {
LogUtils.logNewCatalogAppearanceEvent(validEntranceName, catalogTitle)
}
fun logSubCatalogClick(position: Int) {
LogUtils.logSubCatalogClickEvent(validEntranceName, "${catalogTitle}_${selectedCatalogName}", position)
}
fun logSubCatalogContentClick(itemName: String, listPosition: Int) {
LogUtils.logSubCatalogContentClickEvent(
validEntranceName,
"${catalogTitle}_${selectedCatalogName}_${itemName}",
selectedCatalogPosition,
listPosition
)
}
class Factory(private val catalogId: String, private val catalogTitle: String) :
ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return CatalogViewModel(HaloApp.getInstance().application, catalogId, catalogTitle) as T
}
}
}

View File

@ -0,0 +1,56 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.base.DownloadToolbarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.entity.CatalogEntity
class NewCatalogListActivity : DownloadToolbarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun showDownloadMenu(): Boolean {
return true
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun isAutoResetViewBackgroundEnabled() = true
companion object {
fun getIntent(
context: Context,
primaryCatalogId: String, // 一级分类 id
primaryCatalogName: String, // 一级分类名
catalogTitle: String,
catalog: CatalogEntity,
initTitle: String
): Intent {
val bundle = Bundle()
bundle.putParcelable(EntranceConsts.KEY_DATA, catalog)
bundle.putString(EntranceConsts.KEY_PRIMARY_CATALOG_ID, primaryCatalogId)
bundle.putString(EntranceConsts.KEY_PRIMARY_CATALOG_NAME, primaryCatalogName)
bundle.putString(EntranceConsts.KEY_NAME, catalog.name)
bundle.putString(EntranceConsts.KEY_CATALOG_TITLE, catalogTitle)
bundle.putString(EntranceConsts.KEY_CATALOG_INIT_TITLE, initTitle)
return getTargetIntent(
context,
NewCatalogListActivity::class.java,
NewCatalogListFragment::class.java,
bundle
)
}
}
}

View File

@ -0,0 +1,192 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.util.SparseArray
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.IExposable
import com.gh.common.util.DownloadItemUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.lightgame.download.DownloadEntity
class NewCatalogListAdapter(
context: Context,
private val mBaseExposureSource: ExposureSource,
private val mViewModel: NewCatalogListViewModel,
private val mEntrance: String?
) : ListAdapter<GameEntity>(context), IExposable {
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
val positionAndPackageMap = HashMap<String, Int>()
override fun setListData(updateData: MutableList<GameEntity>?) {
// 记录游戏位置
if (updateData != null) {
for (i in 0 until updateData.size) {
val gameEntity = updateData[i]
var packages = gameEntity.id
for (apkEntity in gameEntity.getApk()) {
packages += apkEntity.packageName
}
positionAndPackageMap[packages + i] = i
}
}
super.setListData(updateData)
}
fun clearPositionAndPackageMap() {
positionAndPackageMap.clear()
}
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
return oldItem?.id == newItem?.id
}
override fun getItemViewType(position: Int): Int {
if (position == itemCount - 1) {
return ItemViewType.ITEM_FOOTER
}
return ItemViewType.GAME_NORMAL
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.GAME_NORMAL -> {
GameItemViewHolder(parent.toBinding())
}
else -> {
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is GameItemViewHolder) {
val padTop = if (position == 0) 16F.dip2px() else 8F.dip2px()
holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 8F.dip2px())
val gameEntity = mEntityList[position]
holder.bindGameItem(gameEntity)
holder.initServerType(gameEntity)
val sortType = mViewModel.sortType.value
val sortSize = mViewModel.sortSize.text
val toolbarTitle = mViewModel.title
val selectedCatalogName = mViewModel.selectedCatalog.name
val exposureSources = ArrayList<ExposureSource>()
exposureSources.add(mBaseExposureSource)
exposureSources.add(ExposureSource(toolbarTitle))
exposureSources.add(ExposureSource("二级分类详情", "$selectedCatalogName+$sortType+$sortSize"))
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
mExposureEventSparseArray.put(position, event)
holder.itemView.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
mContext,
gameEntity,
StringUtils.buildString(
mEntrance,
"+(",
toolbarTitle,
":列表[",
selectedCatalogName,
"=",
sortType,
"=",
(position + 1).toString(),
"])"
),
traceEvent = event
)
}
DownloadItemUtils.setOnClickListener(
mContext,
holder.binding.downloadBtn,
gameEntity,
position,
this,
StringUtils.buildString(
StringUtils.buildString(
mEntrance,
"+(",
toolbarTitle,
":列表[",
selectedCatalogName,
"=",
sortType,
"=",
(position + 1).toString(),
"])"
)
),
location = StringUtils.buildString(selectedCatalogName, ":", gameEntity.name),
traceEvent = event
)
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), "star&brief")
} else if (holder is FooterViewHolder) {
holder.initItemPadding()
holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
}
}
fun notifyItemByDownload(download: DownloadEntity) {
for (key in positionAndPackageMap.keys) {
// sentry上报download.packageName可能为空
if (download.packageName != null
&& download.gameId != null
&& key.contains(download.packageName)
&& key.contains(download.gameId)
) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap()[download.platform] = download
notifyItemChanged(position)
}
}
}
}
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
for (key in positionAndPackageMap.keys) {
if (key.contains(status.packageName) && key.contains(status.gameId)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap().remove(status.platform)
notifyItemChanged(position)
}
}
}
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mExposureEventSparseArray.get(pos)
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return null
}
}

View File

@ -0,0 +1,209 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import com.ethanhua.skeleton.Skeleton
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.view.CatalogFilterView
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.databinding.FragmentCatalogListBinding
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class NewCatalogListFragment : ListFragment<GameEntity, NewCatalogListViewModel>() {
private var mPrimaryCatalogName: String = "" // 一级分类名
private var mPrimaryCatalogId: String = "" // 一级分类 id
private var mPrimeCatalog: CatalogEntity? = null
private var mSubCatalogList = arrayListOf<CatalogEntity.SubCatalogEntity>()
private var mInitCatalogName = ""
private var mAdapter: NewCatalogListAdapter? = null
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
showUnzipFailureDialog(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
}
}
private var mBinding: FragmentCatalogListBinding? = null
private lateinit var mExposureListener: ExposureListener
private lateinit var mViewModel: NewCatalogListViewModel
override fun getLayoutId() = 0
override fun getInflatedLayout() = FragmentCatalogListBinding.inflate(layoutInflater).apply { mBinding = this }.root
override fun provideListViewModel() = viewModelProvider<NewCatalogListViewModel>()
override fun provideListAdapter() = mAdapter
?: NewCatalogListAdapter(
requireContext(),
ExposureSource(mPrimaryCatalogName),
mViewModel,
mEntrance
).apply { mAdapter = this }
override fun getItemDecoration() = null
override fun onCreate(savedInstanceState: Bundle?) {
mViewModel = provideListViewModel()
mViewModel.title = arguments?.getString(EntranceConsts.KEY_NAME) ?: ""
mViewModel.categoryTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
mPrimeCatalog = arguments?.getParcelable(EntranceConsts.KEY_DATA)
mSubCatalogList = mPrimeCatalog?.subCatalog as? ArrayList<CatalogEntity.SubCatalogEntity> ?: arrayListOf()
mInitCatalogName = arguments?.getString(EntranceConsts.KEY_CATALOG_INIT_TITLE) ?: ""
mPrimaryCatalogName = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_NAME) ?: ""
mPrimaryCatalogId = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_ID) ?: ""
mViewModel.selectedCatalog = mSubCatalogList.find { entity -> entity.name == mInitCatalogName }
?: CatalogEntity.SubCatalogEntity()
mViewModel.primaryCatalogId = mPrimaryCatalogId
initSortType()
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setNavigationTitle(mViewModel.title)
initFilterView()
mViewModel.refresh.observeNonNull(this) { onRefresh() }
mExposureListener = ExposureListener(this, mAdapter!!)
mListRv.addOnScrollListener(mExposureListener)
mSkeletonScreen = Skeleton.bind(mBinding?.listSkeleton)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
.color(R.color.ui_skeleton_highlight)
.duration(Constants.SHIMMER_DURATION)
.maskWidth(Constants.MASK_WIDTH)
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.load(R.layout.fragment_tags_skeleton)
.show()
}
private fun initSortType() {
mPrimeCatalog?.switch?.run {
if (hotSort == "on") {
mViewModel.sortType = CatalogFilterView.SortType.RECOMMENDED
return@run
}
if (newSort == "on") {
mViewModel.sortType = CatalogFilterView.SortType.NEWEST
return@run
}
if (starSort == "on") {
mViewModel.sortType = CatalogFilterView.SortType.RATING
return@run
}
}
}
private fun initFilterView() {
mBinding?.filterContainer?.run {
visibility = View.VISIBLE
setTypeList(mPrimeCatalog?.switch ?: CatalogEntity.CatalogSwitch())
setCatalogList(mSubCatalogList, mInitCatalogName)
setOnConfigSetupListener(object : CatalogFilterView.OnCatalogFilterSetupListener {
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mViewModel.updateSortConfig(sortSize = sortSize)
}
override fun onSetupSortType(sortType: CatalogFilterView.SortType) {
mViewModel.updateSortConfig(sortType = sortType)
}
override fun onSetupSortCatalog(sortCatalog: CatalogEntity.SubCatalogEntity) {
mViewModel.updateSortConfig(sortCatalog = sortCatalog)
}
})
}
}
override fun onResume() {
super.onResume()
DownloadManager.getInstance().addObserver(mDataWatcher)
}
override fun onPause() {
super.onPause()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
override fun onRefresh() {
mAdapter?.clearPositionAndPackageMap()
super.onRefresh()
}
// 下载被删除事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBDownloadStatus) {
if ("delete" == status.status) {
mAdapter?.notifyItemAndRemoveDownload(status)
}
}
// 安装/卸载 事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(busFour: EBPackage) {
if (busFour.isInstalledOrUninstalled()) {
mAdapter?.notifyDataSetChanged()
}
}
fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
val data = mAdapter?.positionAndPackageMap ?: return
for (gameAndPosition in data) {
if (gameAndPosition.key.contains(downloadEntity.packageName)) {
val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
if (targetView != null) {
DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
return
}
}
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mBinding?.filterContainer?.run {
setRootBackgroundColor(R.color.ui_surface.toColor(requireContext()))
setItemTextColor(R.color.text_secondary.toColor(requireContext()))
updatePopupWindow()
}
}
}

View File

@ -0,0 +1,98 @@
package com.gh.gamecenter.catalog
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.gh.gamecenter.common.entity.ExposureEntity
import com.gh.common.exposure.ExposureUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.common.view.CatalogFilterView
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
class NewCatalogListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
var title = "" // 显示在 Toolbar 的标题
var categoryTitle = "" // 跳转进来的分类的标题
val refresh = MutableLiveData<Boolean>()
var selectedCatalog = CatalogEntity.SubCatalogEntity()
var sortType = CatalogFilterView.SortType.RECOMMENDED
var sortSize = SubjectSettingEntity.Size()
var primaryCatalogId = "" // 一级分类 id
private val sensitiveApi = RetrofitManager.getInstance().api
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
return if (selectedCatalog.link.type == "column") { // column(专题)/tag(标签)
sensitiveApi.getColumn(selectedCatalog.link.link, getSortType(), getSortSize(), page) // 专题
} else {
sensitiveApi.getGamesWithSpecificTag(getSortSize(), getSortType(), page) // 标签
}
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) {
it.forEach { game -> game.hideSizeInsideDes = true }
ExposureUtils.updateExposureInfo(
gameList = it,
containerId = primaryCatalogId,
containerType = ExposureEntity.CATEGORY_ID
)
mResultLiveData.postValue(it)
}
}
fun updateSortConfig(
sortSize: SubjectSettingEntity.Size? = null,
sortType: CatalogFilterView.SortType? = null,
sortCatalog: CatalogEntity.SubCatalogEntity? = null
) {
when {
sortSize != null && sortSize != this.sortSize -> {
this.sortSize = sortSize
refresh.postValue(true)
}
sortType != null && sortType != this.sortType -> {
this.sortType = sortType
refresh.postValue(true)
}
sortCatalog != null && sortCatalog != selectedCatalog -> {
selectedCatalog = sortCatalog
refresh.postValue(true)
}
}
}
private fun getSortSize(): String? {
return if (selectedCatalog.link.type == "column") {
UrlFilterUtils.getFilterQuery(
"min_size", sortSize.min.toString(),
"max_size", sortSize.max.toString()
)
} else {
UrlFilterUtils.getFilterQuery(
"tag_id", selectedCatalog.link.link,
"min_size", sortSize.min.toString(),
"max_size", sortSize.max.toString()
)
}
}
private fun getSortType(): String? {
return when (sortType) {
CatalogFilterView.SortType.RECOMMENDED -> if (selectedCatalog.link.type == "column") "position:1" else "download:-1"
CatalogFilterView.SortType.NEWEST -> "publish:-1"
CatalogFilterView.SortType.RATING -> "star:-1"
}
}
}

View File

@ -0,0 +1,54 @@
package com.gh.gamecenter.catalog
import android.content.Context
import android.view.ViewGroup
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.SubCatalogItemBinding
import com.gh.gamecenter.entity.CatalogEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class SubCatalogAdapter(
context: Context,
private val mCatalogViewModel: CatalogViewModel,
private val mPrimaryCatalog: CatalogEntity,
private var mList: List<CatalogEntity.SubCatalogEntity>
) : BaseRecyclerAdapter<SubCatalogAdapter.SubCatalogItemViewHolder>(context) {
private val mTypes = listOf("专题", "标签")
override fun getItemCount() = mList.size
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
SubCatalogItemViewHolder(parent.toBinding())
override fun onBindViewHolder(holder: SubCatalogItemViewHolder, position: Int) {
holder.binding.run {
val catalogEntity = mList[position]
ImageUtils.display(catalogIcon, catalogEntity.icon)
catalogName.text = catalogEntity.name
catalogName.setTextColor(R.color.text_primary.toColor(mContext))
recommendTag.goneIf(!catalogEntity.recommended)
root.setOnClickListener {
mCatalogViewModel.logSubCatalogContentClick(catalogEntity.name, position)
if (mTypes.contains(catalogEntity.type)) {
root.context.startActivity(
NewCatalogListActivity.getIntent(
mContext,
mCatalogViewModel.catalogId,
mCatalogViewModel.catalogTitle,
catalogEntity.name,
mPrimaryCatalog,
catalogEntity.name
)
)
} else {
DialogHelper.showUpgradeDialog(mContext)
}
}
}
}
class SubCatalogItemViewHolder(val binding: SubCatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
}

View File

@ -0,0 +1,94 @@
package com.gh.gamecenter.catalog
import android.os.Bundle
import android.view.View
import androidx.lifecycle.Observer
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
import com.gh.gamecenter.databinding.FragmentSubCatalogBinding
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
class SubCatalogFragment : ToolbarFragment() {
private var mBinding: FragmentSubCatalogBinding? = null
private var mViewModel: SubCatalogViewModel? = null
private var mCatalogViewModel: CatalogViewModel? = null
private var mEntity: CatalogEntity? = null
private var mCatalogId: String = ""
private var mCatalogTitle: String = ""
private var mPrimaryCatalogId: String = ""
override fun getLayoutId() = 0
override fun getInflatedLayout() = FragmentSubCatalogBinding.inflate(layoutInflater).apply { mBinding = this }.root
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mCatalogId = arguments?.getString(EntranceConsts.KEY_CATALOG_ID) ?: ""
mCatalogTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
mPrimaryCatalogId = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_ID) ?: ""
mViewModel = viewModelProvider(SubCatalogViewModel.Factory(mCatalogId))
mCatalogViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId)
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel?.catalogs?.observe(viewLifecycleOwner, Observer {
mBinding?.run {
reuseLoading.root.visibility = View.GONE
if (it != null) {
reuseNoConnection.root.visibility = View.GONE
if (it.subCatalog.isNotEmpty()) {
rvSubCatalog.visibility = View.VISIBLE
reuseNoneData.root.visibility = View.GONE
mEntity = it
initView()
} else {
rvSubCatalog.visibility = View.GONE
reuseNoneData.root.visibility = View.VISIBLE
}
} else {
rvSubCatalog.visibility = View.GONE
reuseNoneData.root.visibility = View.GONE
reuseNoConnection.root.visibility = View.VISIBLE
reuseNoConnection.root.setOnClickListener {
reuseLoading.root.visibility = View.VISIBLE
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
}
}
}
})
}
private fun initView() {
mEntity?.takeIf { mCatalogViewModel != null }?.run {
mBinding?.rvSubCatalog?.layoutManager = GridLayoutManager(requireContext(), 3)
mBinding?.rvSubCatalog?.adapter = SubCatalogAdapter(requireContext(), mCatalogViewModel!!, this, subCatalog)
}
}
fun changeSubCatalog(primaryCatalogId: String) {
mBinding?.run {
mPrimaryCatalogId = primaryCatalogId
rvSubCatalog.visibility = View.GONE
reuseNoneData.root.visibility = View.GONE
reuseNoConnection.root.visibility = View.GONE
reuseLoading.root.visibility = View.VISIBLE
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mBinding?.rvSubCatalog?.adapter?.run {
mBinding?.rvSubCatalog?.recycledViewPool?.clear()
notifyItemRangeChanged(0, itemCount)
}
}
}

View File

@ -0,0 +1,44 @@
package com.gh.gamecenter.catalog
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.entity.CatalogEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
class SubCatalogViewModel(application: Application, private val catalogId: String) : AndroidViewModel(application) {
private val api = RetrofitManager.getInstance().api
var catalogs = MutableLiveData<CatalogEntity>()
@SuppressLint("CheckResult")
fun getSubCatalogs(primaryCatalogId: String) {
api.getSubCatalogs(catalogId, primaryCatalogId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : BiResponse<CatalogEntity>() {
override fun onSuccess(data: CatalogEntity) {
catalogs.postValue(data)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
catalogs.postValue(null)
}
})
}
class Factory(private val catalogId: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return SubCatalogViewModel(HaloApp.getInstance().application, catalogId) as T
}
}
}

View File

@ -0,0 +1,51 @@
package com.gh.gamecenter.category
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.base.DownloadToolbarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
class CategoryDirectoryActivity : DownloadToolbarActivity() {
companion object {
fun getIntent(context: Context, categoryId: String, categoryTitle: String): Intent {
val bundle = Bundle()
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, categoryId)
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, categoryTitle)
return getTargetIntent(
context,
CategoryDirectoryActivity::class.java,
CategoryDirectoryFragment::class.java,
bundle
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun showDownloadMenu(): Boolean {
return true
}
override fun getActivityNameInChinese(): String {
return "游戏分类"
}
override fun provideNormalIntent(): Intent {
return getTargetIntent(this, CategoryDirectoryActivity::class.java, CategoryDirectoryFragment::class.java)
}
override fun isAutoResetViewBackgroundEnabled() = true
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
}

View File

@ -0,0 +1,157 @@
package com.gh.gamecenter.category
import android.content.Context
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.view.SubCategoryView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.category.CategoryListActivity.Companion.getIntent
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.ItemCategoryBinding
import com.gh.gamecenter.entity.CategoryEntity
import net.cachapa.expandablelayout.ExpandableLayout
class CategoryDirectoryAdapter(context: Context, var categoryTitle: String) : ListAdapter<CategoryEntity>(context) {
var recyclerView: RecyclerView? = null
var expandStatusMap: HashMap<Int, Boolean> = HashMap()
private var sixteenDp: Int = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
if (sixteenDp == 0) sixteenDp = DisplayUtils.dip2px(parent.context, 16f)
return CategoryViewHolder(parent.toBinding())
}
override fun getItemCount(): Int {
return mEntityList.size
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
when (holder) {
is CategoryViewHolder -> {
val category = mEntityList[position]
holder.binding.run {
divider.setBackgroundColor(R.color.ui_background.toColor(mContext))
containerPrimaryCategory.setOnClickListener {
root.context.startActivity(getIntent(root.context, categoryTitle, category, "全部"))
}
ImageUtils.display(iconIv, category.icon)
categoryName.text = category.name
}
holder.bindCategory(
category = mEntityList[position],
expandableStatusMap = expandStatusMap,
isExpended = expandStatusMap[position] != null && expandStatusMap[position] == true,
marginTop = sixteenDp,
categoryTitle = categoryTitle,
expandedAction = { recyclerView?.smoothScrollToPosition(position) })
}
}
}
internal class CategoryViewHolder(var binding: ItemCategoryBinding) : RecyclerView.ViewHolder(binding.root) {
fun bindCategory(
category: CategoryEntity,
expandableStatusMap: HashMap<Int, Boolean>,
isExpended: Boolean,
marginTop: Int,
categoryTitle: String,
expandedAction: () -> Unit
) {
category.data?.let {
var subCategoryView: SubCategoryView? = null
binding.containerUnexpandable.removeAllViews()
val unexpandableSize = if (it.size > 6) 6 else it.size
val unexpandableCategoryList = it.subList(0, unexpandableSize)
unexpandableCategoryList.forEachIndexed { index, c ->
when (index % 3) {
0 -> {
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
if (subCategoryView != null) params.setMargins(0, marginTop, 0, 0)
subCategoryView = SubCategoryView(binding.root.context)
subCategoryView?.categoryTitle = categoryTitle
subCategoryView?.primeCategory = category
subCategoryView?.layoutParams = params
binding.containerUnexpandable.addView(subCategoryView)
subCategoryView?.setLeftCategory(c)
}
1 -> subCategoryView?.setCenterCategory(c)
2 -> subCategoryView?.setRightCategory(c)
}
}
if (it.size >= 7) {
val extraCategoryList = it.subList(6, it.size)
binding.ivToggle.visibility = View.VISIBLE
binding.ivToggle.setOnClickListener { binding.containerExpandable.toggle() }
when {
isExpended -> binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_up)
else -> binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_down)
}
val subCategoryViewLinearLayout = LinearLayout(binding.root.context)
subCategoryViewLinearLayout.orientation = LinearLayout.VERTICAL
subCategoryView = SubCategoryView(binding.root.context)
binding.containerExpandable.removeAllViews()
binding.containerExpandable.addView(subCategoryViewLinearLayout)
binding.containerExpandable.setExpanded(isExpended, false)
binding.containerExpandable.setOnExpansionUpdateListener { _, state ->
when (state) {
ExpandableLayout.State.COLLAPSED -> {
binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_down)
expandableStatusMap[adapterPosition] = false
}
ExpandableLayout.State.EXPANDED -> {
binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_up)
expandableStatusMap[adapterPosition] = true
expandedAction.invoke()
}
}
}
extraCategoryList.forEachIndexed { index, c ->
when (index % 3) {
0 -> {
subCategoryView = SubCategoryView(binding.root.context)
val params = LinearLayout.LayoutParams(
LinearLayout.LayoutParams.MATCH_PARENT,
LinearLayout.LayoutParams.MATCH_PARENT
)
params.setMargins(0, marginTop, 0, 0)
subCategoryView?.categoryTitle = categoryTitle
subCategoryView?.primeCategory = category
subCategoryView?.layoutParams = params
subCategoryViewLinearLayout.addView(subCategoryView)
subCategoryView?.setLeftCategory(c)
}
1 -> subCategoryView?.setCenterCategory(c)
2 -> subCategoryView?.setRightCategory(c)
}
}
} else {
binding.ivToggle.visibility = View.GONE
binding.containerExpandable.removeAllViews()
}
}
}
}
}

View File

@ -0,0 +1,104 @@
package com.gh.gamecenter.category
import android.graphics.Color
import android.view.View
import androidx.lifecycle.ViewModelProviders
import com.ethanhua.skeleton.Skeleton
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.view.VerticalItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.LazyListFragment
import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding
import com.gh.gamecenter.entity.CategoryEntity
class CategoryDirectoryFragment : LazyListFragment<CategoryEntity, CategoryDirectoryListViewModel>() {
private lateinit var mViewModel: CategoryDirectoryListViewModel
private lateinit var mAdapter: CategoryDirectoryAdapter
private var mBinding: FragmentListBaseSkeletonBinding? = null
override fun getRealLayoutId() = R.layout.fragment_list_base_skeleton
override fun onRealLayoutInflated(inflatedView: View) {
super.onRealLayoutInflated(inflatedView)
mBinding = FragmentListBaseSkeletonBinding.bind(inflatedView)
}
override fun onFragmentFirstVisible() {
mViewModel = ViewModelProviders.of(this).get(CategoryDirectoryListViewModel::class.java)
mViewModel.categoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
mAdapter =
CategoryDirectoryAdapter(requireContext(), arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: "")
super.onFragmentFirstVisible()
}
override fun initRealView() {
super.initRealView()
setNavigationTitle(arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE))
mSkeletonScreen = Skeleton.bind(mBinding?.listSkeleton)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
.color(R.color.ui_skeleton_highlight)
.duration(Constants.SHIMMER_DURATION)
.maskWidth(Constants.MASK_WIDTH)
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.load(R.layout.fragment_category_skeleton)
.show()
onLoadRefresh()
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
mListRv?.setPadding(
0,
context?.resources?.getDimension(R.dimen.home_recyclerview_padding_top)?.toInt() ?: 0,
0,
0
)
mListRv?.clipToPadding = false
}
mListRv?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
mAdapter.recyclerView = mListRv
mListRefresh?.isEnabled = false
}
override fun isAutomaticLoad() = false
override fun provideListAdapter() = mAdapter
override fun provideListViewModel() = mViewModel
override fun getItemDecoration() = VerticalItemDecoration(context, 0F, false)
override fun onLoadRefresh() {
mCachedView?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
super.onLoadRefresh()
}
override fun onLoadDone() {
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
super.onLoadDone()
}
override fun onLoadEmpty() {
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
super.onLoadEmpty()
}
override fun onLoadError() {
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
super.onLoadError()
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mListRv?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
mListRv?.adapter?.run {
mListRv?.recycledViewPool?.clear()
notifyItemRangeChanged(0, itemCount)
}
}
}

View File

@ -0,0 +1,21 @@
package com.gh.gamecenter.category
import android.app.Application
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.entity.CategoryEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
class CategoryDirectoryListViewModel(application: Application) :
ListViewModel<CategoryEntity, CategoryEntity>(application) {
var categoryId = ""
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData, mResultLiveData::postValue)
}
override fun provideDataObservable(page: Int): Observable<MutableList<CategoryEntity>> {
return RetrofitManager.getInstance().api.getCategories(categoryId, page)
}
}

View File

@ -0,0 +1,53 @@
package com.gh.gamecenter.category
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.base.DownloadToolbarActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.entity.CategoryEntity
class CategoryListActivity : DownloadToolbarActivity() {
companion object {
fun getIntent(context: Context, categoryTitle: String, category: CategoryEntity, initTitle: String): Intent {
val bundle = Bundle()
if (category.data!!.isEmpty() || category.data!![0].name != "全部") {
val plainCategory = CategoryEntity(id = category.id, name = "全部")
((category.data) as ArrayList).add(0, plainCategory)
}
bundle.putParcelable(EntranceConsts.KEY_DATA, category)
bundle.putString(EntranceConsts.KEY_NAME, category.name)
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, categoryTitle)
bundle.putString(EntranceConsts.KEY_CATEGORY_INIT_TITLE, initTitle)
return getTargetIntent(
context,
CategoryListActivity::class.java,
NewCategoryListFragment::class.java,
bundle
)
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
setToolbarMenu(R.menu.menu_download)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
override fun showDownloadMenu(): Boolean {
return true
}
override fun getActivityNameInChinese(): String {
return "游戏分类详情"
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
}

View File

@ -0,0 +1,51 @@
package com.gh.gamecenter.category
import android.content.Context
import android.view.ViewGroup
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.databinding.ItemTagBinding
import com.gh.gamecenter.entity.CategoryEntity
import com.lightgame.adapter.BaseRecyclerAdapter
class NewCategoryHorizontalAdapter(
context: Context,
private val mViewModel: NewCategoryListViewModel,
private val mCategoryList: List<CategoryEntity>,
private val mSmoothScrollAction: (position: Int) -> Unit
) : BaseRecyclerAdapter<TagsViewHolder>(context) {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagsViewHolder {
return TagsViewHolder(parent.toBinding())
}
override fun onBindViewHolder(holder: TagsViewHolder, position: Int) {
val categoryEntity = mCategoryList[position]
if (mViewModel.selectedCategory.name == categoryEntity.name) {
holder.binding.tagTv.background = R.drawable.bg_tag_text.toDrawable()
holder.binding.tagTv.setTextColor(R.color.text_white.toColor(mContext))
} else {
holder.binding.tagTv.background = null
holder.binding.tagTv.setTextColor(R.color.text_757575.toColor(mContext))
}
holder.binding.tagTv.text = categoryEntity.name
holder.binding.tagTv.setOnClickListener {
mViewModel.changeSelectedCategory(categoryEntity)
notifyDataSetChanged()
mSmoothScrollAction.invoke(position)
}
}
override fun getItemCount(): Int {
return mCategoryList.size
}
}
class TagsViewHolder(val binding: ItemTagBinding) : BaseRecyclerViewHolder<Any>(binding.root)

View File

@ -0,0 +1,185 @@
package com.gh.gamecenter.category
import android.content.Context
import android.util.SparseArray
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.IExposable
import com.gh.common.util.DownloadItemUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.constant.ItemViewType
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.viewholder.FooterViewHolder
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.lightgame.download.DownloadEntity
class NewCategoryListAdapter(
context: Context,
private val mViewModel: NewCategoryListViewModel,
private val mEntrance: String?
) : ListAdapter<GameEntity>(context), IExposable {
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
val positionAndPackageMap = HashMap<String, Int>()
override fun setListData(updateData: MutableList<GameEntity>?) {
// 记录游戏位置
if (updateData != null) {
for (i in 0 until updateData.size) {
val gameEntity = updateData[i]
var packages = gameEntity.id
for (apkEntity in gameEntity.getApk()) {
packages += apkEntity.packageName
}
positionAndPackageMap[packages + i] = i
}
}
super.setListData(updateData)
}
fun clearPositionAndPackageMap() {
positionAndPackageMap.clear()
}
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
return oldItem?.id == newItem?.id
}
override fun getItemViewType(position: Int): Int {
if (position == itemCount - 1) {
return ItemViewType.ITEM_FOOTER
}
return ItemViewType.GAME_NORMAL
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return when (viewType) {
ItemViewType.GAME_NORMAL -> {
GameItemViewHolder(parent.toBinding())
}
else -> {
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
}
}
}
override fun getItemCount(): Int {
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is GameItemViewHolder) {
val gameEntity = mEntityList[position]
holder.bindGameItem(gameEntity)
holder.initServerType(gameEntity)
val padTop = if (position == 0) 16F.dip2px() else 8F.dip2px()
holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 8F.dip2px())
val sortType = if ("download:-1" == mViewModel.getSortType()) "最热" else "最新"
val toolbarTitle = mViewModel.title
val categoryTitle = mViewModel.categoryTitle
val selectedCategoryName = mViewModel.selectedCategory.name ?: ""
val exposureSources = ArrayList<ExposureSource>()
exposureSources.add(ExposureSource(categoryTitle, selectedCategoryName))
exposureSources.add(ExposureSource("二级分类", "$selectedCategoryName+$sortType"))
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
mExposureEventSparseArray.put(position, event)
holder.itemView.setOnClickListener {
GameDetailActivity.startGameDetailActivity(
mContext,
gameEntity,
StringUtils.buildString(
mEntrance,
"+(",
toolbarTitle,
":列表[",
selectedCategoryName,
"=",
sortType,
"=",
(position + 1).toString(),
"])"
),
traceEvent = event
)
}
DownloadItemUtils.setOnClickListener(
mContext,
holder.binding.downloadBtn,
gameEntity,
position,
this,
StringUtils.buildString(
StringUtils.buildString(
mEntrance,
"+(",
toolbarTitle,
":列表[",
selectedCategoryName,
"=",
sortType,
"=",
(position + 1).toString(),
"])"
)
),
location = StringUtils.buildString(selectedCategoryName, ":", gameEntity.name),
traceEvent = event
)
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding))
} else if (holder is FooterViewHolder) {
holder.initItemPadding()
holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
}
}
fun notifyItemByDownload(download: DownloadEntity) {
for (key in positionAndPackageMap.keys) {
if (key.contains(download.packageName) && key.contains(download.gameId)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap()[download.platform] = download
notifyItemChanged(position)
}
}
}
}
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
for (key in positionAndPackageMap.keys) {
if (key.contains(status.packageName) && key.contains(status.gameId)) {
val position = positionAndPackageMap[key]
if (position != null && mEntityList != null && position < mEntityList.size) {
mEntityList[position].getEntryMap().remove(status.platform)
notifyItemChanged(position)
}
}
}
}
override fun getEventByPosition(pos: Int): ExposureEvent? {
return mExposureEventSparseArray.get(pos)
}
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
return null
}
}

View File

@ -0,0 +1,198 @@
package com.gh.gamecenter.category
import android.os.Bundle
import android.view.View
import androidx.recyclerview.widget.LinearLayoutManager
import com.ethanhua.skeleton.Skeleton
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DialogUtils
import com.gh.common.view.ConfigFilterView
import com.gh.common.xapk.XapkInstaller
import com.gh.common.xapk.XapkUnzipStatus
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.tryCatchInRelease
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.databinding.FragmentTagsBinding
import com.gh.gamecenter.entity.CategoryEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class NewCategoryListFragment : ListFragment<GameEntity, NewCategoryListViewModel>() {
private var mPrimeCategory: CategoryEntity? = null
private var mSubCategoryList = arrayListOf<CategoryEntity>()
private val mBinding by lazy { FragmentTagsBinding.inflate(layoutInflater) }
private var mAdapter: NewCategoryListAdapter? = null
private val mDataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
showUnzipFailureDialog(downloadEntity)
}
}
override fun onDataInit(downloadEntity: DownloadEntity) {
mAdapter?.notifyItemByDownload(downloadEntity)
}
}
private lateinit var mExposureListener: ExposureListener
private lateinit var mViewModel: NewCategoryListViewModel
override fun onCreate(savedInstanceState: Bundle?) {
mViewModel = provideListViewModel()
mViewModel.title = arguments?.getString(EntranceConsts.KEY_NAME) ?: ""
mViewModel.categoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
mPrimeCategory = arguments?.getParcelable(EntranceConsts.KEY_DATA)
mSubCategoryList = mPrimeCategory?.data as? ArrayList<CategoryEntity> ?: arrayListOf()
val initSelectedCategory = arguments?.getString(EntranceConsts.KEY_CATEGORY_INIT_TITLE)
mViewModel.selectedCategory =
mSubCategoryList.find { categoryEntity -> categoryEntity.name == initSelectedCategory }
?: CategoryEntity()
super.onCreate(savedInstanceState)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
setNavigationTitle(mViewModel.title)
mViewModel.refresh.observeNonNull(this) { onRefresh() }
mBinding.configContainer.visibility = View.VISIBLE
mBinding.configContainer.updateAllTextView(mViewModel.sortType)
mBinding.configContainer.setOnConfigSetupListener(object : ConfigFilterView.OnConfigFilterSetupListener {
override fun onShowSortSize() {}
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
mViewModel.updateSortConfig(sortSize = sortSize)
}
override fun onSetupSortType(sortType: ConfigFilterView.SortType) {
mViewModel.updateSortConfig(sortType = sortType)
}
})
mExposureListener = ExposureListener(this, mAdapter!!)
mListRv.addOnScrollListener(mExposureListener)
updateCategoriesView(mSubCategoryList)
mSkeletonScreen =
Skeleton.bind(mBinding.listSkeleton).shimmer(false).load(R.layout.fragment_subject_skeleton).show()
}
override fun onResume() {
super.onResume()
DownloadManager.getInstance().addObserver(mDataWatcher)
}
override fun onPause() {
super.onPause()
DownloadManager.getInstance().removeObserver(mDataWatcher)
}
override fun getItemDecoration() = null
override fun getLayoutId() = 0
override fun getInflatedLayout() = mBinding.root
override fun provideListViewModel(): NewCategoryListViewModel {
return viewModelProvider()
}
override fun provideListAdapter(): ListAdapter<GameEntity> {
return mAdapter
?: NewCategoryListAdapter(requireContext(), mViewModel, mEntrance).apply { mAdapter = this }
}
private fun updateCategoriesView(categoryList: ArrayList<CategoryEntity>) {
mBinding.tagsRecyclerView.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
mBinding.tagsRecyclerView.adapter = NewCategoryHorizontalAdapter(requireContext(), mViewModel, categoryList) {
mBinding.tagsRecyclerView.smoothScrollToPosition(it)
}
// 调整选中标签位置
for (index in 0 until categoryList.size) {
if (categoryList[index].name == mViewModel.selectedCategory.name) {
mBinding.tagsRecyclerView.postDelayed({
tryCatchInRelease { mBinding.tagsRecyclerView.smoothScrollToPosition(index) }
}, 200)
break
}
}
}
override fun onRefresh() {
mAdapter?.clearPositionAndPackageMap()
super.onRefresh()
}
// 下载被删除事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBDownloadStatus) {
if ("delete" == status.status) {
mAdapter?.notifyItemAndRemoveDownload(status)
}
}
// 安装/卸载 事件
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(busFour: EBPackage) {
if (busFour.isInstalledOrUninstalled()) {
mAdapter?.notifyDataSetChanged()
}
}
fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
val data = mAdapter?.positionAndPackageMap ?: return
for (gameAndPosition in data) {
if (gameAndPosition.key.contains(downloadEntity.packageName)) {
val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
if (targetView != null) {
DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
return
}
}
}
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mBinding.run {
divider.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
tagsRecyclerView.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
tagsRecyclerView.adapter?.run {
notifyItemRangeChanged(0, itemCount)
}
configContainer.run {
container.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
updateAllTextView(mViewModel.sortType)
updatePopupWindow()
}
}
}
}

View File

@ -0,0 +1,81 @@
package com.gh.gamecenter.category
import android.app.Application
import androidx.lifecycle.MutableLiveData
import com.gh.common.exposure.ExposureUtils
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.common.view.ConfigFilterView
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.entity.CategoryEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.entity.SubjectSettingEntity
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
import io.reactivex.Single
class NewCategoryListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
var title = "" // 显示在 Toolbar 的标题
var categoryTitle = "" // 跳转进来的分类的标题
val refresh = MutableLiveData<Boolean>()
var selectedCategory = CategoryEntity()
var sortType = ConfigFilterView.SortType.RECOMMENDED
private var mSortSize = SubjectSettingEntity.Size()
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? {
return null
}
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
return RetrofitManager.getInstance()
.api
.getGamesInCategory(selectedCategory.id, getSortType(), getSortSize(), page)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) {
ExposureUtils.updateExposureInfo(it)
mResultLiveData.postValue(it)
}
}
fun changeSelectedCategory(category: CategoryEntity) {
if (selectedCategory != category) {
selectedCategory = category
refresh.postValue(true)
}
}
fun updateSortConfig(
sortSize: SubjectSettingEntity.Size? = null,
sortType: ConfigFilterView.SortType? = null
) {
if (sortSize != null && sortSize != mSortSize) {
mSortSize = sortSize
refresh.postValue(true)
} else if (sortType != null && sortType != this.sortType) {
this.sortType = sortType
refresh.postValue(true)
}
}
private fun getSortSize(): String? {
return UrlFilterUtils.getFilterQuery(
"min_size", mSortSize.min.toString(),
"max_size", mSortSize.max.toString()
)
}
fun getSortType(): String? {
return if (sortType == ConfigFilterView.SortType.RECOMMENDED) {
"download:-1"
} else {
"publish:-1"
}
}
}

View File

@ -160,7 +160,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
val ownerAd = downloadManagerAd?.ownerAd
val showOnFailed = downloadManagerAd?.displayRule?.onFailedAction == "show"
if ((showThirdPartyAd && thirdPartyAd != null) || (!showThirdPartyAd && thirdPartyAd != null && ownerAd == null)) {
initThirdPartyAd(downloadManagerAd, thirdPartyAd) { isSuccess ->
initThirdPartyAd(thirdPartyAd) { isSuccess ->
mBinding.maskView.goneIf(!isSuccess)
if (!isSuccess && ownerAd != null && showOnFailed) {
mSlideInterval = ownerAd.adSource?.sliderInterval ?: -1
@ -182,34 +182,12 @@ class DownloadFragment : BaseFragment_TabLayout() {
}
}
private fun initThirdPartyAd(adConfig: AdConfig?, thirdPartyAd: AdConfig.ThirdPartyAd, callback: (isSuccess: Boolean) -> Unit) {
val onAdShowAction = {
SensorsBridge.trackEvent("ThirdPartyAdShow",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", adConfig?.typeChinese ?: "",
"ad_placement", "下载管理",
"ad_space_id", adConfig?.id ?: "",
"ad_space_name", adConfig?.name ?: ""
)
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
"ad_source", thirdPartyAd.sourceName,
"ad_id", thirdPartyAd.slotId,
"ad_format", adConfig?.typeChinese ?: "",
"ad_placement", "下载管理",
"ad_space_id", adConfig?.id ?: "",
"ad_space_name", adConfig?.name ?: ""
)
}
private fun initThirdPartyAd(thirdPartyAd: AdConfig.ThirdPartyAd, callback: (isSuccess: Boolean) -> Unit) {
AdDelegateHelper.requestThirdPartyBannerAd(
this,
mBinding.adContainer,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(requireActivity()),
onAdShowAction,
onAdClickAction,
callback
)
}
@ -248,7 +226,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
if (it.isNullOrEmpty() && adConfig.displayRule.adSource == AdDelegateHelper.AD_TYPE_OWNER &&
adConfig.displayRule.onFailedAction == "show" && adConfig.thirdPartyAd != null) {
// 自有广告游戏为空时,显示第三方广告
initThirdPartyAd(adConfig, adConfig.thirdPartyAd) { isSuccess ->
initThirdPartyAd(adConfig.thirdPartyAd) { isSuccess ->
mBinding.maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME, System.currentTimeMillis())

View File

@ -6,7 +6,7 @@ class AdConfig(
@SerializedName("_id")
val id: String = "",
val name: String,
val location: String, // 广告插入位置。光环启动halo_launch 下载管理download_manager 游戏搜索game_search 畅玩启动helper_launch
val location: String, // 广告插入位置。光环启动halo_launch 下载管理download_manager 游戏搜索game_search 助手启动helper_launch
val type: String, // 广告位类型。开屏广告launch 信息流广告native banner 广告banner 插屏广告interstitial
val position: Int, // 定位,不存在的时候返回:-1
@SerializedName("display_rules")
@ -18,19 +18,12 @@ class AdConfig(
@SerializedName("owner_ads")
val ownerAd: OwnerAdEntity? = null,
) {
companion object {
const val TYPE_LAUNCH = "launch"
const val TYPE_NATIVE = "native"
const val TYPE_BANNER = "banner"
const val TYPE_INTERSTITIAL = "interstitial"
}
val typeChinese
get() = when (type) {
TYPE_LAUNCH -> "开屏"
TYPE_NATIVE -> "信息流"
TYPE_BANNER -> "banner"
TYPE_INTERSTITIAL -> "插屏"
"launch" -> "开屏"
"native" -> "信息流"
"banner" -> "banner"
"interstitial" -> "插屏"
else -> ""
}

View File

@ -26,8 +26,7 @@ data class BottomTab(
@SerializedName("is_default_page")
var default: Boolean = false, // 是否为默认显示页
var guide: Guide? = null, // 引导文案
var diverter: DiverterEntity? = null, // 分流器
var isTransparentStyle: Boolean = false, // 本地字段透明底部Tab
var isTransparentStyle: Boolean = false // 本地字段透明底部Tab
) : Parcelable {
@Parcelize
data class SearchStyle(

View File

@ -6,6 +6,40 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class CatalogEntity(
@SerializedName("_id")
var id: String = "",
var name: String = "",
var switch: CatalogSwitch = CatalogSwitch(),
@SerializedName("has_special")
var hasSpecial: Boolean = false,
@SerializedName("sub_catalogs")
var subCatalog: List<SubCatalogEntity> = emptyList()
) : Parcelable {
@Parcelize
data class SubCatalogEntity(
@SerializedName("_id")
var id: String = "",
var name: String = "",
var icon: String = "",
var type: String = "",
var link: LinkEntity = LinkEntity(),
var recommended: Boolean = false
) : Parcelable
@Parcelize
data class CatalogSwitch(
@SerializedName("sort_hot")
var hotSort: String = "",
@SerializedName("sort_new")
var newSort: String = "",
@SerializedName("sort_star")
var starSort: String = ""
) : Parcelable
}
@Parcelize
class SpecialCatalogEntity(

View File

@ -1,31 +0,0 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterData(
@SerializedName("diverter_id")
val diverterId: String = "",
@SerializedName("diverter_name")
val diverterName: String = "",
@SerializedName("branch_id")
val branchId: String = "",
@SerializedName("branch_name")
val branchName: String = "",
@SerializedName("branch_type")
val branchType: String = "",
@SerializedName("inprocess_time")
val inprocessTime: Int = 0,
@SerializedName("bypass_visit_time")
val bypassVisitTime: Int = 0,
@SerializedName("link_id")
val linkId: String = "",
@SerializedName("link_type")
val linkType: String = "",
@SerializedName("link_text")
val linkText: String = "",
@SerializedName("bypass_status")
val bypassStatus: Int = 0,
): Parcelable

View File

@ -1,15 +0,0 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
class DiverterEntity(
@SerializedName("module_id")
val moduleId: String = "",
@SerializedName("module_index")
val moduleIndex: Int = -1,
@SerializedName("diverter_data")
val diverterData: DiverterData = DiverterData()
): Parcelable

View File

@ -1,4 +1,4 @@
package com.gh.gamecenter.common.entity
package com.gh.gamecenter.entity
import android.net.Uri
import android.os.Parcelable

View File

@ -73,6 +73,8 @@ data class SubjectRecommendEntity(
"column" -> "专题"
"column_collection" -> "专题合集"
"block" -> "版块"
"category" -> "分类"
"catalog" -> "新分类"
"category_v2" -> "新分类2.0"
"column_test" -> "开测表"
"server" -> "开服表"

View File

@ -23,9 +23,6 @@ import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.value.LottieValueCallback
import com.gh.common.browse.BrowseTimer
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.prioritychain.CommunityHomeGuideHandler
import com.gh.common.prioritychain.PriorityChain
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.NewLogUtils
@ -45,6 +42,7 @@ import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.databinding.FragmentCommunityHomeBinding
import com.gh.gamecenter.databinding.LayoutCommunityHomeVideoGuideBinding
import com.gh.gamecenter.databinding.TabItemCommunityBinding
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.eventbus.EBTypeChange
@ -83,8 +81,6 @@ class CommunityHomeFragment : LazyFragment() {
private var mNavigationBitmap: Bitmap? = null
private var mShowVideo = true
private var mBottomTabId = ""
private val mPriorityChain by lazy { PriorityChain() }
private var mSuperiorChain: ISuperiorChain? = null
private val browseTimer = BrowseTimer()
.withResult {
@ -160,12 +156,6 @@ class CommunityHomeFragment : LazyFragment() {
})
}
private fun addHomeVideoGuideHandler() {
val decorView = activity?.window?.decorView as? FrameLayout
val communityHomeGuideHandler = CommunityHomeGuideHandler(21, requireContext(), decorView, mBinding?.videoLottie)
mPriorityChain.addHandler(communityHomeGuideHandler)
}
override fun initRealView() {
super.initRealView()
@ -176,7 +166,24 @@ class CommunityHomeFragment : LazyFragment() {
mMainWrapperViewModel?.bottomTabListLiveData?.observe(this) { tabList ->
mBinding?.videoAndSearchContainer?.goneIf(!mShowVideo) {
val decorView = requireActivity().window.decorView as? FrameLayout
addHomeVideoGuideHandler()
if (SPUtils.getBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, true)) {
val guideLayoutBinding = LayoutCommunityHomeVideoGuideBinding.inflate(
LayoutInflater.from(requireContext()),
decorView,
true
)
guideLayoutBinding.root.setOnClickListener { view ->
decorView?.removeView(view)
SPUtils.setBoolean(Constants.SP_SHOW_COMMUNITY_HOME_VIDEO_GUIDE, false)
mBinding?.videoLottie?.playAnimation()
SPUtils.setLong(
Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME,
System.currentTimeMillis()
)
}
}
// 每日首次进入社区tab视频lottie播放3次
val lastPlayTime = SPUtils.getLong(Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME, 0L) / 1000
@ -278,14 +285,6 @@ class CommunityHomeFragment : LazyFragment() {
DisplayUtils.transparentStatusBar(requireActivity())
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
NewLogUtils.logCommunityHomeEvent("view_community")
mSuperiorChain?.registerInferiorChain(mPriorityChain)
}
override fun onFragmentPause() {
super.onFragmentPause()
mSuperiorChain?.unregisterInferiorChain(mPriorityChain)
}
fun setCurrentItem(index: Int) {
@ -837,11 +836,6 @@ class CommunityHomeFragment : LazyFragment() {
fun getTopBgView() = mBinding?.topBg
fun setSuperiorChain(superiorChain: ISuperiorChain?): CommunityHomeFragment {
this.mSuperiorChain = superiorChain
return this
}
companion object {
var TAB_SELECTED_COLOR: Int = R.color.text_primary
var TAB_DEFAULT_COLOR: Int = R.color.community_forum_more

View File

@ -129,40 +129,39 @@ class ForumOrUserSearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
val transaction = when (type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
onFragmentCreate = { ForumOrUserSearchDefaultFragment() }
) { fragment ->
fragment.arguments = intent.extras
}.apply {
val key = searchEt.text.toString().trim { it <= ' ' }
if (key.isNotEmpty()) {
val stayTime = (System.currentTimeMillis() - startPageTime) / 1000
if (mEntrance != ForumDetailFragment.ENTRANCE) {
NewFlatLogUtils.logViewSearchList(stayTime, key)
} else {
NewFlatLogUtils.logViewBbsSearchList(stayTime, key, mBbsId)
}
val transaction = supportFragmentManager.beginTransaction()
when (type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: ForumOrUserSearchDefaultFragment()
fragment.arguments = intent.extras
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
val key = searchEt.text.toString().trim { it <= ' ' }
if (key.isNotEmpty()) {
val stayTime = (System.currentTimeMillis() - startPageTime) / 1000
if (mEntrance != ForumDetailFragment.ENTRANCE) {
NewFlatLogUtils.logViewSearchList(stayTime, key)
} else {
NewFlatLogUtils.logViewBbsSearchList(stayTime, key, mBbsId)
}
}
}
else -> if (mEntrance != ForumDetailFragment.ENTRANCE) {
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumOrUserSearchFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
}
else -> {
if (mEntrance != ForumDetailFragment.ENTRANCE) {
val fragment =
supportFragmentManager.findFragmentByTag(ForumOrUserSearchFragment::class.java.name) as? ForumOrUserSearchFragment
?: ForumOrUserSearchFragment()
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, fragment, ForumOrUserSearchFragment::class.java.name)
} else {
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumContentSearchListFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
fragment.arguments = intent.extras
}
}.apply {
val fragment =
supportFragmentManager.findFragmentByTag(ForumContentSearchListFragment::class.java.name) as? ForumContentSearchListFragment
?: ForumContentSearchListFragment()
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
fragment.arguments = intent.extras
transaction.replace(R.id.search_result, fragment, ForumContentSearchListFragment::class.java.name)
}
startPageTime = System.currentTimeMillis()
}
}

View File

@ -42,14 +42,14 @@ class ForumOrUserSearchDefaultFragment : SearchDefaultFragment() {
override fun initView() {
mBinding = FragmentSearchDefaultBinding.bind(mCachedView)
mBinding.searchDiscoveryTagHeadContainer.root.visibility = View.GONE
mBinding.searchDiscoveryTagFlexContainer.visibility = View.GONE
mBinding.hotTagHeadContainer.root.visibility = View.GONE
mBinding.hotTagFlexContainer.visibility = View.GONE
if (mEntrance == "论坛首页" || mEntrance == "搜索栏") {
mBinding.searchDiscoveryHeadContainer.headTitle.text = "热门论坛"
mBinding.hotHeadContainer.headTitle.text = "热门论坛"
mViewModel.getForumSearchHotContent()
} else {
mBinding.searchDiscoveryHeadContainer.root.visibility = View.GONE
mBinding.searchDiscoveryList.visibility = View.GONE
mBinding.hotHeadContainer.root.visibility = View.GONE
mBinding.hotList.visibility = View.GONE
}
val params = mBinding.historyHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams
params.topMargin = 0.5f.dip2px()

View File

@ -11,6 +11,8 @@ import androidx.fragment.app.Fragment
import androidx.viewpager.widget.ViewPager
import com.gh.gamecenter.R
import com.gh.gamecenter.amway.AmwayFragment
import com.gh.gamecenter.catalog.CatalogFragment
import com.gh.gamecenter.category.CategoryDirectoryFragment
import com.gh.gamecenter.category2.CategoryV2Fragment
import com.gh.gamecenter.common.base.adapter.FragmentAdapter
import com.gh.gamecenter.common.constant.EntranceConsts
@ -157,6 +159,16 @@ abstract class HomeTabWrapperFragment : SearchToolWrapperFragment() {
putString(GameServersTestFragment.TEST_COLUMN_ID, tabEntity.link)
})
"category" -> CategoryDirectoryFragment().with(Bundle().apply {
putString(EntranceConsts.KEY_CATEGORY_ID, tabEntity.link)
putString(EntranceConsts.KEY_CATEGORY_TITLE, tabEntity.text)
})
"catalog" -> CatalogFragment().with(Bundle().apply {
putString(EntranceConsts.KEY_CATALOG_ID, tabEntity.link)
putString(EntranceConsts.KEY_CATALOG_TITLE, tabEntity.text)
})
"category_v2" -> CategoryV2Fragment().with(Bundle().apply {
putString(EntranceConsts.KEY_CATEGORY_ID, tabEntity.link)
putString(EntranceConsts.KEY_CATEGORY_TITLE, tabEntity.text)

View File

@ -23,7 +23,7 @@ class ReloadFragment: BaseLazyFragment() {
super.onCreate(savedInstanceState)
mBinding.reuseLoading.root.visibility = View.VISIBLE
mBinding.reuseNoConnection.connectionReloadTv.setOnClickListener {
MainWrapperRepository.getInstance().init()
MainWrapperRepository.getInstance().getDataUnion()
mBinding.reuseNoConnection.root.visibility = View.GONE
mBinding.reuseLoading.root.visibility = View.VISIBLE
}

View File

@ -26,6 +26,7 @@ import com.gh.gamecenter.adapter.viewholder.GameHeadViewHolder
import com.gh.gamecenter.adapter.viewholder.GameImageViewHolder
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.adapter.viewholder.GameViewPagerViewHolder
import com.gh.gamecenter.category.CategoryDirectoryActivity
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.callback.OnViewClickListener
import com.gh.gamecenter.common.constant.EntranceConsts
@ -979,6 +980,14 @@ class GameFragmentAdapter(
)
)
"category" -> mContext.startActivity(
CategoryDirectoryActivity.getIntent(
mContext,
entity.link!!,
entity.text!!
)
)
"column" -> {
SubjectActivity.startSubjectActivity(
mContext,

View File

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

View File

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

View File

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

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter.game.upload
import android.app.Dialog
import android.content.Intent
import android.os.Bundle
import android.provider.MediaStore
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.text.TextPaint
@ -28,21 +29,18 @@ import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.callback.OnListClickListener
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.choosepic.ChoosePicAdapter
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SpanBuilder
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.FragmentGameUploadBinding
import com.gh.gamecenter.feature.entity.InstallGameEntity
import com.gh.gamecenter.feature.game.SelectGameAdapter
import com.gh.gamecenter.feature.selector.ChooseType
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
import okhttp3.MediaType
import okhttp3.RequestBody
@ -88,6 +86,7 @@ class GameUploadFragment : ToolbarFragment() {
mViewModel.upLoadSuccess.observe(viewLifecycleOwner, Observer {
if (it) {
ToastUtils.showToast("上传成功")
MtaHelper.onEvent("游戏上传", "游戏上传", "上传成功")
mUploadDialog.dismiss()
requireActivity().finish()
} else {
@ -120,11 +119,17 @@ class GameUploadFragment : ToolbarFragment() {
5,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
MtaHelper.onEvent("游戏上传", "游戏图片", "添加图片")
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, MEDIA_STORE_REQUEST)
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图片",
"删除图片"
)
}
mAdapter?.setPicItem(R.layout.game_upload_pic_item)
mAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -142,13 +147,19 @@ class GameUploadFragment : ToolbarFragment() {
1,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
MtaHelper.onEvent("游戏上传", "游戏图标", "添加图片")
PermissionHelper.checkStoragePermissionBeforeAction(requireActivity()) {
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, MEDIA_ICON_STORE_REQUEST)
}
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图标",
"删除图片"
)
}
mIconAdapter?.setPicItem(R.layout.game_upload_pic_item)
mIconAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -183,15 +194,18 @@ class GameUploadFragment : ToolbarFragment() {
private fun initListener() {
mBinding.chooseGameLl.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "点我选择")
showSelectDialog()
}
mBinding.gameIsNetworkingRg.setOnCheckedChangeListener { _, checkedId ->
when (checkedId) {
R.id.gameNetworkingRb -> {
mIsOnline = "yes"
MtaHelper.onEvent("游戏上传", "是否联网", "需要联网")
}
R.id.gameNoNetworkingRb -> {
mIsOnline = "no"
MtaHelper.onEvent("游戏上传", "是否联网", "无需联网")
}
}
}
@ -199,12 +213,15 @@ class GameUploadFragment : ToolbarFragment() {
when (checkedId) {
R.id.gameLanguageChineseRb -> {
mGameLanguage = "中文"
MtaHelper.onEvent("游戏上传", "游戏语言", "中文")
}
R.id.gameLanguageEnglishRb -> {
mGameLanguage = "英文"
MtaHelper.onEvent("游戏上传", "游戏语言", "英文")
}
R.id.gameLanguageOtherRb -> {
mGameLanguage = "其他"
MtaHelper.onEvent("游戏上传", "游戏语言", "其他")
}
}
}
@ -212,17 +229,21 @@ class GameUploadFragment : ToolbarFragment() {
when (checkedId) {
R.id.gameTypeLocalRb -> {
mGameType = "local"
MtaHelper.onEvent("游戏上传", "游戏类型", "单机")
}
R.id.gameTypeOnlineRb -> {
mGameType = "online"
MtaHelper.onEvent("游戏上传", "游戏类型", "网游")
}
R.id.gameTypeOtherRb -> {
mGameType = "other"
MtaHelper.onEvent("游戏上传", "游戏类型", "其他")
}
}
}
mBinding.addGameLabeTv.setOnClickListener {
MtaHelper.onEvent("游戏上传", "游戏标签", "添加标签")
if (mTags.size < mMaxTagSize) {
showAddTagDialog()
} else {
@ -235,6 +256,7 @@ class GameUploadFragment : ToolbarFragment() {
}
private fun commit() {
MtaHelper.onEvent("游戏上传", "提交", "提交")
if (TextUtils.isEmpty(mBinding.tvChoose.text.toString())) {
ToastUtils.showToast("请先选择游戏安装包")
return
@ -449,9 +471,11 @@ class GameUploadFragment : ToolbarFragment() {
}
back.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "关闭")
mSelectGameDialog?.cancel()
}
manualBtn.setOnClickListener {
MtaHelper.onEvent("游戏上传", "安装包", "从设备上选择")
val intent = CleanApkActivity.getIntent(requireContext(), true)
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(requireActivity()) {
startActivityForResult(intent, CHOOSE_LOCAL_APK)
@ -475,20 +499,32 @@ class GameUploadFragment : ToolbarFragment() {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == MEDIA_STORE_REQUEST || requestCode == MEDIA_ICON_STORE_REQUEST) {
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
val selectedImage = data.data ?: return
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
Utils.log("picturePath = $picturePath")
val cursor = requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null)
?: return
cursor.moveToFirst()
val file = File(picturePath)
if (file.length() > 5 * 1024 * 1024) {
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
} else {
if (requestCode == MEDIA_STORE_REQUEST) {
mAdapter!!.addFileList(picturePath)
try {
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val picturePath = cursor.getString(columnIndex)
cursor.close()
Utils.log("picturePath = $picturePath")
val file = File(picturePath)
if (file.length() > 5 * 1024 * 1024) {
ToastUtils.showToast(getString(R.string.pic_max_hint, 5))
} else {
mIconAdapter!!.addFileList(picturePath)
if (requestCode == MEDIA_STORE_REQUEST) {
mAdapter!!.addFileList(picturePath)
} else {
mIconAdapter!!.addFileList(picturePath)
}
}
} catch (e: Exception) {
ToastUtils.showToast(e.message ?: "")
}
} else if (requestCode == CHOOSE_LOCAL_APK) {
val packageName = data.getStringExtra(EntranceConsts.KEY_PACKAGENAME) ?: ""
@ -574,6 +610,7 @@ class GameUploadFragment : ToolbarFragment() {
labelTv.text = tag
labelView.findViewById<View>(R.id.picDelIv).setOnClickListener {
if (mTags.contains(tag)) {
MtaHelper.onEvent("游戏上传", "游戏标签", "删除标签")
mBinding.gameLabelFl.removeView(labelView)
mTags.remove(tag)
mBinding.gameLabelFl.goneIf(mTags.isEmpty())

View File

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

View File

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

View File

@ -16,13 +16,12 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.setRootBackgroundDrawable
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.databinding.DialogChooseGameCollectionCoverTypeBinding
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity.Companion.REQUEST_CODE_IMAGE
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
@ -43,7 +42,7 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
ChooseType.IMAGE,
LocalMediaActivity.ChooseType.IMAGE,
1,
"创建游戏单"
), REQUEST_CODE_IMAGE

View File

@ -285,7 +285,7 @@ class GameCollectionSquareAdapter(
}
})
addOnScrollListener(ScrollEventListener(this).apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
binding.bannerIndicator.onPageScrolled(

View File

@ -410,7 +410,7 @@ class GameCollectionSquareFragment : LazyListFragment<GamesCollectionEntity, Gam
}
})
addOnScrollListener(ScrollEventListener(this).apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
mDefaultBinding.headerContainer.bannerIndicator.onPageScrolled(

View File

@ -63,10 +63,7 @@ import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.RecommendPopupEntity
import com.gh.gamecenter.eventbus.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.PluginLocation
import com.gh.gamecenter.feature.entity.SimpleGame
import com.gh.gamecenter.feature.entity.TagStyleEntity
import com.gh.gamecenter.feature.entity.*
import com.gh.gamecenter.feature.eventbus.EBConcernChanged
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.utils.ApkActiveUtils
@ -74,7 +71,11 @@ import com.gh.gamecenter.feature.utils.SentryHelper
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.gamedetail.cloudarchive.CloudArchiveFragment
import com.gh.gamecenter.gamedetail.desc.DescFragment
import com.gh.gamecenter.gamedetail.dialog.*
import com.gh.gamecenter.gamedetail.dialog.GameBigEventDialog
import com.gh.gamecenter.gamedetail.dialog.GameDetailMoreDialog
import com.gh.gamecenter.gamedetail.dialog.GameTagsDialog
import com.gh.gamecenter.gamedetail.dialog.SpecialDownloadDialogFragment
import com.gh.gamecenter.gamedetail.dialog.SpecialDownloadVisibilityViewModel
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity
import com.gh.gamecenter.gamedetail.entity.Video
@ -148,6 +149,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
private var mShowConcernOnMenu = false
private var mSpecialDownloadDetailFragmentIsShowing = false
private val mFragmentsList = ArrayList<Fragment>()
private val mTabTitleList = ArrayList<String>()
private val mTabTypeList = ArrayList<String>() // tab 类型的列表,用于确定某个类型的 tab 在第几个位置
@ -164,7 +167,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|| downloadEntity.status == DownloadStatus.redirected
) {
// 特殊下载弹窗
if (isSpecialDownloadDialogAvailable(downloadEntity) && !isSpecialDownloadDetailFragmentIsShowing()) {
if (isSpecialDownloadDialogAvailable() && !mSpecialDownloadDetailFragmentIsShowing) {
updateSpecialDownloadDialogIcon(true)
if (downloadEntity.status == DownloadStatus.add) {
@ -865,10 +868,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
showConcernIconAtBottomBarIfAvailable()
val downloadEntitySnapshot = DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity)
if (isSpecialDownloadDialogAvailable(downloadEntitySnapshot)
&& downloadEntitySnapshot != null) {
if (isSpecialDownloadDialogAvailable()
&& DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity) != null) {
updateSpecialDownloadDialogIcon(true)
}
@ -1179,11 +1180,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
}
private fun doShowAlertDialog(dialog: GameEntity.Dialog) {
SensorsBridge.trackEvent("GameDetailDialogShow",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: ""
)
DialogHelper.showDialogWithHtmlContent(
requireContext(),
dialog.title,
@ -1191,33 +1187,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
dialog.confirmButton.text.toString(),
dialog.closeButtonText,
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"link_type", dialog.confirmButton.type ?: "",
"link_id", dialog.confirmButton.link ?: "",
"link_text", dialog.confirmButton.linkText ?: "",
"button_name", dialog.confirmButton.text.toString()
)
dialog.confirmButton.text = dialog.confirmButton.linkText
DirectUtils.directToLinkPage(requireContext(), dialog.confirmButton, mEntrance, "")
},
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"button_name", dialog.closeButtonText
)
},
{
SensorsBridge.trackEvent("GameDetailDialogClick",
"game_id", mGameEntity?.id ?: "",
"game_name", mGameEntity?.name ?: "",
"game_type", mGameEntity?.categoryChinese ?: "",
"button_name", "关闭弹窗"
)
}
)
}
@ -2591,15 +2562,12 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
* 2. 未获取到游戏详情数据
* 3. 当前游戏 APK 不为 1 个
* 4. 当前游戏类型不为畅玩
* 5. 当前游戏不是双下载时使用本地下载进行下载
*/
private fun isSpecialDownloadDialogAvailable(downloadEntity: DownloadEntity? = null): Boolean {
private fun isSpecialDownloadDialogAvailable(): Boolean {
if (Config.getNewApiSettingsEntity()?.install?.questionTip?.linkEntity == null) return false
if (mNewGameDetailEntity == null || mGameEntity == null) return false
if (mGameEntity?.getApk()?.size != 1) return false
if (downloadEntity?.asVGame() == true) return false
if (downloadEntity?.isSimulatorGame() == true) return false
if (downloadEntity?.isLocalDownloadInDualDownloadMode() == true) return false
if (GameUtils.shouldPerformAsVGame(mGameEntity!!)) return false
return true
}
@ -2617,6 +2585,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
if (fragment == null) {
if (!visibilityViewModel.specialDownloadVisibleLiveData.hasObservers()) {
visibilityViewModel.specialDownloadVisibleLiveData.observe(viewLifecycleOwner) {
mSpecialDownloadDetailFragmentIsShowing = it
updateSpecialDownloadDialogIcon(visible = !it)
}
}
@ -2632,10 +2601,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
}
}
private fun isSpecialDownloadDetailFragmentIsShowing(): Boolean {
return childFragmentManager.findFragmentByTag(TAG_SPECIAL_DOWNLOAD_DIALOG) != null
}
override fun scrollToTop() {
val fragment = mFragmentsList.safelyGetInRelease(mBodyBinding.gamedetailVp.currentItem)
if (fragment is IScrollable && fragment.isAdded) {

View File

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

View File

@ -146,7 +146,7 @@ class CustomFoldSlideLargeImageItemAdapter(
if (dataList.isEmpty()) {
0
} else {
Int.MAX_VALUE / 2 - (Int.MAX_VALUE / 2) % dataList.size
Int.MAX_VALUE / 2 - Int.MAX_VALUE % dataList.size
}
class ImageItemViewHolder(

View File

@ -140,7 +140,7 @@ class CommonContentHomeSLideListUi(
}
binding.recyclerView.addOnScrollListener(scrollEventListener.apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrollStateChanged(state: Int) {
super.onPageScrollStateChanged(state)

View File

@ -163,7 +163,7 @@ class CommonContentHomeSlideWithCardsUi(
}
})
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
_currentDataPosition = adapter.getDataPosition(position)
}

View File

@ -101,7 +101,7 @@ class NotificationColumnViewHolder(
private val scrollEventListener by lazy(LazyThreadSafetyMode.NONE) {
ScrollEventListener(binding.rvNotification).apply {
registerOnPageChangeCallback(object : OnPageChangeCallback() {
setOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
if (positionOffset > 0F) {
// position 代表的是正在移出屏幕的 itemView 的位置

View File

@ -132,7 +132,7 @@ class HomeSlideListViewHolder(
updateImmersiveColor(slideList[0].placeholderColor.hexStringToIntColor())
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
var lastStatePosition = -1
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE

View File

@ -153,7 +153,7 @@ class HomeSlideWithCardsViewHolder(
}
})
binding.recyclerView.addOnScrollListener(ScrollEventListener(binding.recyclerView).apply {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
var lastStatePosition = -1
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE

View File

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

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