Compare commits

..

52 Commits

Author SHA1 Message Date
bd02a0b4be refactor: update va commit id. 2024-09-09 18:06:09 +08:00
7a498763bb feat: 去掉Va保活通知栏,简单的为每一个游戏bindservice到va_core进程。 2024-09-09 18:01:35 +08:00
8b839a5b13 Merge branch 'feat/GHZSCY-5596' into 'dev'
feat: 广告位管理第三方广告相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-5596

See merge request halo/android/assistant-android!1884
2024-09-09 17:59:03 +08:00
7d2ed0eac0 feat: 广告位管理第三方广告相关优化—客户端 https://jira.shanqu.cc/browse/GHZSCY-5596 2024-09-09 17:59:03 +08:00
5c70bc2237 Merge branch 'fix/remove_diverter_param' into 'dev'
fix: 移除分流器无用的接口参数

See merge request halo/android/assistant-android!1883
2024-09-09 17:11:22 +08:00
03b36096b8 fix: 移除分流器无用的接口参数 2024-09-09 17:00:57 +08:00
4191dbc4f0 Merge branch 'feat/GHZSCY-5891' into 'dev'
feat: 新增页面分流器—客户端 https://jira.shanqu.cc/browse/GHZSCY-5891

See merge request halo/android/assistant-android!1876
2024-09-09 13:40:52 +08:00
cc20b6e38a Merge branch 'merge/GHZSCY-6494' into 'dev'
refactor: 敏感词文件,身份证区域文件,兼容Android Pie的apache http dex文件移到插件zip包...

See merge request halo/android/assistant-android!1881
2024-09-09 11:19:45 +08:00
3ad5890238 fix: 【光环助手】提示显示问题 https://jira.shanqu.cc/browse/GHZSCY-6632 2024-09-09 11:14:38 +08:00
6179d7055b feat: vasdk compileSdk version set to 34 2024-09-09 11:07:02 +08:00
f18391adc2 fix: 64位插件下载没有game_id的问题 https://jira.shanqu.cc/browse/GHZSCY-6494 2024-09-09 10:51:37 +08:00
3a2f15a436 refactor: 敏感词文件,身份证区域文件,兼容Android Pie的apache http dex文件移到插件zip包 https://jira.shanqu.cc/browse/GHZSCY-6494
d44e4c30
2024-09-09 10:51:37 +08:00
c0e8160955 refactor: 打包去掉插件下载到assets https://jira.shanqu.cc/browse/GHZSCY-6494 2024-09-09 10:50:46 +08:00
9038131c96 refactor: 版本号meta信息维护 2024-09-09 10:50:03 +08:00
4430ae1107 refactor: 整理va_version,根据product flavor来选择-debug后缀, 打包根据git tag 来选择打正式包还是测试包 2024-09-09 10:48:18 +08:00
d6aa2690b2 feat: 临时提交meta信息 2024-09-09 10:47:54 +08:00
e42fd24b71 feat: 插件下载上报埋点 2024-09-09 10:47:49 +08:00
452a1ede24 feat: 调整64位va插件下载时机 2024-09-09 10:46:57 +08:00
ceb227f645 feat: 新增页面分流器—客户端 https://jira.shanqu.cc/browse/GHZSCY-5891 2024-09-06 17:13:44 +08:00
479ea5778b Merge branch 'fix/GHZSCY-6589' into 'dev'
fix:【光环助手】游戏专题-游戏排序问题 https://jira.shanqu.cc/browse/GHZSCY-6589

See merge request halo/android/assistant-android!1879
2024-09-06 17:06:53 +08:00
8969d6fd5d fix:【光环助手】游戏专题-游戏排序问题 https://jira.shanqu.cc/browse/GHZSCY-6589 2024-09-06 16:41:27 +08:00
cc43f2e0fd Merge branch 'feat/GHZSCY-6492-select-only' into 'dev'
feat: 媒体文件上传控件优化(一) https://jira.shanqu.cc/browse/GHZSCY-6282

See merge request halo/android/assistant-android!1877
2024-09-06 15:40:18 +08:00
ab12491f2f feat: 媒体文件上传控件优化(一) https://jira.shanqu.cc/browse/GHZSCY-6282 2024-09-06 15:38:46 +08:00
ae24ab0c87 Merge branch 'fix/GHZSCY-6566' into 'dev'
fix: 批量删除畅玩游戏时会有部分游戏去到下载列表 https://jira.shanqu.cc/browse/GHZSCY-6566

See merge request halo/android/assistant-android!1868
2024-09-05 17:44:06 +08:00
383712900c Merge branch 'release' into 'dev'
merge release to dev

See merge request halo/android/assistant-android!1872
2024-09-04 11:48:42 +08:00
5308ccfabe Merge branch 'fix/GHZSCY-6615' into 'dev'
fix: 空间不足时,下载游戏提示网络不佳且重复请求 https://jira.shanqu.cc/browse/GHZSCY-6615

See merge request halo/android/assistant-android!1867
2024-09-02 15:56:16 +08:00
2ee49b9a20 Merge branch 'fix/GHZSCY-6628' into 'dev'
fix: 修复页面跳转闪退问题 https://jira.shanqu.cc/browse/GHZSCY-6628

See merge request halo/android/assistant-android!1869
2024-09-02 15:49:19 +08:00
3af3d413c0 fix: 修复页面跳转闪退问题 https://jira.shanqu.cc/browse/GHZSCY-6628
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 14:32:13 +08:00
9bf24977ca Merge branch 'release' into dev
# Conflicts:
#	dependencies.gradle
2024-09-02 13:35:01 +08:00
bf1dd958cd Merge branch 'fix/clean_apk_culprit' into 'dev'
fix: 修复清理安装包时会检索到插件 APK 的问题

See merge request halo/android/assistant-android!1865
2024-09-02 11:37:00 +08:00
30c34b799b fix: 修复清理安装包时会检索到插件 APK 的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 11:33:52 +08:00
b202b580bb fix: 空间不足时,下载游戏提示网络不佳且重复请求 https://jira.shanqu.cc/browse/GHZSCY-6615
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-09-02 10:48:14 +08:00
6d29da5172 Merge branch 'fix/jump_to_home_from_external_error' into 'dev'
fix: 从外部跳转光环首页时,SkipActivity未关闭的问题

See merge request halo/android/assistant-android!1863
2024-08-29 16:21:57 +08:00
8aeb0d6f09 fix: 从外部跳转光环首页时,SkipActivity未关闭的问题 2024-08-29 16:19:22 +08:00
4115383b68 Merge branch 'feat/GHZSCY-6517' into 'dev'
feat: 优化畅玩安装完成提示启动弹窗—客户端 https://jira.shanqu.cc/browse/GHZSCY-6517

See merge request halo/android/assistant-android!1845
2024-08-29 10:44:05 +08:00
9075bfa214 feat: 优化畅玩安装完成提示启动弹窗—客户端 https://jira.shanqu.cc/browse/GHZSCY-6517 2024-08-29 10:44:04 +08:00
3fa63e331b Merge branch 'feature-GHZS-5576' into 'dev'
feat: 搜索业务-热门榜单新增触发搜索的榜单类型—客户端 https://jira.shanqu.cc/browse/GHZSCY-5576

See merge request halo/android/assistant-android!1738
2024-08-29 10:43:47 +08:00
aef19fcd49 feat: 搜索业务-热门榜单新增触发搜索的榜单类型—客户端 https://jira.shanqu.cc/browse/GHZSCY-5576 2024-08-29 10:43:47 +08:00
15d7252974 Merge branch 'feature-GHZS-5572' into 'dev'
feat: 搜索业务-新增搜索发现取代热门标签—客户端 https://jira.shanqu.cc/browse/GHZSCY-5572

See merge request halo/android/assistant-android!1732
2024-08-29 10:33:27 +08:00
f6fa060f3a feat: 搜索业务-新增搜索发现取代热门标签—客户端 https://jira.shanqu.cc/browse/GHZSCY-5572 2024-08-29 10:33:27 +08:00
7cfe48b3c8 Merge branch 'feat/GHZSCY-6601' into 'dev'
feat: 游戏详情弹窗补充神策埋点—客户端 https://jira.shanqu.cc/browse/GHZSCY-6601

See merge request halo/android/assistant-android!1862
2024-08-29 10:11:43 +08:00
6bd3c1011d feat: 游戏搜索-排序专题 曝光补充游戏ID—客户端 https://jira.shanqu.cc/browse/GHZSCY-6602 2024-08-29 10:08:57 +08:00
3447ecffd8 feat: 游戏详情弹窗补充神策埋点—客户端 https://jira.shanqu.cc/browse/GHZSCY-6601 2024-08-29 10:08:57 +08:00
6b44140ff3 fix: 批量删除畅玩游戏时会有部分游戏去到下载列表 https://jira.shanqu.cc/browse/GHZSCY-6566
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-27 17:56:41 +08:00
78d26b4cc1 Merge branch 'feat/GHZSCY-6506' into 'dev'
feat: 消息推送:神策埋点新增用户属性极光推送注册id—客户端 https://jira.shanqu.cc/browse/GHZSCY-6506

See merge request halo/android/assistant-android!1861
2024-08-27 14:07:17 +08:00
5565539445 feat: 消息推送:神策埋点新增用户属性极光推送注册id—客户端 https://jira.shanqu.cc/browse/GHZSCY-6506 2024-08-27 14:07:16 +08:00
062f0149d0 Merge branch 'fix/start_activity_error' into 'dev'
fix: 修复页面重建时,部分跳转失效的问题

See merge request halo/android/assistant-android!1857
2024-08-23 16:41:38 +08:00
a504f00bac fix: 修复页面重建时,部分跳转失效的问题
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-23 16:25:24 +08:00
72460b88be Merge remote-tracking branch 'origin/release' into dev
# Conflicts:
#	dependencies.gradle
2024-08-23 11:38:24 +08:00
30135bdb8f Merge branch 'fix/va-install-sdcard' into 'dev'
fix: 本地测试手动安装游戏包安装到一体化畅玩

See merge request halo/android/assistant-android!1848
2024-08-20 16:14:50 +08:00
f8ac85d29c fix: 本地测试手动安装游戏包安装到一体化畅玩 2024-08-20 16:13:46 +08:00
39bba71e7a chore: 版本更新至 5.38.0
Signed-off-by: chenjuntao <chenjuntao@ghzhushou.com>
2024-08-13 13:44:56 +08:00
218 changed files with 5631 additions and 1999 deletions

View File

@ -72,7 +72,7 @@ android_build:
only:
- dev
- release
- feat/GHZSCY-6644
- feat/GHZSCY-6578
# 代码检查
sonarqube_analysis:
@ -158,4 +158,4 @@ oss-upload&send-email:
only:
- dev
- release
- feat/GHZSCY-6644
- feat/GHZSCY-6578

View File

@ -107,7 +107,6 @@ 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
@ -218,6 +217,9 @@ 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接口仅包含正式环境
@ -231,6 +233,9 @@ 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 {
@ -244,7 +249,10 @@ 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 {
@ -257,6 +265,9 @@ 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 {
@ -269,6 +280,9 @@ 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 {
@ -281,6 +295,9 @@ 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}")
}
// 港澳台
@ -371,7 +388,7 @@ dependencies {
implementation "com.lg:easyfloat:${easyFloat}"
implementation ("com.lg:apksig:${apksig}") {
implementation("com.lg:apksig:${apksig}") {
exclude group: 'com.google.protobuf'
}
@ -387,7 +404,7 @@ dependencies {
implementation project(':vspace-bridge:vspace')
implementation(project(':feature:xapk-installer'))
implementation (project(':module_common')) {
implementation(project(':module_common')) {
exclude group: 'androidx.swiperefreshlayout'
}
@ -462,9 +479,11 @@ 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,43 +32,11 @@ 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 {
VHelper.connectService {
context.startActivity(
InstallExternalGameActivity.getIntent(context)
.apply { flags = flags or Intent.FLAG_ACTIVITY_NEW_TASK })
}
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,7 +1,12 @@
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
@ -40,6 +45,19 @@ 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))
@ -56,11 +74,30 @@ 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 {
@ -94,9 +131,11 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
OnItemClickListener.ClickType.CLICK_INSTALL -> {
install(externalGameUiState)
}
OnItemClickListener.ClickType.CLICK_UNINSTALL -> {
uninstall(externalGameUiState)
}
OnItemClickListener.ClickType.CLICK_START -> {
start(externalGameUiState)
}
@ -104,9 +143,8 @@ 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
@ -114,20 +152,12 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
}
}, true)
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)
VHelper.newCwValidateVspaceBeforeAction(
requireContext(),null,
) {
dialog.show()
}
}
private fun uninstall(externalGameUiState: ExternalGameUiState) {
@ -158,6 +188,12 @@ 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,6 +2,7 @@
<resources>
<string name="title_install_external_game">從SD卡安裝</string>
<string name="text_install">安裝</string>
<string name="text_update">更新</string>
<string name="text_uninstall">卸載</string>
<string name="text_start">啟動</string>
</resources>

View File

@ -1,6 +1,4 @@
<?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,6 +185,8 @@
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
@ -454,9 +456,6 @@
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"
@ -574,10 +573,6 @@
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,18 +227,7 @@ object AdDelegateHelper {
/**
* 是否大于开屏广告展示间隔时长
*/
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
}
private fun isMatchStartUpAdDisplayRule(): Boolean = isMatchAdDisplayRule(mSplashAd, Constants.SP_LAST_SPLASH_AD_SHOW_TIME)
/**
* 是否大于广告管理展示间隔时长
@ -292,6 +281,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -313,6 +303,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -330,6 +321,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -353,6 +345,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -374,6 +367,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -403,13 +397,14 @@ object AdDelegateHelper {
sdkStartAdContainer.visibility = View.VISIBLE
requestCsjSplashAd(
activity,
thirdPartyAd.slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
sdkStartAdContainer,
sdkJumpBtn,
timeout,
isHotLaunch,
sdkSplashCallback
)
}
@ -420,27 +415,65 @@ 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,
) {
if (mCsjAdImpl == null) {
val thirdPartyAd = if (isHotLaunch) mSplashAd?.hotStartThirdPartyAd else mSplashAd?.thirdPartyAd
if (mCsjAdImpl == null || thirdPartyAd == 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,
slotId,
thirdPartyAd.slotId,
adViewWidthInPx,
adViewHeightInPx,
adViewWidthInDp,
adViewHeightInDp,
startAdContainer,
timeout,
onAdShowAction,
onAdClickAction,
callback,
)
}
@ -457,6 +490,7 @@ object AdDelegateHelper {
adViewHeightInDp: Float,
startAdContainer: ViewGroup,
sdkStartAdContainer: ViewGroup,
sdkJumpBtn: TextView,
adsViewGroup: FrameLayout,
handler: BaseActivity.BaseHandler,
isHotLaunch: Boolean,
@ -474,6 +508,7 @@ object AdDelegateHelper {
adViewHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsViewGroup,
handler,
isHotLaunch,
@ -621,9 +656,11 @@ object AdDelegateHelper {
slotId: String,
adContainerView: ViewGroup,
expressViewWidth: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit,
) {
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, callback)
mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, onAdShowAction, onAdClickAction, callback)
}
/**
@ -634,6 +671,8 @@ object AdDelegateHelper {
containerView: ViewGroup,
ad: AdConfig.ThirdPartyAd,
expressViewWidthInDp: Float,
onAdShowAction: () -> Unit,
onAdClickAction: () -> Unit,
callback: (isSuccess: Boolean) -> Unit
) {
@ -656,6 +695,28 @@ 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,42 +3,105 @@ 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) {
override fun requestAd(fragment: Fragment, container: ViewGroup, maskView: View, topViewStub: ViewStub, bottomViewStub: ViewStub, adClickAction: () -> Unit): View {
if (AdDelegateHelper.shouldShowHelperLaunchAd()) {
val launchAd = AdDelegateHelper.vGameLaunchAd
val showThirdPartyAd = launchAd?.displayRule?.adSource == AdDelegateHelper.AD_TYPE_SDK
val thirdPartyAd = launchAd?.thirdPartyAd
if (showThirdPartyAd && thirdPartyAd != null) {
AdDelegateHelper.requestThirdPartyBannerAd(
fragment,
container,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(fragment.requireActivity()),
) { isSuccess ->
maskView.goneIf(!isSuccess)
if (isSuccess) {
SPUtils.setLong(Constants.SP_LAST_HELPER_LAUNCH_AD_SHOW_TIME, System.currentTimeMillis())
}
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()
}
}
}
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

@ -21,6 +21,8 @@ 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
@ -30,6 +32,7 @@ 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
@ -502,7 +505,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> constructor(
startActivityForResult(
LocalMediaActivity.getIntent(
this@BaseRichEditorActivity,
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
maxChooseCount,
if (mtaEventName() == "提问帖") "发提问帖" else "发帖子"
), INSERT_MEDIA_VIDEO_CODE
@ -531,7 +534,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,
LocalMediaActivity.ChooseType.IMAGE,
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.entity.LocalVideoEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.entity.QuoteCountEntity
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.retrofit.RetrofitManager
@ -39,8 +39,6 @@ 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

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

View File

@ -0,0 +1,12 @@
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,6 +7,7 @@ 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;
@ -46,6 +47,7 @@ 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;
@ -78,10 +80,10 @@ public class Config {
private static NewApiSettingsEntity.NightMode mNightModeSetting;
private static SimulatorEntity mNewSimulatorEntity;
private static VSetting mVSetting;
private static VNewSetting mVNewSetting;
private volatile static VNewSetting mVNewSetting;
private static AppEntity mNew32UpdateEntity;
public static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static BehaviorSubject<VNewSetting> vNewSettingSubject = BehaviorSubject.create();
private static GameGuidePopupEntity mGameGuidePopupEntity;
private static SharedPreferences mDefaultSharedPreferences;
@ -212,6 +214,16 @@ 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((asVGame, isSubscribe) -> {
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
download(v.getContext(),
progressBar,
gameEntity,
@ -197,7 +197,7 @@ public class BindingAdapters {
builder.addHandler(new GamePermissionHandler());
builder.addHandler(new VersionNumberHandler());
builder.setProcessEndCallback((asVGame, isSubscribe) -> {
builder.setProcessEndCallback(gameEntity.getId(), (asVGame, isSubscribe) -> {
DownloadDialog.showDownloadDialog(
v.getContext(),
gameEntity,

View File

@ -0,0 +1,52 @@
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

@ -24,13 +24,13 @@ class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
override fun jumpActivityWithCallback(context: Context, bundle: Bundle, callback: () -> Unit) {
if (context is FragmentActivity && !context.supportFragmentManager.isDestroyed) {
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
EntranceUtils.jumpActivityCompat(context, bundle, null, object : Callback {
override fun onActivityResult(resultCode: Int, data: Intent?) {
callback()
}
})
} else {
EntranceUtils.jumpActivity(AppManager.getInstance().currentActivity(), bundle)
EntranceUtils.jumpActivityCompat(AppManager.getInstance().currentActivity(), bundle)
}
}

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.jumpActivity(context, nextToBundle, bundle, (resultCode, data) -> {
EntranceUtils.jumpActivityCompat(context, bundle, nextToBundle, (resultCode, data) -> {
if (isTriggerNextStep && listener != null && isLogin()) {
listener.onLogin();
}

View File

@ -910,7 +910,7 @@ object DownloadItemUtils {
addHandler(CheckStoragePermissionHandler())
addHandler(VersionNumberHandler())
}
.setProcessEndCallback { _, _ ->
.setProcessEndCallback(gameEntity.id) { _, _ ->
DownloadDialog.showDownloadDialog(view.context, gameEntity, traceEvent, entrance, location)
}
.buildHandlerChain()
@ -953,7 +953,7 @@ object DownloadItemUtils {
addHandler(LandPageAddressHandler())
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { 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 { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { 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 { asVGame, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { 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 { _, isSubscribe ->
.setProcessEndCallback(gameEntity.id) { _, isSubscribe ->
plugin(context, gameEntity, downloadBtn, entrance, location, isSubscribe as Boolean, traceEvent)
}
.buildHandlerChain()
@ -1089,7 +1089,7 @@ object DownloadItemUtils {
DownloadChainBuilder()
.apply {
addHandler(LandPageAddressHandler())
}.setProcessEndCallback { asVGame, _ ->
}.setProcessEndCallback(gameEntity.id) { asVGame, _ ->
DownloadDialogHelper.findAvailableDialogAndShow(context, gameEntity, apk) {
DialogUtils.checkDownload(
context,

View File

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

View File

@ -27,7 +27,6 @@ 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;
@ -38,7 +37,18 @@ 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) {
@ -48,59 +58,7 @@ public class EntranceUtils {
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 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 {
} else if (callback != null ) {
Intent intent1 = new Intent(context, clazz);
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
@ -112,7 +70,15 @@ public class EntranceUtils {
// 不要回调,正常跳转
context.startActivity(intent1);
}
} 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 {
// 应用未在运行

View File

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

View File

@ -30,6 +30,9 @@ 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)
@ -550,6 +553,8 @@ object NewFlatLogUtils {
// 搜索-点击搜索榜单内容
@JvmStatic
fun logSearchClickRankDetail(
key: String,
rankType: String,
rankName: String,
rankSequence: String,
linkId: String,
@ -558,6 +563,8 @@ 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
@ -2688,4 +2695,73 @@ 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

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

View File

@ -48,6 +48,7 @@ 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
@ -815,18 +816,18 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
if (mBigImageView == null) {
mBigImageView = imageView
}
imageView.setDragListener(object : DraggableBigImageView.DragListener {
override fun onRelease(draggableBigImageView: DraggableBigImageView, scale: Float) {
imageView.setDragListener(object : DragListener {
override fun onRelease(scale: Float) {
updateOriginPosition(mViewPager.currentItem)
performExitAnimation(draggableBigImageView, scale, isFadeOnly())
performExitAnimation(imageView, scale, isFadeOnly())
}
override fun onDrag(draggableBigImageView: DraggableBigImageView, fraction: Float) {
override fun onDrag(fraction: Float) {
mBackgroundView.alpha = 1 - fraction
mIndicatorMask.visibility = View.GONE
}
override fun onRestore(draggableBigImageView: DraggableBigImageView, fraction: Float) {
override fun onRestore(fraction: Float) {
mBackgroundView.alpha = 1F
// mIndicatorMask.goneIf(mUrlList?.size == 1)
if (mUrlList?.size != 1 || mAnswerEntity != null) {

View File

@ -138,7 +138,6 @@ 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;
@ -149,6 +148,8 @@ 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";
@ -490,6 +491,7 @@ 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) {
@ -518,6 +520,7 @@ public class MainActivity extends BaseActivity {
screenHeightInDp,
startAdContainer,
sdkStartAdContainer,
sdkJumpBtn,
adsFl,
(BaseHandler) mBaseHandler,
false,
@ -537,13 +540,19 @@ public class MainActivity extends BaseActivity {
@Override
protected void handleMessage(Message msg) {
super.handleMessage(msg);
if (msg.what == COUNTDOWN_AD) {
if (msg.what == COUNTDOWN_AD || msg.what == COUNTDOWN_SDK_AD) {
mCountdownCount++;
if (mCountdownMaxCount < mCountdownCount) {
int maxCount;
if (msg.what == COUNTDOWN_AD) {
maxCount = mCountdownMaxCount;
} else {
maxCount = COUNTDOWN_SDK_MAX_COUNT;
}
if (maxCount < mCountdownCount) {
AdDelegateHelper.INSTANCE.setShowingSplashAd(false);
hideSplashAd();
if (msg.obj instanceof StartupAdEntity) {
if (msg.what == COUNTDOWN_AD && msg.obj instanceof StartupAdEntity) {
StartupAdEntity ad = (StartupAdEntity) msg.obj;
LinkEntity linkEntity = ad.getJump();
SensorsBridge.trackEvent(
@ -556,14 +565,26 @@ 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 {
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);
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);
}
}
}
}
@ -601,7 +622,11 @@ 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,6 +10,8 @@ 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.*
@ -118,7 +120,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) ?: ""
@ -179,10 +181,11 @@ 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()
@ -213,6 +216,22 @@ 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)
@ -286,38 +305,72 @@ open class SearchActivity : BaseActivity() {
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(this)
open fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
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)
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 -> {
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_DIGEST -> showFragment(
SearchGameIndexFragment::class.java.name,
{ SearchGameIndexFragment() }
) {
removeFragment(SearchGameResultFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
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)
GAME_DETAIL -> showFragment(
SearchGameResultFragment::class.java.name,
{ SearchGameResultFragment() }
) {
removeFragment(SearchGameIndexFragment::class.java.name)
it.setParams(mSearchKey ?: "", mSearchType.value)
}
else -> {
//do nothing
}
else -> null
}
mDisplayType = type
transaction.commitAllowingStateLoss()
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
}
protected fun removeFragment(tag: String) {
val fragment = supportFragmentManager
.findFragmentByTag(tag) ?: return
supportFragmentManager
.beginTransaction()
.remove(fragment)
.commit()
supportFragmentManager.popBackStack()
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -325,6 +378,7 @@ 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, "搜索页面",
@ -341,9 +395,8 @@ open class SearchActivity : BaseActivity() {
}
override fun handleBackPressed(): Boolean {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
if (fragment == null) {
updateDisplayType(DEFAULT)
if (supportFragmentManager.fragments.size == 1) {
finish()
return true
}
return super.handleBackPressed()
@ -417,7 +470,8 @@ enum class SearchType(var value: String) {
DEFAULT("default"),
HISTORY("history"),
MANUAL("initiative"),
HOT("remen");
HOT("remen"),
RANK("rank");
fun toChinese() = when (this) {
AUTO -> "自动搜索"
@ -425,6 +479,7 @@ enum class SearchType(var value: String) {
HISTORY -> "历史搜索"
MANUAL -> "主动搜索"
HOT -> "热门搜索"
RANK -> "榜单搜索"
}
companion object {

View File

@ -419,7 +419,7 @@ public class SkipActivity extends BaseActivity {
} else {
Bundle newBundle = new Bundle();
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
EntranceUtils.jumpActivity(this, null, newBundle, (resultCode, data) -> {
EntranceUtils.jumpActivityCompat(this, newBundle, null, (resultCode, data) -> {
if(CheckLoginUtils.isLogin()) {
VHelper.INSTANCE.updateAuthorizeInfo(true);
}
@ -442,7 +442,7 @@ public class SkipActivity extends BaseActivity {
break;
default:
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页
return;
break;
}
}
} else if ("market".equals(uri.getScheme())) {

View File

@ -12,8 +12,10 @@ 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.*
/**
@ -37,6 +39,7 @@ 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)
@ -58,6 +61,7 @@ class SplashAdActivity : BaseActivity() {
screenHeightInDp,
startAdContainer!!,
sdkStartAdContainer!!,
sdkJumpBtn!!,
adsFl!!,
(mBaseHandler as BaseHandler),
true
@ -81,15 +85,23 @@ class SplashAdActivity : BaseActivity() {
override fun handleMessage(msg: Message) {
super.handleMessage(msg)
if (msg.what == MainActivity.COUNTDOWN_AD) {
if (msg.what == MainActivity.COUNTDOWN_AD || msg.what == MainActivity.COUNTDOWN_SDK_AD) {
mCountdownCount++
if (COUNTDOWN_MAX_COUNT < mCountdownCount) {
val maxCount = if (msg.what == MainActivity.COUNTDOWN_AD) {
COUNTDOWN_MAX_COUNT
} else {
MainActivity.COUNTDOWN_SDK_MAX_COUNT
}
if (maxCount < 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>(R.id.jumpBtn)
jumpBtn.text = getString(R.string.splash_jump, COUNTDOWN_MAX_COUNT - mCountdownCount)
mBaseHandler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_AD, 1000)
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)
}
}
}

View File

@ -17,8 +17,11 @@ 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.*
import com.gh.common.util.DeviceTokenUtils
import com.gh.common.util.DialogUtils
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
@ -28,6 +31,7 @@ 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
@ -55,6 +59,9 @@ 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) {
@ -274,6 +281,12 @@ 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() {
@ -305,6 +318,7 @@ 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,6 +24,7 @@ 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;
@ -85,7 +86,7 @@ public class CleanApkAdapter extends BaseRecyclerAdapter<KcSelectGameViewHolder>
Observable.create(emitter -> {
// 扫描和获取apk数据 分步操作 尽量避免 StackoverflowError
FindAllAPKPath(Environment.getExternalStorageDirectory());
FindAllAPKPath(mContext.getFilesDir());
FindAllAPKPath(new File(FileUtils.getDownloadDir(mContext)));
LoadApkData();
emitter.onComplete();
})

View File

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

View File

@ -4,11 +4,10 @@ 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
@ -64,19 +63,16 @@ class AmwaySearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
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)
}
val transaction = when (type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ AmwaySearchDefaultFragment() }
)
else -> {
val fragment = supportFragmentManager.findFragmentByTag(AmwaySearchListFragment::class.java.name)
?: AmwaySearchListFragment()
transaction.replace(R.id.search_result, fragment, AmwaySearchListFragment::class.java.name)
}
else -> showFragment(
AmwaySearchListFragment::class.java.name,
{ AmwaySearchListFragment() }
)
}
mDisplayType = type
transaction.commitAllowingStateLoss()

View File

@ -33,7 +33,7 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
mViewModel.playedGames.observeNonNull(viewLifecycleOwner) {
defaultViewModel?.isExistHotSearch = it.isNotEmpty()
updateView()
mBinding.hotList.run {
mBinding.searchDiscoveryList.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.hotHeadContainer.headTitle.text = "最近玩过"
mBinding.searchDiscoveryHeadContainer.headTitle.text = "最近玩过"
mBinding.historyFlexContainer.setLimitHeight(mFlexMaxHeight)
updateHistorySearchView(null)

View File

@ -57,6 +57,7 @@ class AmwaySearchListFragment : ToolbarFragment() {
when (it) {
LoadStatus.INIT_LOADING -> {
hideError()
hideRvList()
showLoading()
hideNoDataHint()
}
@ -64,16 +65,19 @@ class AmwaySearchListFragment : ToolbarFragment() {
hideError()
hideLoading()
hideNoDataHint()
showRvList()
}
LoadStatus.INIT_FAILED -> {
hideLoading()
hideNoDataHint()
showError()
hideRvList()
}
LoadStatus.INIT_EMPTY -> {
showNoDataHint()
hideError()
hideLoading()
hideRvList()
}
else -> {
// do nothing
@ -82,6 +86,14 @@ 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,6 +44,7 @@ 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

@ -160,8 +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(thirdPartyAd) { isSuccess ->
if (!isAdded) return@initThirdPartyAd
initThirdPartyAd(downloadManagerAd, thirdPartyAd) { isSuccess ->
mBinding.maskView.goneIf(!isSuccess)
if (!isSuccess && ownerAd != null && showOnFailed) {
mSlideInterval = ownerAd.adSource?.sliderInterval ?: -1
@ -183,18 +182,40 @@ class DownloadFragment : BaseFragment_TabLayout() {
}
}
private fun initThirdPartyAd(thirdPartyAd: AdConfig.ThirdPartyAd, callback: (isSuccess: Boolean) -> Unit) {
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 ?: ""
)
}
AdDelegateHelper.requestThirdPartyBannerAd(
this,
mBinding.adContainer,
thirdPartyAd,
DisplayUtils.getScreenWidthInDp(requireActivity()),
onAdShowAction,
onAdClickAction,
callback
)
}
private fun initOwnerAd(adConfig: AdConfig) {
if (!isAdded || adConfig.id.isEmpty()) return
if (adConfig.id.isEmpty()) return
mAdGameViewModel = viewModelProvider(AdGameViewModel.Factory(adConfig))
initAdGameBanner(adConfig)
mBinding.closeAdIv.setOnClickListener {
@ -227,8 +248,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
if (it.isNullOrEmpty() && adConfig.displayRule.adSource == AdDelegateHelper.AD_TYPE_OWNER &&
adConfig.displayRule.onFailedAction == "show" && adConfig.thirdPartyAd != null) {
// 自有广告游戏为空时,显示第三方广告
initThirdPartyAd(adConfig.thirdPartyAd) { isSuccess ->
if (!isAdded) return@initThirdPartyAd
initThirdPartyAd(adConfig, 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,12 +18,19 @@ 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) {
"launch" -> "开屏"
"native" -> "信息流"
"banner" -> "banner"
"interstitial" -> "插屏"
TYPE_LAUNCH -> "开屏"
TYPE_NATIVE -> "信息流"
TYPE_BANNER -> "banner"
TYPE_INTERSTITIAL -> "插屏"
else -> ""
}

View File

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

View File

@ -0,0 +1,31 @@
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

@ -0,0 +1,15 @@
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

@ -23,6 +23,9 @@ 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
@ -42,7 +45,6 @@ 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
@ -81,6 +83,8 @@ 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 {
@ -156,6 +160,12 @@ 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()
@ -166,24 +176,7 @@ class CommunityHomeFragment : LazyFragment() {
mMainWrapperViewModel?.bottomTabListLiveData?.observe(this) { tabList ->
mBinding?.videoAndSearchContainer?.goneIf(!mShowVideo) {
val decorView = requireActivity().window.decorView as? FrameLayout
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()
)
}
}
addHomeVideoGuideHandler()
// 每日首次进入社区tab视频lottie播放3次
val lastPlayTime = SPUtils.getLong(Constants.SP_COMMUNITY_HOME_VIDEO_LOTTIE_LAST_PLAY_TIME, 0L) / 1000
@ -285,6 +278,14 @@ 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) {
@ -836,6 +837,11 @@ 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,39 +129,40 @@ class ForumOrUserSearchActivity : SearchActivity() {
}
override fun updateDisplayType(type: DisplayType) {
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)
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)
}
}
}
}
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 -> if (mEntrance != ForumDetailFragment.ENTRANCE) {
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumOrUserSearchFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
}
} else {
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)
}
showFragment(
ForumOrUserSearchFragment::class.java.name,
{ ForumContentSearchListFragment() }
) { fragment ->
fragment.setSearchKeyAndType(mSearchKey ?: "", mSearchType.value)
fragment.arguments = intent.extras
}
}.apply {
startPageTime = System.currentTimeMillis()
}
}

View File

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

View File

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

View File

@ -3,7 +3,6 @@ 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
@ -29,18 +28,21 @@ 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
@ -86,7 +88,6 @@ class GameUploadFragment : ToolbarFragment() {
mViewModel.upLoadSuccess.observe(viewLifecycleOwner, Observer {
if (it) {
ToastUtils.showToast("上传成功")
MtaHelper.onEvent("游戏上传", "游戏上传", "上传成功")
mUploadDialog.dismiss()
requireActivity().finish()
} else {
@ -119,17 +120,11 @@ class GameUploadFragment : ToolbarFragment() {
5,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
MtaHelper.onEvent("游戏上传", "游戏图片", "添加图片")
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
startActivityForResult(intent, MEDIA_STORE_REQUEST)
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图片",
"删除图片"
)
}
mAdapter?.setPicItem(R.layout.game_upload_pic_item)
mAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -147,19 +142,13 @@ class GameUploadFragment : ToolbarFragment() {
1,
object : OnListClickListener {
override fun <T : Any?> onListClick(view: View?, position: Int, data: T) {
MtaHelper.onEvent("游戏上传", "游戏图标", "添加图片")
PermissionHelper.checkStoragePermissionBeforeAction(requireActivity()) {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "游戏上传")
startActivityForResult(intent, MEDIA_ICON_STORE_REQUEST)
}
}
}
) {
MtaHelper.onEvent(
"游戏上传",
"游戏图标",
"删除图片"
)
}
mIconAdapter?.setPicItem(R.layout.game_upload_pic_item)
mIconAdapter?.setSuggestAddPicIcon(R.drawable.icon_pic_add)
@ -194,18 +183,15 @@ 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("游戏上传", "是否联网", "无需联网")
}
}
}
@ -213,15 +199,12 @@ class GameUploadFragment : ToolbarFragment() {
when (checkedId) {
R.id.gameLanguageChineseRb -> {
mGameLanguage = "中文"
MtaHelper.onEvent("游戏上传", "游戏语言", "中文")
}
R.id.gameLanguageEnglishRb -> {
mGameLanguage = "英文"
MtaHelper.onEvent("游戏上传", "游戏语言", "英文")
}
R.id.gameLanguageOtherRb -> {
mGameLanguage = "其他"
MtaHelper.onEvent("游戏上传", "游戏语言", "其他")
}
}
}
@ -229,21 +212,17 @@ 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 {
@ -256,7 +235,6 @@ class GameUploadFragment : ToolbarFragment() {
}
private fun commit() {
MtaHelper.onEvent("游戏上传", "提交", "提交")
if (TextUtils.isEmpty(mBinding.tvChoose.text.toString())) {
ToastUtils.showToast("请先选择游戏安装包")
return
@ -471,11 +449,9 @@ 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)
@ -499,32 +475,20 @@ class GameUploadFragment : ToolbarFragment() {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == MEDIA_STORE_REQUEST || requestCode == MEDIA_ICON_STORE_REQUEST) {
val selectedImage = data.data ?: return
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
val cursor = requireContext().contentResolver.query(selectedImage, filePathColumn, null, null, null)
?: return
cursor.moveToFirst()
Utils.log("picturePath = $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))
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)
} else {
if (requestCode == MEDIA_STORE_REQUEST) {
mAdapter!!.addFileList(picturePath)
} else {
mIconAdapter!!.addFileList(picturePath)
}
mIconAdapter!!.addFileList(picturePath)
}
} catch (e: Exception) {
ToastUtils.showToast(e.message ?: "")
}
} else if (requestCode == CHOOSE_LOCAL_APK) {
val packageName = data.getStringExtra(EntranceConsts.KEY_PACKAGENAME) ?: ""
@ -610,7 +574,6 @@ 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

@ -16,12 +16,13 @@ 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() {
@ -42,7 +43,7 @@ class ChooseGameCollectionCoverTypeDialog : BaseDialogFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"创建游戏单"
), REQUEST_CODE_IMAGE

View File

@ -285,7 +285,7 @@ class GameCollectionSquareAdapter(
}
})
addOnScrollListener(ScrollEventListener(this).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(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 {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
mDefaultBinding.headerContainer.bannerIndicator.onPageScrolled(

View File

@ -63,7 +63,10 @@ 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.*
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.eventbus.EBConcernChanged
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.utils.ApkActiveUtils
@ -71,11 +74,7 @@ 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.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.dialog.*
import com.gh.gamecenter.gamedetail.entity.ContentCardEntity
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity
import com.gh.gamecenter.gamedetail.entity.Video
@ -1180,6 +1179,11 @@ 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,
@ -1187,8 +1191,33 @@ 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", "关闭弹窗"
)
}
)
}

View File

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

View File

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

View File

@ -140,7 +140,7 @@ class CommonContentHomeSLideListUi(
}
binding.recyclerView.addOnScrollListener(scrollEventListener.apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(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 {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(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 {
setOnPageChangeCallback(object : OnPageChangeCallback() {
registerOnPageChangeCallback(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 {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(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 {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
var lastStatePosition = -1
var lastScrollState = RecyclerView.SCROLL_STATE_IDLE

View File

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

View File

@ -3,20 +3,12 @@ package com.gh.gamecenter.minigame
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.text.TextUtils
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.base.GlobalActivityManager
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.forum.search.ForumSearchDao
import com.gh.gamecenter.search.SearchDefaultFragment
import com.gh.gamecenter.search.SearchGameResultFragment
import com.lightgame.config.CommonDebug
import com.lightgame.listeners.OnBackPressedListener
/**
* 小游戏-搜索页面
@ -26,22 +18,21 @@ class MiniGameSearchActivity : SearchActivity() {
override fun provideDao(): ISearchHistoryDao = MiniGameSearchDao()
override fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when(type) {
DisplayType.DEFAULT -> {
val fragment = supportFragmentManager.findFragmentByTag(SearchDefaultFragment::class.java.name)
?: MiniGameSearchDefaultFragment().apply {
arguments = Bundle().also { it.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true) }
}
transaction.replace(R.id.search_result, fragment, SearchDefaultFragment::class.java.name)
val transaction = when(type) {
DisplayType.DEFAULT -> showFragment(
SearchDefaultFragment::class.java.name,
{ MiniGameSearchDefaultFragment() }
) {
it.arguments = Bundle().also { bundle ->
bundle.putBoolean(SearchDefaultFragment.KEY_IS_GAME_SEARCH, true)
}
}
else -> {
val digestListFragment =
supportFragmentManager.findFragmentByTag(SearchGameResultFragment::class.java.name) as? MiniGameSearchResultFragment
?: MiniGameSearchResultFragment()
digestListFragment.setParams(mSearchKey ?: "", mSearchType.value)
transaction.replace(R.id.search_result, digestListFragment, SearchGameResultFragment::class.java.name)
else -> showFragment(
SearchGameResultFragment::class.java.name,
{ MiniGameSearchResultFragment() }
) {
it.setParams(mSearchKey ?: "", mSearchType.value)
}
}
mDisplayType = type

View File

@ -699,8 +699,6 @@ class HaloPersonalFragment : BaseLazyFragment() {
} catch (e: Exception) {
} as? ITestCase)?.apply {
addInstallExternalGameButton(mStubBinding.otherItems)
addInstallPluginButton(mStubBinding.otherItems)
addInstallPlugin32Button(mStubBinding.otherItems)
}
}
@ -857,7 +855,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
}
})
addOnScrollListener(ScrollEventListener(this).apply {
setOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {
super.onPageScrolled(position, positionOffset, positionOffsetPixels)
mStubBinding.bannerIndicator.onPageScrolled(

View File

@ -10,20 +10,20 @@ import android.view.View
import androidx.annotation.RequiresApi
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.content.ContextCompat
import com.gh.common.util.*
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.ErrorEntity
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentBackgroundPreviewBinding
import com.gh.gamecenter.feature.entity.BackgroundImageEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserViewModel
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import io.reactivex.Single
@ -282,7 +282,7 @@ class BackgroundPreviewFragment : ToolbarFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"个性背景"
), MEDIA_STORE_REQUEST

View File

@ -12,12 +12,13 @@ import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
import com.gh.gamecenter.databinding.PersonalityBackgroundFragmentBinding
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserViewModel
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
@ -75,7 +76,7 @@ class PersonalityBackgroundFragment : ToolbarFragment() {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
1,
"个性背景"
), MEDIA_STORE_REQUEST

View File

@ -28,6 +28,7 @@ import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.baselist.ListFragment
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_COMMENT_ID
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.syncpage.SyncDataEntity
import com.gh.gamecenter.common.syncpage.SyncFieldConstants
import com.gh.gamecenter.common.syncpage.SyncPageRepository
@ -38,10 +39,10 @@ import com.gh.gamecenter.databinding.ItemCommentEditImageBinding
import com.gh.gamecenter.eventbus.EBCommentSuccess
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.eventbus.EBDeleteComment
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_ID
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.GAME_COLLECTION_TITLE
import com.gh.gamecenter.qa.comment.CommentActivity.Companion.QUESTION_ID
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.halo.assistant.HaloApp
import com.lightgame.utils.Util_System_Keyboard
import com.lightgame.utils.Utils
@ -494,7 +495,7 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
val maxChooseCount = 9 - mViewModel.pictureList.size
val intent = LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.IMAGE,
ChooseType.IMAGE,
maxChooseCount,
"评论列表"
)
@ -521,11 +522,6 @@ open class NewCommentFragment : ListFragment<CommentEntity, NewCommentViewModel>
return
}
if (mShowInputOnly) {
// Fuck pm
MtaHelper.onEvent("帖子详情", "评论详情-全部回复", "发送")
}
mViewModel.postPictureAndComment(content, mCommentEntity)
}

View File

@ -1,148 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.os.Bundle
import android.view.View
import android.widget.AdapterView
import android.widget.ImageView
import android.widget.PopupWindow
import androidx.core.content.ContextCompat
import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.halo.assistant.HaloApp
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import com.zhihu.matisse.internal.entity.Album
import com.zhihu.matisse.internal.entity.SelectionSpec
import com.zhihu.matisse.internal.model.AlbumCollection
/**
* 选择本地视频/图片
*/
class LocalMediaActivity : ToolBarActivity(), AlbumCollection.AlbumCallbacks {
private var mLocalMediaFragment: LocalMediaFragment? = null
private lateinit var mAlbumsSpinner: VideoAlbumsSpanner
private lateinit var mAlbumsAdapter: VideoAlbumsAdapter
private val mAlbumCollection = AlbumCollection()
private var mIsFirstAlbumLoad = true
private var mChooseType = ""
override fun getLayoutId(): Int = R.layout.activity_video_tablayout_viewpager
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
mChooseType = intent.getStringExtra(EntranceConsts.KEY_TYPE) ?: ""
if (mChooseType == ChooseType.VIDEO.value) {
setNavigationTitle("本地视频")
} else {
setNavigationTitle("本地图片")
}
mLocalMediaFragment = LocalMediaFragment().apply { arguments = intent.extras }
supportFragmentManager.beginTransaction()
.replace(R.id.container, mLocalMediaFragment!!, LocalMediaFragment::class.java.name)
.commitAllowingStateLoss()
initAlbumsSpinner()
mTitleTv.setOnClickListener {
mAlbumsSpinner.show(findViewById<View>(R.id.container).height)
setPhotoNavigationTitle(true)
}
}
private fun initAlbumsSpinner() {
mAlbumsSpinner = VideoAlbumsSpanner(this)
mAlbumsAdapter = VideoAlbumsAdapter(this)
mAlbumsSpinner.setPopupAnchorView(findViewById(R.id.normal_toolbar))
mAlbumsSpinner.setAdapter(mAlbumsAdapter)
mAlbumsSpinner.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
override fun onNothingSelected(parent: AdapterView<*>?) {
}
override fun onItemSelected(parent: AdapterView<*>?, view: View?, position: Int, id: Long) {
mAlbumCollection.setStateCurrentSelection(position)
mAlbumsAdapter.cursor.moveToPosition(position)
val album = Album.valueOf(mAlbumsAdapter.cursor)
if (album.isAll && SelectionSpec.getInstance().capture) {
album.addCaptureCount()
}
if (mLocalMediaFragment?.isAdded == true) {
mLocalMediaFragment?.loadVideos(album)
}
}
}
mAlbumsSpinner.setDismissListener(PopupWindow.OnDismissListener {
setPhotoNavigationTitle(false)
})
//必须加这行代码,[SelectionSpec]是单例模式下次使用必须先更新MimeType
val mimeType = if (mChooseType == ChooseType.VIDEO.value) {
MimeType.ofVideo()
} else {
MimeType.ofImage()
}
val maxChooseCount = intent.getIntExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
Matisse.from(this).choose(mimeType).showSingleMediaType(true).maxSelectable(maxChooseCount)
mAlbumCollection.onCreate(this, this)
mAlbumCollection.loadAlbums()
}
override fun onAlbumLoad(cursor: Cursor?) {
if (mIsFirstAlbumLoad) {
mIsFirstAlbumLoad = false
mAlbumsAdapter.swapCursor(cursor)
mBaseHandler.post {
cursor?.moveToPosition(mAlbumCollection.currentSelection)
val album = Album.valueOf(cursor)
if (album.isAll && SelectionSpec.getInstance().capture) {
album.addCaptureCount()
}
if (mLocalMediaFragment?.isAdded == true) {
mLocalMediaFragment?.loadVideos(album)
}
}
}
}
override fun onAlbumReset() {
}
private fun setPhotoNavigationTitle(up: Boolean) {
val drawable = ContextCompat.getDrawable(
HaloApp.getInstance().application,
if (up) R.drawable.ic_video_arrow_up else R.drawable.ic_video_arrow_down
)
val arrowIv = findViewById<ImageView>(R.id.arrowIv)
arrowIv?.setImageDrawable(drawable)
}
override fun isAutoResetViewBackgroundEnabled(): Boolean = true
override fun onDarkModeChanged() {
super.onDarkModeChanged()
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
}
companion object {
fun getIntent(context: Context, chooseType: ChooseType, maxChooseCount: Int = 1, entrance: String): Intent {
return Intent(context, LocalMediaActivity::class.java).apply {
putExtra(EntranceConsts.KEY_TYPE, chooseType.value)
putExtra(EntranceConsts.KEY_CHOOSE_MAX_COUNT, maxChooseCount)
putExtra(EntranceConsts.KEY_ENTRANCE, entrance)
}
}
}
enum class ChooseType(val value: String) {
VIDEO("video"),
IMAGE("image")
}
}

View File

@ -1,95 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.database.Cursor
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.databinding.LocalVideoItemBinding
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.ui.adapter.RecyclerViewCursorAdapter
import com.zhihu.matisse.internal.utils.PathUtils
class LocalMediaAdapter(
val context: Context,
val mChooseType: String,
val maxChooseSize: Int,
val entrance: String,
val callback: (ArrayList<Item>) -> Unit
) : RecyclerViewCursorAdapter<LocalVideoPreviewViewHolder>(null) {
private val mSelectedMediaList = arrayListOf<Item>()
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): LocalVideoPreviewViewHolder {
val view = LayoutInflater.from(parent.context).inflate(R.layout.local_video_item, parent, false)
return LocalVideoPreviewViewHolder(LocalVideoItemBinding.bind(view))
}
override fun onBindViewHolder(holder: LocalVideoPreviewViewHolder, cursor: Cursor?, position: Int) {
val item = Item.valueOf(cursor)
holder.binding.durationTv.goneIf(mChooseType == LocalMediaActivity.ChooseType.IMAGE.value)
val path = "file:///${PathUtils.getPath(context, item.contentUri)}"
ImageUtils.displayResizeMedia(holder.binding.preview, path, 200, 200)
holder.binding.durationTv.text = TimeUtils.formatVideoDuration(item.duration / 1000)
val drawable = if (mSelectedMediaList.contains(item)) {
if (maxChooseSize == 1) {
R.drawable.ic_choose_media_selected.toDrawable()
} else {
R.drawable.ic_choose_media_bg.toDrawable()
}
} else {
R.drawable.ic_choose_media_normal.toDrawable()
}
holder.binding.checkImageView.setImageDrawable(drawable)
if (mSelectedMediaList.contains(item) && maxChooseSize > 1) {
holder.binding.chooseCountTv.visibility = View.VISIBLE
holder.binding.chooseCountTv.text = (mSelectedMediaList.indexOf(item) + 1).toString()
} else {
holder.binding.chooseCountTv.visibility = View.GONE
}
holder.itemView.setOnClickListener {
if (mSelectedMediaList.contains(item)) {
mSelectedMediaList.remove(item)
notifyDataSetChanged()
callback.invoke(mSelectedMediaList)
} else {
if (maxChooseSize == 1) {
mSelectedMediaList.clear()
}
if (mSelectedMediaList.size < maxChooseSize) {
mSelectedMediaList.add(item)
notifyDataSetChanged()
callback.invoke(mSelectedMediaList)
} else {
if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) {
ToastUtils.showToast("至多选择${maxChooseSize}张图片")
} else {
ToastUtils.showToast("至多选择${maxChooseSize}条视频")
}
}
}
if (entrance == "发帖子" || entrance == "发提问帖" || entrance == "发视频帖") {
val publishContentType = if (entrance == "发帖子") "帖子" else if (entrance == "发提问帖") "提问帖" else "视频帖"
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
NewLogUtils.logChooseMedia("click_radio_button", publishContentType, publishMediaType)
}
}
}
override fun getItemViewType(position: Int, cursor: Cursor?): Int {
return 0
}
fun getSelectedMediaList(): ArrayList<Item> {
return mSelectedMediaList
}
}
class LocalVideoPreviewViewHolder(val binding: LocalVideoItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)

View File

@ -1,191 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.app.Activity
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import androidx.core.os.bundleOf
import androidx.recyclerview.widget.GridLayoutManager
import com.gh.common.util.NewLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
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.tryWithDefaultCatch
import com.gh.gamecenter.common.view.GridSpacingItemDecoration
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.databinding.FragmentLocalMediaBinding
import com.gh.gamecenter.entity.LocalVideoEntity
import com.zhihu.matisse.internal.entity.Album
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.model.AlbumMediaCollection
import com.zhihu.matisse.internal.model.SelectedItemCollection
import com.zhihu.matisse.internal.ui.BasePreviewActivity
import com.zhihu.matisse.internal.ui.SelectedPreviewActivity
import com.zhihu.matisse.internal.utils.PathUtils
import com.zhihu.matisse.ui.MatisseActivity
class LocalMediaFragment : BaseFragment<Any>(), AlbumMediaCollection.AlbumMediaCallbacks {
private lateinit var mBinding: FragmentLocalMediaBinding
private lateinit var mAdapter: LocalMediaAdapter
private var mAlbumMediaCollection: AlbumMediaCollection? = null
private var mChooseType = ""
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View {
mBinding = FragmentLocalMediaBinding.inflate(LayoutInflater.from(requireContext()), null, false)
return mBinding.root
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mChooseType = arguments?.getString(EntranceConsts.KEY_TYPE) ?: ""
// mBinding.reuseNoneData.reuseNoneDataTv.text = "暂无数据~"
mBinding.listRv.layoutManager = GridLayoutManager(requireContext(), 3)
mBinding.listRv.addItemDecoration(GridSpacingItemDecoration(3, 4F.dip2px(), false))
val maxChooseCount = arguments?.getInt(EntranceConsts.KEY_CHOOSE_MAX_COUNT, 1)
mAdapter = LocalMediaAdapter(
requireContext(), mChooseType, maxChooseCount
?: 1, mEntrance
) {
mBinding.previewTv.isEnabled = it.isNotEmpty()
mBinding.confirmTv.isEnabled = it.isNotEmpty()
if (it.isEmpty()) {
mBinding.previewTv.setTextColor(R.color.text_instance.toColor(requireContext()))
mBinding.confirmTv.alpha = 0.6f
} else {
mBinding.previewTv.setTextColor(R.color.text_secondary.toColor(requireContext()))
mBinding.confirmTv.alpha = 1f
}
mBinding.numTv.text = "(${it.size}/${mAdapter.maxChooseSize})"
}
mBinding.numTv.text = "(0/${mAdapter.maxChooseSize})"
mBinding.listRv.adapter = mAdapter
mBinding.listRefresh.isEnabled = false
val publishContentType = if (mEntrance == "发帖子") "帖子" else if (mEntrance == "发提问帖") "提问帖" else "视频帖"
val publishMediaType = if (mChooseType == LocalMediaActivity.ChooseType.IMAGE.value) "图片" else "视频"
mBinding.previewTv.setOnClickListener {
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
val intent = PreviewVideoActivity.getIntent(requireContext(), mAdapter.getSelectedMediaList())
requireActivity().startActivityForResult(intent, PREVIEW_VIDEO)
NewLogUtils.logChooseMedia("click_preview", publishContentType, publishMediaType)
} else {
val intent = Intent(requireContext(), SelectedPreviewActivity::class.java)
val bundle = bundleOf(
SelectedItemCollection.STATE_SELECTION to mAdapter.getSelectedMediaList(),
SelectedItemCollection.STATE_COLLECTION_TYPE to SelectedItemCollection.COLLECTION_IMAGE
)
intent.putExtra(BasePreviewActivity.EXTRA_DEFAULT_BUNDLE, bundle)
startActivityForResult(intent, PREVIEW_IMAGE)
}
}
mBinding.confirmTv.setOnClickListener {
NewLogUtils.logChooseMedia("click_confirm", publishContentType, publishMediaType)
val intent = Intent()
if (mChooseType == LocalMediaActivity.ChooseType.VIDEO.value) {
val localVideoList = arrayListOf<LocalVideoEntity>()
mAdapter.getSelectedMediaList().forEach {
val path = PathUtils.getPath(requireContext(), it.contentUri)
if (path == null) {
toast("视频已不存在,请重新选择")
return@forEach
}
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
val format = getFileFormat(it.mimeType)
localVideoList.add(
LocalVideoEntity(
id,
path,
contentUri = it.contentUri,
duration = it.duration,
format = format,
size = it.size
)
)
}
intent.putExtra(LocalVideoEntity::class.java.name, localVideoList)
} else {
val data = mAdapter.getSelectedMediaList().map { it.contentUri }.toList()
val path = data.map { PathUtils.getPath(requireContext(), it) }.toList()
intent.putParcelableArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION, ArrayList<Uri>(data))
intent.putStringArrayListExtra(MatisseActivity.EXTRA_RESULT_SELECTION_PATH, ArrayList<String>(path))
}
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
}
}
private fun getFileFormat(mimeType: String?): String {
var format = ""
tryWithDefaultCatch {
if (mimeType != null) {
val split = mimeType.split("/")
format = if (split.count() >= 2) {
split[1]
} else {
mimeType
}
}
}
return format
}
override fun onAlbumMediaReset() {
mAdapter.swapCursor(null)
}
override fun onAlbumMediaLoad(cursor: Cursor?) {
mAdapter.swapCursor(cursor)
mBinding.reuseNoneData.reuseNoneData.visibility = View.GONE
mBinding.reuseLlLoading.root.visibility = View.GONE
mBinding.reuseNoConnection.root.visibility = View.GONE
mBinding.listRefresh.isRefreshing = false
}
fun loadVideos(album: Album) {
mAlbumMediaCollection?.onDestroy()
mAlbumMediaCollection = AlbumMediaCollection()
mAlbumMediaCollection?.onCreate(requireActivity(), this@LocalMediaFragment)
mAlbumMediaCollection?.load(album)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null) return
if (requestCode == PREVIEW_IMAGE) {
val bundleExtra = data.getBundleExtra(BasePreviewActivity.EXTRA_RESULT_BUNDLE)
val resultApply = data.getBooleanExtra(BasePreviewActivity.EXTRA_RESULT_APPLY, false)
val items = bundleExtra?.getParcelableArrayList<Item>(SelectedItemCollection.STATE_SELECTION)
if (resultApply && !items.isNullOrEmpty()) {
mAdapter.getSelectedMediaList().clear()
mAdapter.getSelectedMediaList().addAll(items)
mAdapter.notifyDataSetChanged()
mBinding.numTv.text = "(${items.size}/${mAdapter.maxChooseSize})"
}
} else if (requestCode == PREVIEW_VIDEO) {
requireActivity().setResult(Activity.RESULT_OK, data)
requireActivity().finish()
}
}
override fun onDestroy() {
super.onDestroy()
mAlbumMediaCollection?.onDestroy()
}
companion object {
const val PREVIEW_VIDEO = 100
const val PREVIEW_IMAGE = 101
}
}

View File

@ -1,36 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.content.Context
import android.content.Intent
import android.os.Bundle
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.R
import com.zhihu.matisse.internal.entity.Item
class PreviewVideoActivity : BaseActivity() {
override fun getLayoutId(): Int {
return R.layout.activity_amway
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DisplayUtils.transparentStatusBar(this)
val containerFragment = supportFragmentManager.findFragmentByTag(PreviewVideoFragment::class.java.name)
?: PreviewVideoFragment().with(intent.extras)
// 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
supportFragmentManager.beginTransaction()
.replace(R.id.placeholder, containerFragment, PreviewVideoFragment::class.java.name)
.commitAllowingStateLoss()
}
companion object {
fun getIntent(context: Context, videos: ArrayList<Item>): Intent {
val intent = Intent(context, PreviewVideoActivity::class.java)
intent.putExtra(EntranceConsts.KEY_VIDEO_LIST, videos)
return intent
}
}
}

View File

@ -1,229 +0,0 @@
package com.gh.gamecenter.qa.editor
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder
import com.facebook.drawee.generic.RoundingParams
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.common.base.fragment.BaseFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
import com.gh.gamecenter.CropImageActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.FragmentPreviewVideoBinding
import com.gh.gamecenter.databinding.ItemVideoSelectorBinding
import com.gh.gamecenter.entity.LocalVideoEntity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.view.UploadVideoActivity
import com.lightgame.adapter.BaseRecyclerAdapter
import com.shuyu.gsyvideoplayer.GSYVideoManager
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.zhihu.matisse.internal.entity.Item
import com.zhihu.matisse.internal.utils.PathUtils
class PreviewVideoFragment : BaseFragment<Any>() {
private lateinit var mBinding: FragmentPreviewVideoBinding
private var mVideoItems: ArrayList<Item> = arrayListOf()
private var mProcessingDialog: WaitingDialogFragment? = null
private lateinit var mVideoSelectorAdapter: VideoSelectorAdapter
private val mLocalVideoList = arrayListOf<LocalVideoEntity>()
override fun getLayoutId(): Int = 0
override fun getInflatedLayout(): View {
mBinding = FragmentPreviewVideoBinding.inflate(LayoutInflater.from(requireContext()), null, false)
return mBinding.root
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mVideoItems = arguments?.getParcelableArrayList<Item>(EntranceConsts.KEY_VIDEO_LIST)
?: arrayListOf()
if (mVideoItems.isNotEmpty()) {
mVideoItems.forEach {
val path = PathUtils.getPath(requireContext(), it.contentUri)
val id = MD5Utils.getUrlMD5(path) + System.currentTimeMillis()
val format = getFileFormat(it.mimeType)
val localVideoEntity = LocalVideoEntity(
id = id,
filePath = path,
contentUri = it.contentUri,
duration = it.duration,
format = format,
size = it.size
)
mLocalVideoList.add(localVideoEntity)
}
initVideo(mLocalVideoList[0])
}
mBinding.numTv.text = "(1/${mVideoItems.size})"
mVideoSelectorAdapter = VideoSelectorAdapter(requireContext(), mLocalVideoList) { entity, position ->
mBinding.videoView.release()
mBinding.numTv.text = "(${position + 1}/${mVideoItems.size})"
initVideo(entity)
}
mBinding.videoSelectorRv.adapter = mVideoSelectorAdapter
mBinding.videoSelectorRv.layoutManager =
LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
mBinding.videoSelectorRv.addItemDecoration(
GridSpacingItemColorDecoration(
requireContext(),
4,
0,
R.color.transparent
)
)
mBinding.confirmTv.setOnClickListener {
val intent = Intent()
intent.putExtra(LocalVideoEntity::class.java.name, mLocalVideoList)
requireActivity().setResult(Activity.RESULT_OK, intent)
requireActivity().finish()
}
mBinding.changeCoverTv.setOnClickListener {
val item = mVideoItems[mVideoSelectorAdapter.selectPosition]
val intent =
PosterEditActivity.getIntentByPath(requireContext(), PathUtils.getPath(requireContext(), item.uri))
startActivityForResult(intent, UploadVideoActivity.REQUEST_CODE_IMAGE_CROP)
}
mBinding.backBtn.setOnClickListener { requireActivity().finish() }
}
private fun getFileFormat(mimeType: String?): String {
var format = ""
tryWithDefaultCatch {
if (mimeType != null) {
val split = mimeType.split("/")
format = if (split.count() >= 2) {
split[1]
} else {
mimeType
}
}
}
return format
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (data == null || resultCode != Activity.RESULT_OK) return
if (requestCode == UploadVideoActivity.REQUEST_CODE_IMAGE_CROP) {
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: ""
if (imagePath.isNotEmpty()) {
uploadImage(imagePath)
}
}
}
private fun uploadImage(imagePath: String) {
mProcessingDialog = WaitingDialogFragment.newInstance("图片上传中...", false)
mProcessingDialog?.show(requireActivity().supportFragmentManager, WaitingDialogFragment::class.java.name)
UploadImageUtils.uploadImage(
UploadImageUtils.UploadType.poster,
imagePath,
object : UploadImageUtils.OnUploadImageListener {
override fun onSuccess(imageUrl: String) {
mProcessingDialog?.dismiss()
mLocalVideoList[mVideoSelectorAdapter.selectPosition].poster = imageUrl
mBinding.videoView.updateThumb(imageUrl)
}
override fun onError(e: Throwable?) {
mProcessingDialog?.dismiss()
ToastUtils.showToast("上传失败")
}
override fun onProgress(total: Long, progress: Long) {}
})
}
private fun initVideo(entity: LocalVideoEntity) {
GSYVideoOptionBuilder()
.setIsTouchWiget(false)
.setUrl(entity.filePath)
.setRotateViewAuto(false)
.setCacheWithPlay(false)
.setRotateWithSystem(false)
.setReleaseWhenLossAudio(true)
.setLooping(false)
.setShowFullAnimation(false)
.build(mBinding.videoView)
if (entity.poster.isNotEmpty()) {
mBinding.videoView.updateThumb(entity.poster)
} else {
mBinding.videoView.updateThumb("file:///${PathUtils.getPath(requireContext(), entity.contentUri)}")
}
}
class VideoSelectorAdapter(
context: Context,
val localVideoList: ArrayList<LocalVideoEntity>,
val callback: (LocalVideoEntity, Int) -> Unit
) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
var selectPosition: Int = 0
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
return VideoSelectorViewHolder(
ItemVideoSelectorBinding.inflate(
LayoutInflater.from(mContext),
parent,
false
)
)
}
override fun getItemCount(): Int = localVideoList.size
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
if (holder is VideoSelectorViewHolder) {
val item = localVideoList[position]
holder.binding.previewBorder.goneIf(selectPosition != position)
setPreviewBorder(holder.binding.preview, selectPosition == position)
ImageUtils.display(holder.binding.preview, "file:///${PathUtils.getPath(mContext, item.contentUri)}")
holder.binding.root.setOnClickListener {
selectPosition = position
callback.invoke(localVideoList[selectPosition], selectPosition)
notifyDataSetChanged()
}
}
}
private fun setPreviewBorder(view: SimpleDraweeView, isSelected: Boolean) {
val params = RoundingParams()
params.setBorder(
if (isSelected) R.color.black.toColor() else R.color.transparent.toColor(),
if (isSelected) 1f.dip2px().toFloat() else 0f
)
params.setCornersRadius(4f.dip2px().toFloat())
val build = GenericDraweeHierarchyBuilder.newInstance(mContext.resources)
.setRoundingParams(params)
.build()
view.hierarchy = build
}
}
class VideoSelectorViewHolder(val binding: ItemVideoSelectorBinding) : RecyclerView.ViewHolder(binding.root)
override fun onResume() {
super.onResume()
GSYVideoManager.onResume()
}
override fun onPause() {
super.onPause()
GSYVideoManager.onPause()
}
override fun onDestroy() {
super.onDestroy()
GSYVideoManager.releaseAllVideos()
}
}

View File

@ -24,8 +24,10 @@ import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.LocalVideoEntity
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.mvvm.Status
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnUiThread
@ -34,6 +36,7 @@ import com.gh.gamecenter.databinding.FragmentVideoPublishBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.BbsType
import com.gh.gamecenter.qa.dialog.ChooseActivityDialogFragment
@ -41,7 +44,6 @@ import com.gh.gamecenter.qa.dialog.ChooseForumActivity
import com.gh.gamecenter.qa.dialog.ChooseSectionDialogFragment
import com.gh.gamecenter.qa.dialog.InputUrlDialogFragment
import com.gh.gamecenter.qa.editor.GameActivity
import com.gh.gamecenter.qa.editor.LocalMediaActivity
import com.gh.gamecenter.video.poster.PosterEditActivity
import com.gh.gamecenter.video.upload.OnUploadListener
import com.gh.gamecenter.video.upload.UploadManager
@ -117,7 +119,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
1,
"发视频帖"
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE
@ -285,7 +287,7 @@ class VideoPublishFragment : ToolbarFragment(), KeyboardHeightObserver {
startActivityForResult(
LocalMediaActivity.getIntent(
requireContext(),
LocalMediaActivity.ChooseType.VIDEO,
ChooseType.VIDEO,
1,
"发视频帖"
), BaseRichEditorActivity.INSERT_MEDIA_VIDEO_CODE

View File

@ -29,6 +29,7 @@ import com.gh.gamecenter.entity.DeviceDialogEntity;
import com.gh.gamecenter.entity.DialogEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardEntity;
import com.gh.gamecenter.entity.DiscoveryGameCardLabel;
import com.gh.gamecenter.entity.DiverterEntity;
import com.gh.gamecenter.entity.FollowCommonContentCollection;
import com.gh.gamecenter.entity.FollowDynamicEntity;
import com.gh.gamecenter.entity.FollowUserEntity;
@ -107,6 +108,7 @@ import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
import com.gh.gamecenter.feature.entity.CommentEntity;
import com.gh.gamecenter.feature.entity.CommentnumEntity;
import com.gh.gamecenter.feature.entity.ConcernEntity;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.feature.entity.ForumVideoEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.LibaoEntity;
@ -126,7 +128,6 @@ import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.feature.entity.UserEntity;
import com.gh.gamecenter.feature.entity.ViewsEntity;
import com.gh.gamecenter.feature.entity.WXSubscribeMsgConfig;
import com.gh.gamecenter.feature.entity.FloatingWindowEntity;
import com.gh.gamecenter.gamedetail.entity.BigEvent;
import com.gh.gamecenter.gamedetail.entity.NewGameDetailEntity;
import com.gh.gamecenter.home.custom.model.CustomPageData;
@ -3358,7 +3359,7 @@ public interface ApiService {
* 页面数据聚合[底部tab+多tab导航页+默认数据]
*/
@GET("app/data_union")
Single<DataUnionEntity> getDataUnion();
Single<DataUnionEntity> getDataUnion(@Query("diverter") String diverter);
/**
* 底部tab
@ -3390,4 +3391,16 @@ public interface ApiService {
*/
@GET("game_lists/hot_columns")
Single<List<SubjectEntity>> getHotColumns();
/**
* 分流器列表信息
*/
@GET("app/{module}/diverter")
Single<List<DiverterEntity>> getDiverterList(@Header("Install-First-Access") String isInstallFirstAccess, @Path("module") String module);
/**
* 访问分流页面,更新分流访问次数
*/
@PATCH("app/{module}/diverter_visit_time")
Single<ResponseBody> patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body);
}

View File

@ -1,9 +1,6 @@
package com.gh.gamecenter.search
import android.graphics.Color
import android.graphics.LinearGradient
import android.graphics.Shader
import android.graphics.Typeface
import android.graphics.drawable.GradientDrawable
import android.os.Bundle
import android.text.TextUtils
@ -18,7 +15,6 @@ import androidx.viewpager.widget.PagerAdapter
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureManager
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.BaseFragment
@ -34,7 +30,7 @@ import com.gh.gamecenter.databinding.TabItemSearchDefaultRankBinding
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.db.SearchHistoryDao
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.feature.entity.HotTagEntity
import com.gh.gamecenter.feature.entity.DiscoveryTagEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.google.android.flexbox.FlexboxLayout
@ -46,7 +42,7 @@ import org.json.JSONObject
open class SearchDefaultFragment : BaseFragment<Any>() {
private var mHotTagList: List<HotTagEntity>? = null
private var mSearchDiscoveryTagList: List<DiscoveryTagEntity>? = null
protected var mRankList: List<SettingsEntity.Search.RankList>? = null
protected lateinit var mBinding: FragmentSearchDefaultBinding
@ -63,28 +59,28 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
// FlexboxLayout:maxLine 不符合需求
protected val mFlexMaxHeight = DisplayUtils.dip2px(57F)
private val mHotTagClickListener: (Int) -> Unit = {
val tag = mHotTagList!![it]
NewFlatLogUtils.logSearchHotTagClick(
tag.name ?: "",
tag.type ?: "",
tag.link ?: "",
tag.text ?: ""
)
DataLogUtils.uploadHotTagLog(context, tag.name)
PageSwitchDataHelper.pushCurrentPageData(
hashMapOf(
Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "游戏搜索-热门标签"),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_ID, tag.id ?: ""),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_NAME, tag.name ?: " ")
private val mSearchDiscoveryTagClickListener: (Int) -> Unit = {
val tag = mSearchDiscoveryTagList!![it]
val keyword = tag.keyword
if (keyword.isNotEmpty()) {
PageSwitchDataHelper.pushCurrentPageData(
hashMapOf(
Pair(PageSwitchDataHelper.PAGE_BUSINESS_TYPE, "游戏搜索-搜索发现"),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_ID, tag.id ?: ""),
Pair(PageSwitchDataHelper.PAGE_BUSINESS_NAME, tag.text ?: " ")
)
)
)
SensorsBridge.trackEvent("SearchLabelClick", "label_name", tag.name ?: "", "label_id", tag.id ?: "")
val exposureEvent = ExposureEvent.createEvent(
null,
source = listOf(ExposureSource("首页搜索", ""), ExposureSource("热门标签", tag.name ?: ""))
)
DirectUtils.directToLinkPage(requireContext(), tag, "(搜索-${tag.name})", "", exposureEvent) // 不需要path
SensorsBridge.trackEvent("SearchLabelClick", "label_name", tag.text ?: "", "label_id", tag.id ?: "")
SensorsBridge.trackSearchDiscoveryClick(
labelName = tag.text ?: "",
labelId = tag.id ?: "",
searchContent = keyword,
position = it
)
mViewModel?.add(keyword)
EventBus.getDefault().post(EBSearch("history", keyword))
Util_System_Keyboard.hideSoftKeyboardByIBinder(context, mBinding.historyFlex.windowToken)
}
}
override fun getLayoutId(): Int {
@ -137,16 +133,16 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
}
mViewModel?.isExistRankList = mRankList?.isNotEmpty() == true
mHotTagList = Config.getSettings()?.search?.hotTag
mViewModel?.isExistHotTag = mHotTagList?.isNotEmpty() == true
mSearchDiscoveryTagList = Config.getSettings()?.search?.discoveryTag
mViewModel?.isExistSearchDiscoveryTag = mSearchDiscoveryTagList?.isNotEmpty() == true
updateHistorySearchView(null)
mViewModel?.historySearchLiveData?.observe(viewLifecycleOwner) {
updateHistorySearchView(it)
}
mBinding.hotTagFlexContainer.setLimitHeight(mFlexMaxHeight)
createFlexContent(mBinding.hotTagFlex, getTagListString(), true, clickListener = mHotTagClickListener)
mBinding.searchDiscoveryTagFlexContainer.setLimitHeight(mFlexMaxHeight)
createFlexContent(mBinding.searchDiscoveryTagFlex, getTagListString(), clickListener = mSearchDiscoveryTagClickListener)
initHeadView()
initRankViewPager()
}
@ -175,12 +171,12 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
})
}
}
mBinding.hotHeadContainer.headTitle.text = getString(R.string.search_hot)
mBinding.hotHeadContainer.headTitle.textSize = 16F
mBinding.hotHeadContainer.headActionTv.visibility = View.GONE
mBinding.hotTagHeadContainer.headTitle.text = getString(R.string.search_hot_tag)
mBinding.hotTagHeadContainer.headTitle.textSize = 16F
mBinding.hotTagHeadContainer.headActionTv.visibility = View.GONE
mBinding.searchDiscoveryHeadContainer.headTitle.text = getString(R.string.search_hot)
mBinding.searchDiscoveryHeadContainer.headTitle.textSize = 16F
mBinding.searchDiscoveryHeadContainer.headActionTv.visibility = View.GONE
mBinding.searchDiscoveryTagHeadContainer.headTitle.text = getString(R.string.search_discovery_tag)
mBinding.searchDiscoveryTagHeadContainer.headTitle.textSize = 16F
mBinding.searchDiscoveryTagHeadContainer.headActionTv.visibility = View.GONE
}
protected open fun initRankViewPager() {
@ -315,8 +311,8 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
mBinding.historyHeadContainer.root.visibility =
if (mViewModel?.isExistHistory == true) View.VISIBLE else View.GONE
mBinding.historyFlex.visibility = if (mViewModel?.isExistHistory == true) View.VISIBLE else View.GONE
mBinding.hotTagHeadContainer.root.layoutParams =
(mBinding.hotTagHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
mBinding.searchDiscoveryTagHeadContainer.root.layoutParams =
(mBinding.searchDiscoveryTagHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
setMargins(
0,
if (mViewModel?.isExistHistory == true) 16F.dip2px() else 0,
@ -324,21 +320,21 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
0
)
}
mBinding.hotHeadContainer.root.layoutParams =
(mBinding.hotHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
mBinding.searchDiscoveryHeadContainer.root.layoutParams =
(mBinding.searchDiscoveryHeadContainer.root.layoutParams as ConstraintLayout.LayoutParams).apply {
setMargins(
0,
if (mViewModel?.isExistHistory == false && mViewModel?.isExistHotTag == false) 16F.dip2px() else 0,
if (mViewModel?.isExistHistory == false && mViewModel?.isExistSearchDiscoveryTag == false) 16F.dip2px() else 0,
0,
0
)
}
mBinding.hotTagHeadContainer.root.visibility =
if (mViewModel?.isExistHotTag == true) View.VISIBLE else View.GONE
mBinding.hotTagFlex.visibility = if (mViewModel?.isExistHotTag == true) View.VISIBLE else View.GONE
mBinding.hotHeadContainer.root.visibility =
mBinding.searchDiscoveryTagHeadContainer.root.visibility =
if (mViewModel?.isExistSearchDiscoveryTag == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryTagFlex.visibility = if (mViewModel?.isExistSearchDiscoveryTag == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryHeadContainer.root.visibility =
if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.hotList.visibility = if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.searchDiscoveryList.visibility = if (mViewModel?.isExistHotSearch == true) View.VISIBLE else View.GONE
mBinding.rankTabLayout.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
mBinding.rankTabIndicator.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
mBinding.rankViewPager.visibility = if (mViewModel?.isExistRankList == true) View.VISIBLE else View.GONE
@ -360,9 +356,9 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
private fun getTagListString(): List<String> {
val list = ArrayList<String>()
if (mHotTagList != null) {
for (entity in mHotTagList!!) {
entity.name?.let { list.add(it) }
if (mSearchDiscoveryTagList != null) {
for (entity in mSearchDiscoveryTagList!!) {
entity.text?.let { list.add(it) }
}
}
return list
@ -371,7 +367,6 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
fun createFlexContent(
flexView: FlexboxLayout,
contentList: List<String>?,
isHotTag: Boolean = false,
clickListener: (Int) -> Unit
) {
@ -385,16 +380,12 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
val params = FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, 24F.dip2px())
flexCell.layoutParams = params
if (isHotTag && !mHotTagList.isNullOrEmpty() && mHotTagList!![index].isGuessSearch) {
createSmartHotTagStyle(flexCell, contentList[index])
} else {
flexCell.setSingleLine()
flexCell.ellipsize = TextUtils.TruncateAt.END
flexCell.gravity = Gravity.CENTER
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(contentList[index], 6)
flexCell.setTextColor(R.color.text_secondary.toColor(requireContext()))
}
flexCell.setSingleLine()
flexCell.ellipsize = TextUtils.TruncateAt.END
flexCell.gravity = Gravity.CENTER
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(contentList[index], 6)
flexCell.setTextColor(R.color.text_secondary.toColor(requireContext()))
flexCell.setPadding(8F.dip2px(), 0, 8F.dip2px(), 0)
flexCell.background = if (mIsDarkModeOn) GradientDrawable().apply {
setStroke(0.5F.dip2px(), Color.parseColor("#21FFFFFF"))
@ -406,30 +397,6 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
}
}
private fun createSmartHotTagStyle(flexCell: TextView, name: String) {
flexCell.setDrawableStart(R.drawable.ic_smart_search)
flexCell.compoundDrawablePadding = 4F.dip2px()
flexCell.gravity = Gravity.CENTER_VERTICAL
flexCell.typeface = Typeface.DEFAULT_BOLD
flexCell.textSize = 12F
flexCell.text = StringUtils.shrinkStringWithDot(name, 6)
flexCell.setTextColor(Color.WHITE)
val colors =
intArrayOf(R.color.text_FFB749.toColor(requireContext()), R.color.text_FF6D3C.toColor(requireContext()))
val position = floatArrayOf(0F, 1F)
val linearGradient = LinearGradient(
0F,
0F,
flexCell.paint.textSize * flexCell.text.length,
0F,
colors,
position,
Shader.TileMode.CLAMP
)
flexCell.paint.shader = linearGradient
flexCell.invalidate()
}
private fun postExposureEvent(index: Int) {
mRankList?.safelyGetInRelease(index)?.content?.forEach {
if (it.link.type == "game") {
@ -444,17 +411,24 @@ open class SearchDefaultFragment : BaseFragment<Any>() {
super.onDarkModeChanged()
initHeadView()
mBinding.rootContainer.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
mBinding.hotList.adapter?.run {
mBinding.searchDiscoveryList.adapter?.run {
notifyItemRangeChanged(0, itemCount)
}
mViewModel?.historySearchLiveData?.value?.let { updateHistorySearchView(it) }
createFlexContent(mBinding.hotTagFlex, getTagListString(), true, clickListener = mHotTagClickListener)
createFlexContent(mBinding.searchDiscoveryTagFlex, getTagListString(), clickListener = mSearchDiscoveryTagClickListener)
}
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(requireContext().applicationContext)
protected open fun provideAdapter(pageRatio: Float): PagerAdapter =
SearchDefaultRankListAdapter(requireContext(), mRankList!!, pageRatio, mIsGameSearch, mSourceEntrance)
SearchDefaultRankListAdapter(
requireContext(),
mViewModel!!,
mRankList!!,
pageRatio,
mIsGameSearch,
mSourceEntrance
)
private fun provideViewModel(): SearchDefaultViewModel {
val factory = SearchDefaultViewModel.Factory(provideDao())

View File

@ -6,23 +6,27 @@ import android.view.ViewGroup
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.SearchDefaultRankItemBinding
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.utils.Util_System_Keyboard
import org.greenrobot.eventbus.EventBus
import org.json.JSONException
import org.json.JSONObject
class SearchDefaultRankAdapter(
context: Context,
private val mViewModel: SearchDefaultViewModel,
private val mRankList: SettingsEntity.Search.RankList,
private val mIsGameSearch: Boolean,
private val mIsGameSearch: Boolean
) : BaseRecyclerAdapter<SearchDefaultRankAdapter.SearchDefaultRankItemViewHolder>(context) {
override fun getItemCount() = minOf(mRankList.content.size, 10)
override fun getItemCount() = minOf(mRankList.content.size, 20)
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
SearchDefaultRankItemViewHolder(parent.toBinding())
@ -34,7 +38,8 @@ class SearchDefaultRankAdapter(
icon.goneIf(!mRankList.isShowIcon) {
icon.display(if (rank.link.type == "game") gameEntity?.icon else rank.icon)
}
name.text = if (rank.link.type == "game") gameEntity?.name else rank.name.ifBlank { rank.link.text }
val labelName = if (rank.link.type == "game") gameEntity?.name else rank.name.ifBlank { rank.link.text }
name.text = labelName
index.run {
typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH)
text = (position + 1).toString()
@ -60,18 +65,31 @@ class SearchDefaultRankAdapter(
root.setOnClickListener {
val linkEntity = rank.link
DirectUtils.directToLinkPage(
mContext,
linkEntity,
"游戏搜索-搜索榜单",
"${mRankList.title}-${rank.name}",
rank.exposureEvent
)
if (linkEntity.type == "game_search") {
mViewModel.add(linkEntity.text ?: "")
EventBus.getDefault().post(EBSearch(SearchType.RANK.value, linkEntity.text))
} else {
DirectUtils.directToLinkPage(
mContext,
linkEntity,
"游戏搜索-搜索榜单",
"${mRankList.title}-${rank.name}",
rank.exposureEvent
)
}
Util_System_Keyboard.hideSoftKeyboardByIBinder(mContext, it.windowToken)
// 是否来源于游戏搜索
if (mIsGameSearch) {
val searchType = if (linkEntity.type == "game_search") {
SearchType.RANK.toChinese()
} else {
""
}
NewFlatLogUtils.logSearchClickRankDetail(
linkEntity.link ?: "",
searchType,
rank.name,
(position + 1).toString(),
linkEntity.link ?: "",
@ -89,6 +107,8 @@ class SearchDefaultRankAdapter(
val trackEvent = JSONObject()
try {
trackEvent.put("text", labelName)
trackEvent.put("search_content", linkEntity.link)
trackEvent.put("game_name", gameEntity?.name)
trackEvent.put("game_id", gameEntity?.id)
trackEvent.put("list_name", mRankList.title)

View File

@ -11,6 +11,7 @@ import com.gh.gamecenter.feature.entity.SettingsEntity
class SearchDefaultRankListAdapter(
private val mContext: Context,
private val mViewModel: SearchDefaultViewModel,
private val mRankList: List<SettingsEntity.Search.RankList>,
private val mPageWidth: Float,
private val mIsGameSearch: Boolean,
@ -31,7 +32,7 @@ class SearchDefaultRankListAdapter(
view!!.findViewById<RecyclerView>(R.id.rankContainer).run {
layoutManager = LinearLayoutManager(mContext)
adapter = SearchDefaultRankAdapter(mContext, mRankList[position], mIsGameSearch)
adapter = SearchDefaultRankAdapter(mContext, mViewModel, mRankList[position], mIsGameSearch)
}
return view
}

View File

@ -10,7 +10,7 @@ class SearchDefaultViewModel(private val dao: ISearchHistoryDao) : ViewModel() {
val historySearchLiveData = MutableLiveData<List<String>>()
var isExistHotSearch: Boolean = false
var isExistHotTag: Boolean = false
var isExistSearchDiscoveryTag: Boolean = false
var isExistHistory: Boolean = false
var isExistRankList: Boolean = false

View File

@ -76,7 +76,7 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc
val exposureSources = arrayListOf<ExposureSource>().apply {
add(ExposureSource("首页搜索"))
add(ExposureSource(type, key))
add(ExposureSource("专题", entity.name))
add(ExposureSource("专题", "${entity.name}+${entity.columnId}"))
}
val exposureEvent = ExposureEvent.createEvent(game, exposureSources)
exposureList.add(exposureEvent)

View File

@ -68,6 +68,12 @@ class SearchGameResultAdapter(
val positionAndPackageMap = HashMap<String, Int>()
private val adIdSet = hashSetOf<String>() // 记录展示过的广告id
fun clearAdIdSet() {
adIdSet.clear()
}
override fun setListData(updateData: MutableList<SearchItemData>?) {
exposureEventArray = SparseArray(updateData?.size ?: 0)
// 记录游戏位置
@ -216,6 +222,7 @@ class SearchGameResultAdapter(
is SearchGameAdItemViewHolder -> {
val adEntity = mEntityList[position].ad
val adConfig = mEntityList[position].adConfig
val slotId = adEntity?.slotId ?: ""
val adContainer = holder.binding.adContainer
val screenWidthInDp = DisplayUtils.getScreenWidthInDp(fragment.activity)
@ -224,7 +231,36 @@ class SearchGameResultAdapter(
// 广告 slotId 没有变,不管它
} else {
adContainer.tag = slotId
AdDelegateHelper.requestThirdPartyFlowAd(fragment, slotId, adContainer, screenWidthInDp) {
val onAdShowAction: () -> Unit = {
if (!adIdSet.contains(adConfig?.id)) {
SensorsBridge.trackEvent("ThirdPartyAdShow",
json {
"ad_source" to adEntity?.sourceName
"ad_id" to slotId
"ad_format" to adConfig?.typeChinese
"ad_placement" to "搜索结果"
"ad_space_id" to adConfig?.id
"ad_space_name" to adConfig?.name
"position" to adConfig?.position
}
)
}
adIdSet.add(adConfig?.id ?: "")
}
val onAdClickAction = {
SensorsBridge.trackEvent("ThirdPartyAdClick",
json {
"ad_source" to adEntity?.sourceName
"ad_id" to slotId
"ad_format" to adConfig?.typeChinese
"ad_placement" to "搜索结果"
"ad_space_id" to adConfig?.id
"ad_space_name" to adConfig?.name
"position" to adConfig?.position
}
)
}
AdDelegateHelper.requestThirdPartyFlowAd(fragment, slotId, adContainer, screenWidthInDp, onAdShowAction, onAdClickAction) {
}
}

View File

@ -354,6 +354,7 @@ open class SearchGameResultFragment : ListFragment<GameEntity, SearchGameResultV
this.mKey = key
this.mType = type
mAdapter?.key = key
mAdapter?.clearAdIdSet()
mListViewModel?.updateSearchKeyWithType(key, type)
mListViewModel?.clearSearchSubjects()
mListViewModel?.load(LoadType.REFRESH)

View File

@ -188,7 +188,7 @@ class SearchGameResultViewModel(
list: List<GameEntity>
) {
thirdPartyAdList.forEach {
decoratedItemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd))
decoratedItemDataList.add(it.position - 1, SearchItemData(ad = it.thirdPartyAd, adConfig = it))
SPUtils.setLong(Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + it.position, System.currentTimeMillis())
}
postResultList(decoratedItemDataList, list)
@ -209,7 +209,7 @@ class SearchGameResultViewModel(
if ((showThirdPartyAd && adConfig?.thirdPartyAd != null)
|| (showOwnerAd && adConfig?.ownerAd == null && adConfig?.thirdPartyAd != null && showOnFailed)
) {
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd))
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
SPUtils.setLong(
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
System.currentTimeMillis()
@ -254,7 +254,7 @@ class SearchGameResultViewModel(
)
} else if (showOnFailed && adConfig.thirdPartyAd != null) {
// 自有广告为空时,显示第三方广告
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd))
decoratedItemDataList.add(position - 1, SearchItemData(ad = adConfig.thirdPartyAd, adConfig = adConfig))
SPUtils.setLong(
Constants.SP_LAST_GAME_SEARCH_AD_SHOW_TIME + adConfig.position,
System.currentTimeMillis()

View File

@ -9,20 +9,18 @@ import android.view.Gravity
import android.view.View
import android.widget.FrameLayout
import android.widget.LinearLayout
import android.widget.PopupWindow
import android.widget.TextView
import androidx.core.view.isVisible
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.BugFixedPopupWindow
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.doOnEnd
import com.gh.gamecenter.core.utils.doOnStart
import com.gh.gamecenter.databinding.FragmentGameServerTestV2Binding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingBinding
import com.gh.gamecenter.databinding.LayoutGameServerTestV2SettingGuideBinding
import com.gh.gamecenter.feature.entity.PageLocation
import com.gh.gamecenter.mygame.MyGameActivity
@ -70,7 +68,7 @@ class GameServerTestV2Fragment : LazyFragment() {
if (SPUtils.getBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, true)) {
mBaseHandler.post {
showSettingView {
showGuidePopupWindow()
showGuide()
}
}
SPUtils.setBoolean(Constants.SP_SHOW_GAME_SERVER_TEST_V2_SETTING_GUIDE, false)
@ -293,6 +291,19 @@ class GameServerTestV2Fragment : LazyFragment() {
}.start()
}
private fun showGuide() {
mBinding?.settingGuideContainer?.setOnClickListener {
dismissGuide()
}
mBinding?.settingGuideContainer?.isVisible = true
}
private fun dismissGuide() {
mBinding?.settingGuideIv?.animate()?.alpha(0F)?.setDuration(200L)?.doOnEnd {
mBinding?.settingGuideContainer?.isVisible = false
}?.start()
}
private fun getItemTextView(type: String): TextView {
return TextView(requireContext()).apply {
text = type
@ -332,22 +343,11 @@ class GameServerTestV2Fragment : LazyFragment() {
.commitAllowingStateLoss()
}
private fun showGuidePopupWindow(): PopupWindow {
val guideBinding = LayoutGameServerTestV2SettingGuideBinding.inflate(layoutInflater)
return BugFixedPopupWindow(
guideBinding.root,
FrameLayout.LayoutParams.WRAP_CONTENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
isFocusable = true
isTouchable = true
isOutsideTouchable = true
animationStyle = R.style.popup_window_ease_in_and_out_anim_style
showAsDropDown(mBinding?.optionIv, 0, (-4F).dip2px())
}
}
override fun onBackPressed(): Boolean {
if (mBinding?.settingGuideContainer?.isVisible == true) {
dismissGuide()
return true
}
if (mSettingBinding != null) {
dismissSettingView()
return true

View File

@ -87,6 +87,11 @@ abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
protected fun initViewPager() {
mViewPager?.run {
isUserInputEnabled = false
// 去掉默认动画
setPageTransformer { page, _ ->
page.translationX = 0F
page.alpha = 1F
}
adapter = provideAdapter()
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {

View File

@ -14,32 +14,45 @@ import com.gh.gamecenter.fragment.ReloadFragment
class MainFragmentStateAdapter(private val mFragment: Fragment) : BaseDiffFragmentStateAdapter<BottomTab>(mFragment) {
override fun createFragment(data: BottomTab?, position: Int): Fragment {
if (data != null) {
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
if (data.link == null) return ReloadFragment()
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, arrayListOf(ExposureSource("底部tab", data.name)))
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return when (data.link.type) {
if (data == null) return ReloadFragment()
val bundle = Bundle()
val superiorChain = if (mFragment is ISuperiorChain) mFragment else null
mFragment.arguments?.let { bundle.putAll(it) }
bundle.putBoolean(EntranceConsts.KEY_IS_HOME, true)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_ID, data.id)
bundle.putString(EntranceConsts.KEY_BOTTOM_TAB_NAME, data.name)
bundle.putInt(EntranceConsts.KEY_POSITION, position)
val exposureSourceList = arrayListOf(ExposureSource("底部tab", data.name))
data.diverter?.let {
exposureSourceList.add(
ExposureSource(
"分流器",
"${it.diverterData.diverterName}+${it.diverterData.diverterId}"
)
)
}
bundle.putParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST, exposureSourceList)
bundle.putBoolean(EntranceConsts.KEY_IS_FROM_MAIN_WRAPPER, true)
return if (data.link == null) {
ReloadFragment()
} else {
when (data.link!!.type) {
ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE -> {
bundle.putParcelable(LinkEntity::class.java.simpleName, data.link)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV -> {
bundle.putParcelable(BottomTab.SearchStyle::class.java.simpleName, data.searchStyle)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link.text)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, data.link!!.link)
bundle.putString(EntranceConsts.KEY_MULTI_TAB_NAV_NAME, data.link!!.text)
SearchToolbarTabWrapperFragment().setSuperiorChain(superiorChain).apply { arguments = bundle }
}
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link, false)
else -> ViewPagerFragmentHelper.createFragment(mFragment, bundle, data.link!!, false)
}
} else {
return ReloadFragment()
}
}

View File

@ -80,8 +80,11 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
initBottomTab(mBottomTabList)
showBottomTabGuideIfExists()
mAdapter.submitList(mBottomTabList)
val defaultBottomTabIndex = mViewModel!!.defaultBottomTabIndex
val defaultBottomTab = mBottomTabList.getOrNull(defaultBottomTabIndex) ?: return@observe
mViewPager?.doOnNextLayout {
setCurrentItem(mViewModel!!.defaultBottomTabIndex)
mViewModel?.handleBypassVisit(defaultBottomTab)
setCurrentItem(defaultBottomTabIndex)
}
}
}
@ -213,6 +216,9 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
if (bottomTab?.guide != null) {
dismissBottomTabGuide()
}
bottomTab?.let {
mViewModel?.handleBypassVisit(it)
}
playTabAnimation(toCheck)
changeBottomTabStyle(toCheck)
EventBus.getDefault().post(EBReuse(Constants.FINISH_PULL_DOWN_PUSH))

View File

@ -3,14 +3,19 @@ package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import androidx.lifecycle.MutableLiveData
import com.gh.common.util.HomeBottomBarHelper
import com.gh.common.util.HomeBottomBarHelper.isDefaultHomeBottomTabDataExist
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.LaunchRedirect
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.SingletonHolder
import com.gh.gamecenter.core.utils.TimeUtils
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DataUnionEntity
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.entity.MultiTabNav
import com.gh.gamecenter.home.custom.model.CustomPageData
import com.gh.gamecenter.retrofit.RetrofitManager
@ -21,6 +26,8 @@ import kotlinx.coroutines.delay
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import java.util.concurrent.TimeoutException
class MainWrapperRepository {
private val mNewApi = RetrofitManager.getInstance().newApi
@ -46,9 +53,17 @@ class MainWrapperRepository {
val customPageLiveData = MutableLiveData<CustomPageData?>()
val errorLiveData = MutableLiveData<Exception>()
val diverterList = arrayListOf<DiverterEntity>()
private val mTabSelectEventFlow = MutableSharedFlow<MainSelectedEvent>()
val tabSelectEventFlow = mTabSelectEventFlow as SharedFlow<MainSelectedEvent>
fun init() {
// 若 timeout 后数据未加载完成,则即便还没有回调也使用默认数据生成底部 tab
emitDefaultTabDataAfterTimeout()
getBypassList()
}
/**
* 发送首次启动跳转事件的选中事件
*/
@ -133,16 +148,37 @@ class MainWrapperRepository {
}
}
@SuppressLint("CheckResult")
fun getBypassList() {
val isInstallFirstAccess = TimeUtils.isToday(SPUtils.getLong(Constants.SP_BRAND_NEW_FIRST_LAUNCH_TIME) / 1000).toString()
mNewApi.getDiverterList(isInstallFirstAccess, BYPASS_TYPE_BOTTOM_TAB)
.subscribeOn(Schedulers.io())
.timeout(BYPASS_TIME_OUT, TimeUnit.MILLISECONDS)
.subscribe(object : BiResponse<List<DiverterEntity>>() {
override fun onSuccess(data: List<DiverterEntity>) {
diverterList.clear()
diverterList.addAll(data)
getDataUnion()
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
getDataUnion()
val reasonString = if (exception is TimeoutException) "分流判断超时" else "分流页面返回为空"
SensorsBridge.trackFailByPass("底部tab", "", "", reasonString)
NewFlatLogUtils.logFailByPass("底部tab", "", "", reasonString)
}
})
}
@SuppressLint("CheckResult")
fun getDataUnion() {
// 若历史 tab 数据存在,优先使用作为占位
if (isDefaultHomeBottomTabDataExist()) {
processBottomTabData(emptyList())
} else {
// 若 timeout 后数据未加载完成,则即便还没回调 onFailure 也生成两个底部 tab
emitDefaultTabDataAfterTimeout()
var diverter = ""
diverterList.forEach {
diverter += "${it.moduleIndex}:${it.diverterData.branchId},"
}
mNewApi.dataUnion
mNewApi.getDataUnion(diverter)
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<DataUnionEntity>() {
override fun onSuccess(data: DataUnionEntity) {
@ -163,7 +199,9 @@ class MainWrapperRepository {
private fun processBottomTabData(bottomTabList: List<BottomTab>) {
if (bottomTabList.isNotEmpty()) {
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
diverterList.forEach {
bottomTabList.getOrNull(it.moduleIndex)?.diverter = it
}
var preSelectedTab: BottomTab? = null
@ -206,11 +244,16 @@ class MainWrapperRepository {
}
}
HomeBottomBarHelper.updateDefaultHomeBottomTabData(bottomTabList)
bottomTabListLiveData.postValue(bottomTabList)
defaultNavId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = bottomTabList.find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
} else {
HomeBottomBarHelper.getDefaultHomeBottomTabData().run {
val defaultIndex = indexOfFirst { it.default }
if (defaultIndex != -1) {
defaultBottomTabIndex = defaultIndex
}
bottomTabListLiveData.postValue(this)
defaultNavId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_MULTI_TAB_NAV && it.default }?.link?.link ?: ""
defaultCustomPageId = find { it.link?.type == ViewPagerFragmentHelper.TYPE_CUSTOM_PAGE && it.default }?.link?.link ?: ""
@ -248,7 +291,10 @@ class MainWrapperRepository {
}
}
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() })
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() }) {
private const val BYPASS_TIME_OUT = 1000L
const val BYPASS_TYPE_BOTTOM_TAB = "bottom_tab"
}
}
sealed class MainSelectedEvent {

View File

@ -1,15 +1,22 @@
package com.gh.gamecenter.wrapper
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.*
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.RealNameHelper
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toRequestBody
import com.gh.gamecenter.entity.BottomTab
import com.gh.gamecenter.entity.DiverterEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.lightgame.utils.Utils
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.flow.map
@ -23,6 +30,7 @@ import retrofit2.HttpException
class MainWrapperViewModel(application: Application, private val mRepository: MainWrapperRepository) :
AndroidViewModel(application) {
private val mApi = RetrofitManager.getInstance().api
private val mNewApi = RetrofitManager.getInstance().newApi
val defaultBottomTabIndex
get() = mRepository.defaultBottomTabIndex
@ -45,6 +53,8 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
val bottomDoubleTabAction = MutableLiveData<BottomTab>()
private val byPassVisitBottomTabIdSet = hashSetOf<String>()
/**
* 请求各种弹窗的数据
*/
@ -91,6 +101,57 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
})
}
fun handleBypassVisit(bottomTab: BottomTab) {
if (byPassVisitBottomTabIdSet.contains(bottomTab.id)) return
bottomTab.diverter?.let {
val source = "底部tab${bottomTab.name}"
SensorsBridge.trackByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
NewFlatLogUtils.logByPassBrowsing(
source,
it.diverterData.diverterName,
it.diverterData.diverterId,
it.diverterData.branchId,
it.diverterData.branchName,
it.diverterData.inprocessTime,
it.diverterData.bypassVisitTime,
it.diverterData.linkType,
it.diverterData.linkId,
it.diverterData.linkText,
it.diverterData.bypassStatus,
)
patchBypassVisitTime(it)
byPassVisitBottomTabIdSet.add(bottomTab.id)
}
}
@SuppressLint("CheckResult")
private fun patchBypassVisitTime(diverterEntity: DiverterEntity) {
val data = mapOf(
"module_id" to diverterEntity.moduleId,
"diverter_id" to diverterEntity.diverterData.diverterId
)
mNewApi.patchDiverterVisitTime(MainWrapperRepository.BYPASS_TYPE_BOTTOM_TAB, data.toRequestBody())
.compose(singleToMain())
.subscribe({
Utils.log(TAG, "patchDiverterVisitTime onSuccess: ${it.string()}")
}, {
Utils.log(TAG, "patchDiverterVisitTime onError: ${it.message}")
})
}
class Factory(
private val mApplication: Application,
) : ViewModelProvider.NewInstanceFactory() {
@ -100,6 +161,7 @@ class MainWrapperViewModel(application: Application, private val mRepository: Ma
}
companion object {
private const val TAG = "MainWrapperViewModel"
const val SHOULD_SHOW_OPENING_DIALOG = "show_opening_dialog"
}
}

View File

@ -3,6 +3,7 @@ package com.gh.ndownload.suspendwindow
import android.app.Activity
import android.app.Application
import android.app.Application.ActivityLifecycleCallbacks
import android.os.Build
import android.os.Bundle
import androidx.core.view.ViewCompat
import com.gh.gamecenter.common.constant.Constants
@ -127,7 +128,7 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
SensorsBridge.trackDownloadSuspendedWindowGuideShow(
gameId = downloadEntity.gameId,
gameName = downloadEntity.name,
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
gameType = downloadEntity.categoryChinese,
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载"

View File

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

View File

@ -16,7 +16,7 @@ public class Constants {
public static final String PAGE_SOURCE_OTHERS = "其他";
public static final String PAGE_SOURCE_MY_GAME = "我的游戏";
public static final String PAGE_SOURCE_SHORTCUT = "桌面快捷启动";
public static final String PAGE_SOURCE_FLOATING_WINDOW = "加载完成悬浮";
public static final String PAGE_SOURCE_INSTALLED_LAUNCH_DIALOG = "安装完成提示启动弹";
public static final String KEY_EVENT = "event";
@ -27,5 +27,6 @@ public class Constants {
public static final String KEY_ERROR_CODE = "code";
public static final String LAUNCH_LOCATION_FLOATING_WINDOW = "畅玩启动悬浮窗";
public static final String LAUNCH_LOCATION_INSTALLED_LAUNCH_DIALOG = "安装完成提示启动弹窗";
}

View File

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

View File

@ -111,7 +111,7 @@ class VDownloadManagerFragment :
override fun onLoadRefresh() {
mReuseNoData?.visibility = View.GONE
mListViewModel.load(LoadType.REFRESH)
mViewModel.refresh()
}
override fun onDarkModeChanged() {

View File

@ -5,6 +5,7 @@ import androidx.annotation.WorkerThread
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.utils.toProperReadableSize
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
@ -12,10 +13,15 @@ import com.gh.gamecenter.core.utils.NumberUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.ApkEntity
import com.lightgame.download.DownloadStatus
import io.reactivex.Observable
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.subjects.PublishSubject
import org.greenrobot.eventbus.EventBus
import java.util.concurrent.TimeUnit
class VDownloadManagerViewModel(application: Application) :
ListViewModel<GameEntity, GameEntity>(application) {
@ -23,6 +29,19 @@ class VDownloadManagerViewModel(application: Application) :
var type = ""
var isFromHomeRecent = false
var loadPublishSubject: PublishSubject<LoadType> = PublishSubject.create()
val disposable: Disposable = loadPublishSubject.debounce(300, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe {
load(it)
}
fun refresh() {
loadPublishSubject.onNext(LoadType.REFRESH)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) {
mResultLiveData.postValue(it)
@ -88,7 +107,8 @@ class VDownloadManagerViewModel(application: Application) :
for (downloadEntity in downloadList) {
// 过滤下载完成的部分
if (downloadEntity.status == DownloadStatus.done
&& VHelper.getAllVGameSnapshots().any { it.packageName == downloadEntity.packageName }) {
&& VHelper.getAllVGameSnapshots().any { it.packageName == downloadEntity.packageName }
) {
continue
}
gameList.add(VHelper.toGameEntity(downloadEntity))
@ -124,15 +144,20 @@ class VDownloadManagerViewModel(application: Application) :
@WorkerThread
fun removeItems(idList: ArrayList<String>) {
val size = idList.size.coerceAtMost(99)
val packageList = arrayListOf<String>()
val apkList = arrayListOf<ApkEntity>()
for (id in idList) {
val apkEntity =
mResultLiveData.value?.firstOrNull { id == it.id }?.getApk()?.firstOrNull() ?: continue
apkList.add(apkEntity)
}
for (apkEntity in apkList) {
DownloadManager.getInstance().pause(apkEntity.url)
DownloadManager.getInstance().cancel(apkEntity.url)
packageList.add(apkEntity.packageName)
VHelper.uninstall(apkEntity.packageName)
@ -147,7 +172,7 @@ class VDownloadManagerViewModel(application: Application) :
PackageObserver.onPackageChanged(event)
}
ToastUtils.toast("已删除 $size 款游戏")
ToastUtils.toast("已删除 ${packageList.size} 款游戏")
}
}
@ -163,6 +188,12 @@ class VDownloadManagerViewModel(application: Application) :
return type == TYPE_DOWNLOADED
}
override fun onCleared() {
super.onCleared()
disposable.dispose()
}
companion object {
const val TYPE = "type"
const val TYPE_DOWNLOADED = "type_downloaded"

View File

@ -0,0 +1,276 @@
package com.gh.vspace
import android.app.Dialog
import android.app.DialogFragment
import android.app.FragmentManager
import android.graphics.drawable.ColorDrawable
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import androidx.recyclerview.widget.*
import androidx.viewpager2.widget.ViewPager2.OnPageChangeCallback
import androidx.viewpager2.widget.ViewPager2.PageTransformer
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.layoutInflater
import com.gh.gamecenter.common.view.PageControllerAdapter
import com.gh.gamecenter.common.view.PageTransformerAdapter
import com.gh.gamecenter.common.view.ScrollEventListener
import com.gh.gamecenter.databinding.DialogVgameInstalledLaunchBinding
import com.gh.gamecenter.databinding.ItemVgameInstalledLaunchBinding
import com.gh.gamecenter.feature.entity.InstallGameEntity
import com.gh.vspace.Constants.LAUNCH_LOCATION_INSTALLED_LAUNCH_DIALOG
import com.lightgame.download.DownloadEntity
class VGameInstalledLaunchDialog : DialogFragment() {
companion object {
private const val DIALOG_WIDTH = 300F
private const val VIEW_PAGER_WIDTH = 112F
private const val VIEW_PAGER_HEIGHT = 96F
private const val MIN_ITEM_SCALE_SIZE = 52F
private const val MAX_ITEM_SCALE_SIZE = 72F
private const val ITEM_SCALE_SIZE_INTERVAL = MAX_ITEM_SCALE_SIZE - MIN_ITEM_SCALE_SIZE
private const val MIN_ITEM_SCALE_HORIZONTAL_MARGIN = 8F
private const val MAX_ITEM_SCALE_HORIZONTAL_MARGIN = 20F
private const val ITEM_SCALE_HORIZONTAL_MARGIN_INTERVAL =
MAX_ITEM_SCALE_HORIZONTAL_MARGIN - MIN_ITEM_SCALE_HORIZONTAL_MARGIN
private const val TAG = "VGameInstalledLaunchDialog"
fun show(fragmentManager: FragmentManager, downloadEntity: DownloadEntity) {
val installedGame = VHelper.getAllInstalledVGameEntity().firstOrNull {
downloadEntity.gameId == it.gameId
} ?: return
val dialog = fragmentManager.findFragmentByTag(TAG) as VGameInstalledLaunchDialog?
if (dialog != null) {
dialog.addLaunchGame(installedGame)
} else {
VGameInstalledLaunchDialog().apply {
addLaunchGame(installedGame)
show(fragmentManager, TAG)
}
}
}
}
private val installAdapter: VInstalledGameListAdapter by lazy { VInstalledGameListAdapter() }
private var binding: DialogVgameInstalledLaunchBinding? = null
private fun addLaunchGame(installGameEntity: InstallGameEntity) {
val newInstalledList = (installAdapter.currentList + installGameEntity).sortedWith { o1, o2 ->
(o1.installTime - o2.installTime).toInt()
}
installAdapter.submitList(newInstalledList) {
updateGameDesc(newInstalledList.size)
}
}
@Deprecated("Deprecated in Java")
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
return super.onCreateDialog(savedInstanceState).apply {
window?.setBackgroundDrawable(ColorDrawable())
}
}
@Deprecated("Deprecated in Java")
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
return DialogVgameInstalledLaunchBinding.inflate(inflater, container, false)
.also { binding = it }
.root
}
@Deprecated("Deprecated in Java")
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
binding?.apply {
rv.apply {
val horizontalPadding = ((DIALOG_WIDTH - VIEW_PAGER_WIDTH) / 2).dip2px()
setPadding(horizontalPadding, 0, horizontalPadding, 0)
adapter = installAdapter
layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
val snapHelper = VInstalledPagerSnapHelper().also {
it.attachToRecyclerView(this)
}
ScrollEventListener(this).also {
val pageTransformerAdapter =
PageTransformerAdapter(it.mLayoutManager, VInstalledGameItemTransformer())
val pageControllerAdapter = PageControllerAdapter(this, snapHelper, it)
it.registerOnPageChangeCallback(pageControllerAdapter)
it.registerOnPageChangeCallback(pageTransformerAdapter)
it.registerOnPageChangeCallback(object : OnPageChangeCallback() {
override fun onPageSelected(position: Int) {
val currentList = installAdapter.currentList
gameName.text = currentList[position].gameName
}
})
installAdapter.setOnItemClickListener { _, i ->
pageControllerAdapter.currentItem = i
}
installAdapter.registerAdapterDataObserver(pageControllerAdapter.currentItemDataSetChangeObserver)
launch.setOnClickListener { view ->
val currentItem = pageControllerAdapter.currentItem
val installedGame = installAdapter.currentList[currentItem]
val vDownloadEntity =
VHelper.getVDownloadEntitySnapshot(installedGame.gameId, installedGame.packageName)
?: return@setOnClickListener
VHelper.installOrLaunch(
context = view.context,
downloadEntity = vDownloadEntity,
location = LAUNCH_LOCATION_INSTALLED_LAUNCH_DIALOG
)
NewFlatLogUtils.logHaloFunInstalledLaunchDialogClick(
gameId = installedGame.gameId,
gameName = installedGame.gameName ?: "",
buttonType = "启动游戏"
)
dismissAllowingStateLoss()
}
addOnScrollListener(it)
}
updateGameDesc(installAdapter.currentList.size)
}
NewFlatLogUtils.logHaloFunInstalledLaunchDialogShow()
}
}
private fun updateGameDesc(size: Int) {
binding?.gameDesc?.text = if (size == 1) {
getString(R.string.dialog_vgame_installed_launch_desc_1)
} else {
getString(R.string.dialog_vgame_installed_launch_desc_2, size)
}
}
class VInstalledGameListAdapter(
) : ListAdapter<InstallGameEntity, VInstalledGameItemViewHolder>(COMPARATOR) {
companion object {
private val COMPARATOR = object : DiffUtil.ItemCallback<InstallGameEntity>() {
override fun areItemsTheSame(oldItem: InstallGameEntity, newItem: InstallGameEntity): Boolean {
return oldItem.gameId == newItem.gameId
&& oldItem.gameName == newItem.gameName
&& oldItem.packageName == newItem.packageName
&& oldItem.gameIcon == newItem.gameIcon
}
override fun areContentsTheSame(oldItem: InstallGameEntity, newItem: InstallGameEntity): Boolean {
return oldItem.gameId == newItem.gameId
&& oldItem.gameName == newItem.gameName
&& oldItem.packageName == newItem.packageName
&& oldItem.gameIcon == newItem.gameIcon
}
}
}
private var onItemClickListener: ((View, Int) -> Unit)? = null
fun setOnItemClickListener(onItemClick: (View, Int) -> Unit) {
this.onItemClickListener = onItemClick
}
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): VInstalledGameItemViewHolder =
VInstalledGameItemViewHolder(
ItemVgameInstalledLaunchBinding.inflate(
parent.layoutInflater,
parent,
false
)
)
override fun onBindViewHolder(holder: VInstalledGameItemViewHolder, position: Int) {
// 指定图片的宽高,否则会图片变得很模糊
holder.binding.icon.setTag(ImageUtils.TAG_TARGET_WIDTH, MAX_ITEM_SCALE_SIZE.dip2px())
holder.binding.icon.displayGameIcon(getItem(position).gameIcon, null)
holder.binding.icon.setOnClickListener {
onItemClickListener?.invoke(it, position)
}
}
}
class VInstalledGameItemViewHolder(val binding: ItemVgameInstalledLaunchBinding) :
RecyclerView.ViewHolder(binding.root)
class VInstalledGameItemTransformer : PageTransformer {
override fun transformPage(page: View, position: Float) {
// 水平外边距会随着焦点位置距离变化而变化,取值范围(8-20)DP
val horizontalMargin: Int
// 图标大小会随着焦点位置距离变化而变化,取值范围(52-72)DP
val size = if (position == 0F) {// 处于焦点位置,保持最大值
horizontalMargin = MAX_ITEM_SCALE_HORIZONTAL_MARGIN.dip2px()
MAX_ITEM_SCALE_SIZE.dip2px()
} else if (position > 0F && position < 1F) {// 处于焦点与非焦点位置之间(右侧),动态计算大小
horizontalMargin =
(ITEM_SCALE_HORIZONTAL_MARGIN_INTERVAL * (1 - position) + MIN_ITEM_SCALE_HORIZONTAL_MARGIN).dip2px()
(ITEM_SCALE_SIZE_INTERVAL * (1 - position) + MIN_ITEM_SCALE_SIZE).dip2px()
} else if (position > -1F && position < 0F) {// 处于焦点与非焦点位置之间(左侧),动态计算大小
horizontalMargin =
(ITEM_SCALE_HORIZONTAL_MARGIN_INTERVAL * (1 + position) + MIN_ITEM_SCALE_HORIZONTAL_MARGIN).dip2px()
(ITEM_SCALE_SIZE_INTERVAL * (1 + position) + MIN_ITEM_SCALE_SIZE).dip2px()
} else {// 处于非焦点位置,保持最小值
horizontalMargin = MIN_ITEM_SCALE_HORIZONTAL_MARGIN.dip2px()
MIN_ITEM_SCALE_SIZE.dip2px()
}
// 垂直外边距
val verticalMargin = (VIEW_PAGER_HEIGHT.dip2px() - size) / 2
page.layoutParams = (page.layoutParams as RecyclerView.LayoutParams).apply {
width = size
height = size
leftMargin = horizontalMargin
rightMargin = horizontalMargin
topMargin = verticalMargin
bottomMargin = verticalMargin
}
}
}
class VInstalledPagerSnapHelper : PagerSnapHelper() {
override fun calculateDistanceToFinalSnap(
layoutManager: RecyclerView.LayoutManager,
targetView: View
): IntArray = intArrayOf(distanceToCenter(layoutManager, targetView), 0)
private fun distanceToCenter(layoutManager: RecyclerView.LayoutManager, targetView: View): Int {
val params = targetView.layoutParams as RecyclerView.LayoutParams
val decoratedStart = layoutManager.getDecoratedLeft(targetView) - params.leftMargin
// 与自带的PagerSnapHelper的区别在这里移动的距离必须是图标的最大值否则会onPageSelected会出现重复回调的问题
val decoratedMeasurement = MAX_ITEM_SCALE_SIZE.dip2px() + params.leftMargin + params.rightMargin
val childCenter = decoratedStart + decoratedMeasurement / 2
val totalSpace = layoutManager.width - layoutManager.paddingLeft - layoutManager.paddingRight
val startAfterPadding = layoutManager.paddingLeft
val containerCenter = startAfterPadding + totalSpace / 2
return childCenter - containerCenter
}
}
}

View File

@ -10,6 +10,7 @@ import android.database.sqlite.SQLiteFullException
import android.net.Uri
import android.os.Build
import android.text.TextUtils
import android.util.Log
import android.view.View
import android.widget.LinearLayout
import android.widget.TextView
@ -86,13 +87,14 @@ import com.lightgame.utils.Utils
import com.lightgame.view.CheckableLinearLayout
import io.reactivex.Completable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import org.greenrobot.eventbus.EventBus
import java.io.File
import java.util.*
import java.util.concurrent.CopyOnWriteArrayList
import java.util.concurrent.TimeUnit
import java.util.regex.Pattern
object VHelper {
@ -500,11 +502,13 @@ object VHelper {
fun getAllInstalledVGameEntity(): ArrayList<InstallGameEntity> = getAllVGameSnapshots().map {
InstallGameEntity().apply {
gameId = it.downloadEntity.gameId
gameIcon = it.downloadEntity.icon
gameName = it.downloadEntity.getMetaExtra(Constants.GAME_NAME)
gameVersion = it.downloadEntity.versionName
packageName = it.packageName
}
installTime = mInstalledInfoList.find { info ->
info.packageName == it.packageName }?.firstInstallTime ?: 0L }
}.toArrayList()
/**
@ -901,7 +905,10 @@ object VHelper {
Utils.log(LOG_TAG, "尝试安装新游戏 ${downloadEntity.path}")
// 如果一个游戏存在旧版畅玩助手内,此次安装就是游戏更新
val isLegacyGame = isLegacyInstalled(downloadEntity.packageName)
Utils.log(LOG_TAG, "${downloadEntity.packageName} ${if (isLegacyGame) "是旧畅玩助手的游戏" else "是新畅玩组件的游戏"}")
Utils.log(
LOG_TAG,
"${downloadEntity.packageName} ${if (isLegacyGame) "是旧畅玩助手的游戏" else "是新畅玩组件的游戏"}"
)
// 更新此包名对应的 gameId Map
mTempPackageNameAndGameIdMap[downloadEntity.packageName] = downloadEntity.gameId
@ -1144,7 +1151,7 @@ object VHelper {
}
)
runOnUiThread {
showFloatingWindow(downloadEntity)
showVGameInstalledDialog(downloadEntity)
}
mPackageInstalledLiveData.postValue(packageName)
} else {
@ -1398,9 +1405,18 @@ object VHelper {
VirtualAppManager.AIDL_SERVER_REMOTE_GUIDE_ACTIVITY
)
)
Config.getVNewSettingEntity()?.vaPlugin?.let {
intent.putExtra("pluginId", it.id)
intent.putExtra("pluginVersionName", it.versionName)
if (va.getPluginVersion().isEmpty()) {
val url = va.getDefaultPluginUrl()
if (url.isEmpty()) {
throw IllegalStateException("插件是必须配置的参数,请检查是否配置正确")
}
// 传递默认的插件信息
intent.putExtra("pluginUrl", url)
} else {
Config.getVNewSettingEntity()?.vaPlugin?.let {
intent.putExtra("pluginId", it.id)
intent.putExtra("pluginVersionName", it.versionName)
}
}
intent.putExtra(
KEY_LAUNCH_COUNT,
@ -1454,8 +1470,8 @@ object VHelper {
// 最近在玩
from = PAGE_SOURCE_RECENTLY_PLAYING
launchLocation = null
} else if(launchLocation == LAUNCH_LOCATION_FLOATING_WINDOW) {
from = PAGE_SOURCE_FLOATING_WINDOW
} else if(launchLocation == LAUNCH_LOCATION_INSTALLED_LAUNCH_DIALOG) {
from = PAGE_SOURCE_INSTALLED_LAUNCH_DIALOG
launchLocation = null
} else {
when (context) {
@ -1485,30 +1501,37 @@ object VHelper {
}
}
}
is VDownloadManagerActivity -> {
// 畅玩管理
from = PAGE_SOURCE_CW_MANAGER
}
is MyGameActivity -> {
// 我的游戏
from = PAGE_SOURCE_MY_GAME
}
is GameDetailActivity -> {
// 游戏详情
from = PAGE_SOURCE_GAME_DETAIL
}
is SearchActivity -> {
// 搜索
from = PAGE_SOURCE_SEARCH
}
is ColumnCollectionDetailActivity -> {
//非首页的排行榜
from = PAGE_SOURCE_RANK
}
is SubjectActivity -> {
// 专题
from = PAGE_SOURCE_TOPIC
}
is GameCollectionDetailActivity,
is ToolbarWrapperActivity,
is BlockActivity,
@ -1742,11 +1765,11 @@ object VHelper {
}
fun showFloatingWindow(downloadEntity: DownloadEntity) {
private fun showVGameInstalledDialog(downloadEntity: DownloadEntity) {
val topActivity = AppManager.getInstance().currentActivity() ?: return
if (topActivity.isFinishing || topActivity is VSpaceLoadingActivity) return
VLoadCompleteWindowHelper.showFloatingWindow(topActivity, toGameEntity(downloadEntity))
VGameInstalledLaunchDialog.show(topActivity.fragmentManager, downloadEntity)
}
/**
@ -2210,23 +2233,7 @@ object VHelper {
return sortedEntityList.take(8)
}
@SuppressLint("CheckResult")
fun preparePluginUpdate() {
Config.vNewSettingSubject.debounce(2, TimeUnit.SECONDS).doOnNext {
it?.vaPlugin?.let { vaPlugin ->
if (!vaPlugin.id.isNullOrEmpty()) {
if (!vaPlugin.url64.isNullOrEmpty()) {
downloadPlugin(id = "${vaPlugin.id}64", url = vaPlugin.url64)
}
if (!vaPlugin.url32.isNullOrEmpty()) {
downloadPlugin(id = "${vaPlugin.id}32", url = vaPlugin.url32)
}
}
}
}.subscribeOn(Schedulers.io()).subscribe({}, {})
}
private fun downloadPlugin(id: String, url: String) {
private fun downloadPlugin(id: String, url: String, pluginVersion: String, gameId: String) {
val pluginFileName = va.getPluginUpdateFileName(id)
val currentPluginFile = File(HaloApp.getInstance().cacheDir, pluginFileName)
if (currentPluginFile.exists() && va.isZipFile(currentPluginFile)) {
@ -2236,6 +2243,42 @@ object VHelper {
if (currentDownloadEntity != null) {
SimpleDownloadManager.cancel(id)
}
val downloadVaPluginBaseLogParams = va.getCwBaseLogParams(
gid = HaloApp.getInstance().gid,
oaid = HaloApp.getInstance().oaid,
hostVersion = com.gh.gamecenter.BuildConfig.VERSION_NAME,
hostChannel = HaloApp.getInstance().channel
)
DownloadMessageHandler.registerListener(id, object : DownloadListener {
override fun onError(error: DownloadError) {
DownloadMessageHandler.unregisterListener(id, this)
}
override fun onProgress(progress: Float) {
}
override fun onSizeReceived(fileSize: Long) {
}
override fun onStatusChanged(status: DownloadStatus) {
if (status == DownloadStatus.COMPLETED) {
DownloadMessageHandler.unregisterListener(id, this)
va.logBeforeGameLaunch(
mutableMapOf<String, String>().apply {
put(KEY_EVENT, "va_download_complete")
put(KEY_GAME_ID, gameId)
put("target_va_version", pluginVersion)
putAll(downloadVaPluginBaseLogParams)
}
)
}
}
override fun onSpeedChanged(speed: Float) {
}
})
SimpleDownloadManager.download(
DownloadConfigBuilder()
.setUniqueId(id)
@ -2248,31 +2291,20 @@ object VHelper {
.setDownloadExecutor(AppExecutor.ioExecutor)
.build()
)
DownloadMessageHandler.registerListener(id, object : DownloadListener {
override fun onError(error: DownloadError) {
va.logBeforeGameLaunch(
mutableMapOf<String, String>().apply {
put(KEY_EVENT, "va_download")
put(KEY_GAME_ID, gameId)
put("target_va_version", pluginVersion)
putAll(downloadVaPluginBaseLogParams)
}
override fun onProgress(progress: Float) {
}
override fun onSizeReceived(fileSize: Long) {
}
override fun onStatusChanged(status: DownloadStatus) {
if (status == DownloadStatus.COMPLETED) {
DownloadMessageHandler.unregisterListener(id, this)
}
}
override fun onSpeedChanged(speed: Float) {
}
})
)
}
}
fun updateAuthorizeInfo(isLogin: Boolean) {
if(!isVGameOn()) return
if (!isVGameOn()) return
runOnIoThread {
if (isLogin) {
val token = UserManager.getInstance().token
@ -2311,4 +2343,70 @@ object VHelper {
launchLocation = PAGE_SOURCE_SHORTCUT
}
/**
* 开始下载畅玩游戏时初始化Va插件
*/
fun initVaPlugin(gameId: String) {
Config.getVNewSettingObservable()
.take(1)
.observeOn(Schedulers.io())
.subscribe(object : io.reactivex.Observer<VNewSetting> {
override fun onSubscribe(d: Disposable) {
}
override fun onNext(vNewSetting: VNewSetting) {
val pluginVersion64 = va.getPluginVersion()
val onlinePluginVersionName = vNewSetting.vaPlugin?.versionName ?: ""
val defaultPluginUrl = va.getDefaultPluginUrl()
if (pluginVersion64.isEmpty() && onlinePluginVersionName.isEmpty()) {
// 不存在64位插件 并且 接口没有获取到插件信息
if (defaultPluginUrl.isNotEmpty()) {
// 下载默认的插件
downloadPlugin(
id = MD5Utils.getUrlMD5(defaultPluginUrl),
url = defaultPluginUrl,
pluginVersion = getPluginVersionInUrl(defaultPluginUrl),
gameId = gameId
)
}
}
vNewSetting.vaPlugin?.let { vaPlugin ->
if (!vaPlugin.id.isNullOrEmpty()) {
if (!vaPlugin.url64.isNullOrEmpty()) {
downloadPlugin(
id = "${vaPlugin.id}64",
url = vaPlugin.url64,
pluginVersion = vaPlugin.versionName ?: "",
gameId = gameId
)
}
if (!vaPlugin.url32.isNullOrEmpty()) {
downloadPlugin(
id = "${vaPlugin.id}32",
url = vaPlugin.url32,
pluginVersion = vaPlugin.versionName ?: "",
gameId = gameId
)
}
}
}
}
override fun onError(e: Throwable) {
}
override fun onComplete() {
}
})
}
private fun getPluginVersionInUrl(url: String): String {
val matcher = Pattern.compile("\\d+\\.\\d+\\.\\d+").matcher(url)
while (matcher.find()) {
return matcher.group()
}
return ""
}
}

View File

@ -29,6 +29,7 @@ import com.lzf.easyfloat.enums.SidePattern
import com.lzf.easyfloat.interfaces.OnFloatAnimator
import java.lang.ref.WeakReference
@Deprecated("use VGameInstalledLaunchDialog instead.")
object VLoadCompleteWindowHelper {
private const val LOAD_COMPLETE_WINDOW = "load_complete_window"
private const val LOOP_TIME = 2000L

View File

@ -369,7 +369,7 @@ public class HaloApp extends MultiDexApplication {
GlobalPriorityChainHelper.INSTANCE.preStart(isNewForThisVersion);
MainWrapperRepository.Companion.getInstance().getDataUnion();
MainWrapperRepository.Companion.getInstance().init();
// QQ小游戏开放互联初始化
IQGameProvider provider = (IQGameProvider) ARouter
@ -556,7 +556,6 @@ public class HaloApp extends MultiDexApplication {
LoadedApkHuaWei.hookHuaWeiVerifier(this);
DownloadMessageHandler.INSTANCE.init(SimpleDownloadDatabase.getInstance().downloadDao());
VHelper.INSTANCE.preparePluginUpdate();
// 初始化 WebView ABI 列表
getWebviewAbiList();

View File

@ -1,6 +1,5 @@
package com.halo.assistant.fragment.user
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -13,7 +12,6 @@ import com.gh.gamecenter.common.base.fragment.WaitingDialogFragment
import com.gh.common.util.*
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.core.utils.EmptyCallback
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.common.utils.enlargeTouchArea
@ -22,7 +20,11 @@ import com.gh.gamecenter.databinding.FragmentManuallyRealNameBinding
import com.gh.gamecenter.login.entity.IdCardEntity
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.utils.UploadImageUtils
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.squareup.picasso.MemoryPolicy
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import io.reactivex.disposables.Disposable
class ManuallyRealNameFragment : ToolbarFragment() {
@ -69,28 +71,10 @@ class ManuallyRealNameFragment : ToolbarFragment() {
if (data == null) return
if (requestCode == REQUEST_IMAGE) {
val selectedImage = data.data ?: return
val filePathColumn = arrayOf(MediaStore.Images.Media.DATA)
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
try {
val cursor = requireContext().contentResolver.query(
selectedImage,
filePathColumn,
null,
null,
null
) ?: return
cursor.moveToFirst()
val columnIndex = cursor.getColumnIndex(filePathColumn[0])
val picturePath = cursor.getString(columnIndex)
cursor.close()
uploadPicture(selectedImage, picturePath)
} catch (e: Exception) {
ToastUtils.showToast(e.localizedMessage ?: "")
}
uploadPicture(selectedPaths[0], picturePath)
}
}
@ -150,22 +134,13 @@ class ManuallyRealNameFragment : ToolbarFragment() {
}
private fun selectImage() {
try {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, REQUEST_IMAGE)
} catch (e: ActivityNotFoundException) {
// https://stackoverflow.com/questions/45707678
val getIntent = Intent(Intent.ACTION_GET_CONTENT)
getIntent.type = "image/*"
val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickIntent.type = "image/*"
val chooserIntent = Intent.createChooser(getIntent, "Select Image")
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(pickIntent))
startActivityForResult(chooserIntent, REQUEST_IMAGE)
}
val intent = LocalMediaActivity.getIntent(
requireContext(),
ChooseType.IMAGE,
1,
"人工审核"
)
startActivityForResult(intent, REQUEST_IMAGE)
}
private fun removeUploadedImage() {

View File

@ -2,10 +2,8 @@ package com.halo.assistant.fragment.user.avatar
import android.app.Activity
import android.app.Dialog
import android.content.ActivityNotFoundException
import android.content.Intent
import android.os.Bundle
import android.provider.MediaStore
import android.text.TextUtils
import android.view.Gravity
import android.view.LayoutInflater
@ -18,15 +16,18 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.feature.selector.LocalMediaActivity
import com.gh.gamecenter.common.utils.PermissionHelper.checkStoragePermissionBeforeAction
import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogChangeAvatarBinding
import com.gh.gamecenter.feature.selector.ChooseType
import com.gh.gamecenter.login.user.UserViewModel
import com.halo.assistant.HaloApp
import com.halo.assistant.fragment.user.UserPortraitCropImageActivity
import com.lightgame.utils.Utils
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.internal.utils.PathUtils
import org.json.JSONException
import org.json.JSONObject
@ -93,8 +94,8 @@ class ChangeAvatarDialog : BaseDialogFragment() {
if (resultCode == Activity.RESULT_OK && data != null) {
when (requestCode) {
REQUEST_MEDIA_ICON -> {
val selectedImage = data.data ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedImage)
val selectedPaths = Matisse.obtainResult(data) ?: return
val picturePath = PathUtils.getPath(requireContext(), selectedPaths[0])
Utils.log("picturePath = $picturePath")
// 上传头像
val intent = UserPortraitCropImageActivity.getIntent(
@ -144,12 +145,9 @@ class ChangeAvatarDialog : BaseDialogFragment() {
e.printStackTrace()
}
}
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
try {
startActivityForResult(intent, REQUEST_MEDIA_ICON)
} catch (e: ActivityNotFoundException) {
Utils.toast(requireContext(), "找不到图片选择器")
}
val intent = LocalMediaActivity.getIntent(requireContext(), ChooseType.IMAGE, 1, "头像选择")
startActivityForResult(intent, REQUEST_MEDIA_ICON)
}
override fun onDarkModeChanged() {

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 395 B

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