Compare commits

...

32 Commits

Author SHA1 Message Date
49a610deee feat: 动态java方法注册 2023-10-11 19:41:28 +08:00
61f43d53b2 feat: 文章详情WebView预加载 2023-10-11 15:52:35 +08:00
0dcf5d2097 Merge branch 'fix/GHZS-3596' into 'dev'
fix: 【光环助手】APP内图片显示问题 https://jira.shanqu.cc/browse/GHZS-3596

See merge request halo/android/assistant-android!1375
2023-10-09 15:08:09 +08:00
32e584c04c Merge remote-tracking branch 'origin/dev' into dev-5.32.0
# Conflicts:
#	dependencies.gradle
2023-10-09 10:45:04 +08:00
fbe29d82be fix: 【光环助手】APP内图片显示问题 https://jira.shanqu.cc/browse/GHZS-3596 2023-10-08 16:54:13 +08:00
3d4977dc87 Merge branch 'feature-GHZS-3462' into 'dev-5.32.0'
feat: 帖子加载问题优化—客户端 https://jira.shanqu.cc/browse/GHZS-3462

See merge request halo/android/assistant-android!1373
2023-10-08 16:14:05 +08:00
4d0fda6157 feat: 帖子加载问题优化—客户端 https://jira.shanqu.cc/browse/GHZS-3462 2023-10-08 16:14:05 +08:00
28e28d0775 feat: 畅玩游戏启动中转页面内容调整—客户端 https://jira.shanqu.cc/browse/CWZS-101 2023-09-26 09:33:09 +08:00
6f53a67554 feat: 畅玩游戏启动中转页面内容调整—客户端 https://jira.shanqu.cc/browse/CWZS-101 2023-09-26 09:33:09 +08:00
05268442f8 Merge branch 'fix/GHZS-3500' into 'dev-5.32.0'
fix: 求版本下载跳转问题 https://jira.shanqu.cc/browse/GHZS-2674

See merge request halo/android/assistant-android!1362
2023-09-20 10:42:45 +08:00
55521e6f29 fix: 求版本下载跳转问题 https://jira.shanqu.cc/browse/GHZS-2674 2023-09-20 10:42:45 +08:00
db5b09cc11 Merge branch 'feature-GHZS-3493' into 'dev-5.32.0'
fix: 插件求版本相关优化—0915测试 https://jira.shanqu.cc/browse/GHZS-3493

See merge request halo/android/assistant-android!1361
2023-09-19 10:51:12 +08:00
b0922b4c29 fix: 插件求版本相关优化—0915测试 https://jira.shanqu.cc/browse/GHZS-3493 2023-09-19 10:51:12 +08:00
b809ae6687 Merge branch 'fix/GHZS-3452' into 'dev-5.32.0'
fix: 开发者中心及其配套优化—0912测试(2) https://jira.shanqu.cc/browse/GHZS-3452

See merge request halo/android/assistant-android!1358
2023-09-15 09:42:17 +08:00
c575e589ae fix: 开发者中心及其配套优化—0912测试(2) https://jira.shanqu.cc/browse/GHZS-3452 2023-09-15 09:30:57 +08:00
2af47af681 Merge branch 'feat/GHZS-3437' into 'dev-5.32.0'
feat: 游戏下载增加文件格式 https://jira.shanqu.cc/browse/GHZS-3437

See merge request halo/android/assistant-android!1354
2023-09-14 17:12:49 +08:00
b1228bdd2a feat: 游戏下载增加文件格式 https://jira.shanqu.cc/browse/GHZS-3437 2023-09-14 17:12:49 +08:00
ffae0d1b1d Merge branch 'dev' into 'dev-5.32.0'
Dev

See merge request halo/android/assistant-android!1346
2023-09-12 13:59:06 +08:00
3fc1fd821a Merge branch 'feature/GHZS-3408' into 'dev-5.32.0'
feat: 开发者中心及配套优化—客户端 https://jira.shanqu.cc/browse/GHZS-3408

See merge request halo/android/assistant-android!1339
2023-09-11 09:55:00 +08:00
b3af44cc4c feat: 开发者中心及配套优化—客户端 https://jira.shanqu.cc/browse/GHZS-3408 2023-09-11 09:49:25 +08:00
dd7072eea4 Merge branch 'feature-GHZS-3412' into 'dev-5.32.0'
feat: 插件求版本相关优化—0905测试 https://jira.shanqu.cc/browse/GHZS-3412

See merge request halo/android/assistant-android!1337
2023-09-08 15:55:50 +08:00
4ee463f03e feat: 插件求版本相关优化—0905测试 https://jira.shanqu.cc/browse/GHZS-3412 2023-09-08 15:55:49 +08:00
16526ddfbd Merge branch 'feature/GHZS-3346' into 'dev-5.32.0'
feat: 插件管理功能优化(第2期)—客户端 https://jira.shanqu.cc/browse/GHZS-3346

See merge request halo/android/assistant-android!1334
2023-09-08 11:20:21 +08:00
d6e676300e feat: 插件管理功能优化(第2期)—客户端 https://jira.shanqu.cc/browse/GHZS-3346 2023-09-08 11:15:30 +08:00
7852972e01 feat: 插件管理功能优化(第2期)—客户端 https://jira.shanqu.cc/browse/GHZS-3346 2023-09-08 11:15:22 +08:00
80403e1684 Merge remote-tracking branch 'origin/dev' into dev-5.32.0 2023-09-07 10:30:53 +08:00
db7fbe057e Merge remote-tracking branch 'origin/dev' into dev-5.32.0 2023-09-05 14:35:39 +08:00
41340f7333 Merge branch 'feature-GHZS-3295' into 'dev-5.32.0'
feat: 插件求版本相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-3295

See merge request halo/android/assistant-android!1310
2023-09-01 11:45:18 +08:00
e73fc09d9b feat: 插件求版本相关优化—客户端 https://jira.shanqu.cc/browse/GHZS-3295 2023-09-01 11:35:50 +08:00
70e63ed8e3 Merge branch 'fix/GHZS-3286' into 'dev-5.32.0'
fix: 单机插件更新跳转—客户端 https://jira.shanqu.cc/browse/GHZS-3286

See merge request halo/android/assistant-android!1296
2023-08-30 11:32:38 +08:00
b7a36074ed fix: 单机插件更新跳转—客户端 https://jira.shanqu.cc/browse/GHZS-3286 2023-08-30 11:08:31 +08:00
141d179a64 chore: 版本个更新至 5.32.0 2023-08-29 14:12:07 +08:00
35 changed files with 618 additions and 179 deletions

View File

@ -71,7 +71,7 @@ android_build:
exit_codes: 137
only:
- dev
- dev-5.31.0
- dev-5.32.0
# 代码检查
sonarqube_analysis:
@ -102,7 +102,7 @@ sonarqube_analysis:
exit_codes: 137
only:
- dev
- dev-5.31.0
- dev-5.32.0
## 发送简易检测结果报告
send_sonar_report:
@ -120,7 +120,7 @@ send_sonar_report:
exit_codes: 137
only:
- dev
- dev-5.31.0
- dev-5.32.0
oss-upload&send-email:
tags:
@ -152,4 +152,4 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail.py
only:
- dev
- dev-5.31.0
- dev-5.32.0

View File

@ -275,7 +275,7 @@
<activity
android:name="com.gh.gamecenter.VoteActivity"
android:screenOrientation="portrait"
android:windowSoftInputMode="stateAlwaysHidden|adjustResize" />
android:windowSoftInputMode="stateAlwaysHidden|adjustPan" />
<activity
android:name="com.gh.gamecenter.WeiBoShareActivity"

View File

@ -352,9 +352,9 @@ RE.ImageClickListener = function() {
var img = imgs[i];
var imageClassName = img.className;
if (imageClassName == "image-link"|| img.className == "poster") continue;
window.imagelistener.imageArr(img.src);
window.NativeCallBack.invokeMethod("imageArr", img.src);
img.onclick = function() {
window.imagelistener.imageClick(this.src);
window.NativeCallBack.invokeMethod("imageClick", this.src);
}
}
}

View File

@ -60,6 +60,9 @@ object AdDelegateHelper {
private val mGameSearchAdList: ArrayList<AdConfig> by lazy { arrayListOf() }
private var mVGameLaunchAd: AdConfig? = null
val vGameLaunchAd: AdConfig?
get() = mVGameLaunchAd
private const val AD_SDK_CSJ = "穿山甲"
private const val AD_SDK_BEIZI = "倍孜"
private const val AD_TYPE_SDK = "third_party_ads" // 第三方 SDK 广告

View File

@ -58,7 +58,7 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori
val accelerateSet =
HashSet(SPUtils.getStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET))
if (!mGameList.isNullOrEmpty() && !accelerateSet.contains(mGameList!![0].messageId)) {
showAccelerateNotificationPopupWindow(mGameList!![0]) {
showAccelerateNotificationPopupWindow(mActivity!!, mViewModel, mBaseHandler, mGameList!![0]) {
processNext()
}
accelerateSet.add(mGameList!![0].messageId)
@ -74,54 +74,61 @@ class AccelerateNotificationHandler(priority: Int) : PriorityChainHandler(priori
}
}
private fun showAccelerateNotificationPopupWindow(gameEntity: GameEntity?, dismissCallback: () -> Unit) {
val binding: PopupAccelerateNotificationBinding =
PopupAccelerateNotificationBinding.inflate(LayoutInflater.from(mActivity))
if (gameEntity != null) {
binding.gameIconView.displayGameIcon(gameEntity)
binding.gameNameTv.text = gameEntity.name
binding.root.setOnClickListener {
NewFlatLogUtils.logMessageInformClickPluginVersion(
gameEntity.id,
gameEntity.name!!
)
if (gameEntity.messageId.isNotEmpty()) {
// 把对应系统消息设为已读
mViewModel?.postMessageRead(gameEntity.messageId)
companion object {
fun showAccelerateNotificationPopupWindow(
activity: Activity,
viewModel: MainWrapperViewModel?,
baseHandler: BaseFragment.BaseHandler?,
gameEntity: GameEntity?,
dismissCallback: (() -> Unit)?
) {
val binding: PopupAccelerateNotificationBinding =
PopupAccelerateNotificationBinding.inflate(LayoutInflater.from(activity))
if (gameEntity != null) {
binding.gameIconView.displayGameIcon(gameEntity)
binding.gameNameTv.text = gameEntity.name
binding.root.setOnClickListener {
NewFlatLogUtils.logMessageInformClickPluginVersion(
gameEntity.id,
gameEntity.name!!
)
if (gameEntity.messageId.isNotEmpty()) {
// 把对应系统消息设为已读
viewModel?.postMessageRead(gameEntity.messageId)
}
GameDetailActivity.startGameDetailActivity(
context = activity,
gameId = gameEntity.id,
entrance = "首页插件上架弹窗",
defaultTab = -1,
isSkipGameComment = false,
scrollToLibao = false,
openVideoStreaming = false,
openPlatformWindow = true,
traceEvent = null
)
}
GameDetailActivity.startGameDetailActivity(
context = mActivity!!,
gameId = gameEntity.id,
entrance = "首页插件上架弹窗",
defaultTab = -1,
isSkipGameComment = false,
scrollToLibao = false,
openVideoStreaming = false,
openPlatformWindow = true,
traceEvent = null
}
BugFixedPopupWindow(
binding.root,
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
isTouchable = true
isFocusable = true
isOutsideTouchable = true
animationStyle = R.style.popup_window_ease_in_and_out_anim_style
showAtLocation(
activity.window.decorView,
Gravity.TOP,
0,
DisplayUtils.getStatusBarHeight(activity.resources) + DisplayUtils.dip2px(42f)
)
setOnDismissListener {
dismissCallback?.invoke()
}
baseHandler?.postDelayed({ dismiss() }, 5000)
}
}
BugFixedPopupWindow(
binding.root,
FrameLayout.LayoutParams.MATCH_PARENT,
FrameLayout.LayoutParams.WRAP_CONTENT
).apply {
isTouchable = true
isFocusable = true
isOutsideTouchable = true
animationStyle = R.style.popup_window_ease_in_and_out_anim_style
showAtLocation(
mActivity!!.window.decorView,
Gravity.TOP,
0,
DisplayUtils.getStatusBarHeight(mActivity!!.resources) + DisplayUtils.dip2px(42f)
)
setOnDismissListener {
dismissCallback.invoke()
}
mBaseHandler?.postDelayed({ dismiss() }, 5000)
}
}
}

View File

@ -17,4 +17,6 @@ class PriorityChain {
mHandlerQueue.poll()?.process(mHandlerQueue)
}
fun isHandlerQueueEmpty() = mHandlerQueue.isEmpty()
}

View File

@ -15,18 +15,19 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.gh.gamecenter.common.base.activity.ToolBarActivity;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.MainActivity;
import com.gh.gamecenter.SplashScreenActivity;
import com.gh.gamecenter.common.avoidcallback.AvoidOnResultManager;
import com.gh.gamecenter.common.avoidcallback.Callback;
import com.gh.gamecenter.common.base.activity.ToolBarActivity;
import com.gh.gamecenter.common.base.fragment.ToolbarFragment;
import com.gh.gamecenter.common.constant.EntranceConsts;
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;
@ -66,8 +67,8 @@ public class EntranceUtils {
public static void jumpActivity(Context context, Bundle bundle) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (HaloApp.getInstance().isRunningForeground) {
// 应用正在运行,前台或后台
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;
@ -92,8 +93,8 @@ public class EntranceUtils {
public static void jumpActivity(Context context, Bundle nextToBundle, Bundle bundle, Callback callback) {
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
if (HaloApp.getInstance().isRunningForeground) {
// 应用正在运行,前台或后台
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;

View File

@ -11,9 +11,12 @@ import android.util.AttributeSet;
import android.view.Gravity;
import android.webkit.JavascriptInterface;
import android.webkit.WebChromeClient;
import android.webkit.WebResourceResponse;
import android.webkit.WebView;
import android.webkit.WebViewClient;
import androidx.annotation.Nullable;
import com.gh.common.util.PackageUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.Config;
@ -40,8 +43,10 @@ import java.io.UnsupportedEncodingException;
import java.net.URLDecoder;
import java.net.URLEncoder;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Locale;
import java.util.Map;
/**
* Copyright (C) 2017 Wasabeef
@ -74,6 +79,8 @@ public class RichEditor extends WebView {
private EmptyCallback mInitialLayoutCallback;
private Map<String, DynamicJsInterface> mDynamicJsInterfaces = new HashMap<>();
private String mCurrentContent = "";
public enum Type {
@ -122,6 +129,7 @@ public class RichEditor extends WebView {
private OnDecorationStateListener mDecorationStateListener;
private AfterInitialLoadListener mLoadListener;
private WebChromeClientListener mChromeClientListener;
private WebResourceRequestInterceptor mWebResourceRequestInterceptor;
private PageFinishedListener mPageFinishedListener;
public RichEditor(Context context) {
@ -179,6 +187,10 @@ public class RichEditor extends WebView {
mChromeClientListener = chromeClientListener;
}
public void setWebResourceRequestInterceptor(WebResourceRequestInterceptor interceptor) {
mWebResourceRequestInterceptor = interceptor;
}
public void setPageFinishedListener(PageFinishedListener pageFinishedListener) {
mPageFinishedListener = pageFinishedListener;
}
@ -597,6 +609,14 @@ public class RichEditor extends WebView {
exec("javascript:RE.formatBlock();");
}
public void registerDynamicJsInterface(String method, DynamicJsInterface jsInterface) {
mDynamicJsInterfaces.put(method, jsInterface);
}
public void unregisterDynamicJsInterface(String method) {
mDynamicJsInterfaces.remove(method);
}
/**
* 调用 JS 方法,告诉网页端该 url 对应视频播放的进度
*/
@ -683,6 +703,16 @@ public class RichEditor extends WebView {
return super.shouldOverrideUrlLoading(view, url);
}
@Nullable
@Override
public WebResourceResponse shouldInterceptRequest(WebView view, String url) {
if (mWebResourceRequestInterceptor != null) {
return mWebResourceRequestInterceptor.shouldInterceptRequest(view, url);
}
return super.shouldInterceptRequest(view, url);
}
}
public void setContentOwner(boolean contentOwner) {
@ -699,6 +729,10 @@ public class RichEditor extends WebView {
boolean shouldOverrideUrlLoading(WebView view, String url);
}
public interface WebResourceRequestInterceptor {
WebResourceResponse shouldInterceptRequest(WebView view, String url);
}
public interface PageFinishedListener {
void onPageFinished();
}
@ -840,6 +874,14 @@ public class RichEditor extends WebView {
public void logMtaEvent(String event) {
// do nothing, mta is deprecated
}
@JavascriptInterface
public void invokeMethod(String method, String data) {
DynamicJsInterface jsInterface = mDynamicJsInterfaces.get(method);
if (jsInterface != null) {
jsInterface.invoke(data);
}
}
}
@Override
@ -852,4 +894,8 @@ public class RichEditor extends WebView {
mInitialLayoutCallback = null;
}
}
public interface DynamicJsInterface {
void invoke(String data);
}
}

View File

@ -2,6 +2,7 @@ package com.gh.download.dialog
import android.annotation.SuppressLint
import android.content.Context
import android.os.Build
import android.os.Bundle
import android.view.LayoutInflater
import android.view.View
@ -27,6 +28,7 @@ import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.TimeElapsedHelper
import com.gh.gamecenter.databinding.DialogDownloadBinding
import com.gh.gamecenter.entity.GamePlatform
import com.gh.gamecenter.feature.entity.ApkEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.eventbus.EBPackage
@ -50,8 +52,7 @@ class DownloadDialog : BaseDraggableDialogFragment() {
private var mAdapter: DownloadDialogAdapter? = null
private var mTraceEvent: ExposureEvent? = null
private var mPlatformName: String = ""
private var mPackageName: String = ""
private var mPlatformList: List<GamePlatform>? = null
private var mParentBundle: Bundle? = null
// 合集页面保持和后台一样的顺序
@ -97,8 +98,11 @@ class DownloadDialog : BaseDraggableDialogFragment() {
super.onCreate(savedInstanceState)
mParentBundle = requireActivity().intent.extras?.getBundle(ToolBarActivity.NORMAL_FRAGMENT_BUNDLE)
mPlatformName = mParentBundle?.getString(EntranceConsts.KEY_PLATFORM, "") ?: ""
mPackageName = mParentBundle?.getString(EntranceConsts.KEY_PACKAGENAME, "") ?: ""
mPlatformList = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
mParentBundle?.getParcelableArrayList(EntranceConsts.KEY_PLATFORMS, GamePlatform::class.java)
} else {
mParentBundle?.getParcelableArrayList(EntranceConsts.KEY_PLATFORMS)
}
mGameEntity = requireArguments().getParcelable(GameEntity::class.java.simpleName)!!
mEntrance = requireArguments().getString(EntranceConsts.KEY_ENTRANCE) ?: ""
mLocation = requireArguments().getString(EntranceConsts.KEY_LOCATION) ?: ""
@ -172,28 +176,37 @@ class DownloadDialog : BaseDraggableDialogFragment() {
//自动下载
private fun performAutoDownload(itemList: List<DownloadDialogItemData>, recyclerView: RecyclerView) {
if (mPlatformName.isEmpty() && mPackageName.isEmpty()) return
val platformList = this.mPlatformList
if (platformList.isNullOrEmpty()) return
AppExecutor.uiExecutor.executeWithDelay({
recyclerView.adapter?.let {
for (i in 0 until it.itemCount) {
val apkEntity = itemList[i].normal ?: continue
val apkCollection = apkEntity.apkCollection
if (apkCollection != null) {
if (apkCollection.name == mPlatformName) {
val platformName = platformList[0].name
val packageName = platformList[0].packageName
if (apkCollection != null) {// 插件集合首先切换到集合列表,再自动下载插件
if (apkCollection.name == platformName) {// 第一个需要自动下载的插件名称与插件集合名称相同
scrollAndDownload(recyclerView, false, i)
break
} else {
apkCollection.saveApkEntity?.forEach { entity ->
if (entity.getPlatformName() == mPlatformName || entity.packageName == mPackageName) {
scrollAndDownload(recyclerView, false, i)
}
// 插件集合列表中存在需要自动下载的插件
val matched = apkCollection.saveApkEntity?.any { entity ->
entity.getPlatformName() == platformName || entity.packageName == packageName
}
if (matched == true) {
scrollAndDownload(recyclerView, false, i)
break
}
break
}
}
if (apkEntity.getPlatformName() == mPlatformName || apkEntity.packageName == mPackageName) {
// 自动下载符合条件的插件
val matchPlatformIndex = platformList.indexOfFirst { platform ->
platform.name == apkEntity.getPlatformName() || platform.packageName == apkEntity.packageName
}
if (matchPlatformIndex != -1) {
scrollAndDownload(recyclerView, true, i)
break
}
}
}

View File

@ -10,7 +10,9 @@ import com.gh.common.exposure.ExposureManager.log
import com.gh.common.exposure.ExposureTraceUtils.appendTrace
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.entity.GamePlatform
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent
@ -195,6 +197,7 @@ class GameDetailActivity : DownloadToolbarActivity() {
openPlatformWindow,
null,
null,
null,
traceEvent
)
}
@ -222,6 +225,7 @@ class GameDetailActivity : DownloadToolbarActivity() {
openPlatformWindow: Boolean = false,
platformName: String? = null,
packageName: String? = null,
platforms: List<GamePlatform>? = null,
traceEvent: ExposureEvent? = null
) {
val bundle = Bundle()
@ -261,11 +265,13 @@ class GameDetailActivity : DownloadToolbarActivity() {
}
if (openPlatformWindow) {
bundle.putBoolean(EntranceConsts.KEY_OPEN_PLATFORM_WINDOW, true)
if (!platformName.isNullOrEmpty()) {
bundle.putString(EntranceConsts.KEY_PLATFORM, platformName)
}
if (!packageName.isNullOrEmpty()) {
bundle.putString(EntranceConsts.KEY_PACKAGENAME, packageName)
if (!platforms.isNullOrEmpty()) {
bundle.putParcelableArrayList(EntranceConsts.KEY_PLATFORMS, platforms.toArrayList())
} else if (!platformName.isNullOrEmpty() || !packageName.isNullOrEmpty()) {
bundle.putParcelableArrayList(
EntranceConsts.KEY_PLATFORMS,
arrayListOf(GamePlatform(platformName, packageName))
)
}
}
if (scrollToLibao) {

View File

@ -1,8 +1,10 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.MeEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
/**
* Created by khy on 2017/4/24.
@ -37,4 +39,12 @@ class VersionVoteLinkEntity(
var platform: String = "",
@SerializedName("package")
var packageName: String = "",
) : LinkEntity()
var platforms: List<GamePlatform>? = null
) : LinkEntity()
@Parcelize
class GamePlatform(
val name: String? = null,
@SerializedName("package")
val packageName: String? = null
) : Parcelable

View File

@ -32,6 +32,7 @@ import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
import com.gh.gamecenter.qa.questions.edit.QuestionEditActivity
import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
@ -60,6 +61,9 @@ class CommunityHomeFragment : LazyFragment() {
}
override fun onFragmentFirstVisible() {
ArticleWebCacheManager.init(requireContext().applicationContext)
mViewModel = viewModelProvider()
super.onFragmentFirstVisible()

View File

@ -6,6 +6,7 @@ import android.view.View
import android.view.ViewGroup
import androidx.core.graphics.ColorUtils
import com.gh.common.exposure.ExposureManager
import com.gh.common.prioritychain.AccelerateNotificationHandler
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
import com.gh.common.util.LogUtils
@ -61,6 +62,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() {
private var mIsDisplayingLightContent = false
private var mViewModel: HomeSearchToolWrapperViewModel? = null
private var mMainWrapperViewModel: MainWrapperViewModel? = null
private var mOffsetCritical = 0.7F
@ -94,6 +96,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() {
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
mViewModel = viewModelProviderFromParent()
mMainWrapperViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance()))
mElapsedHelper = TimeElapsedHelper()
mSearchToolbarFragment =
childFragmentManager.findFragmentById(R.id.wrapper_toolbar) as SearchToolbarFragment?
@ -152,6 +155,25 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() {
setHomePush()
}
}
mMainWrapperViewModel?.accelerateNotificationPopup?.observeNonNull(viewLifecycleOwner) {
if (parentFragment is MainWrapperFragment && isSupportVisible) {
val isHandlerQueueEmpty = (parentFragment as MainWrapperFragment).isPriorityChainHandlerQueueEmpty()
val accelerateSet =
HashSet(SPUtils.getStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET))
if (isHandlerQueueEmpty && it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) {
AccelerateNotificationHandler.showAccelerateNotificationPopupWindow(
requireActivity(),
mMainWrapperViewModel,
mBaseHandler as BaseHandler,
it[0],
null
)
accelerateSet.add(it[0].messageId)
SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet)
}
}
}
}
private fun initSmartRefresh() {
@ -707,6 +729,7 @@ class HomeSearchToolWrapperFragment : HomeTabWrapperFragment() {
resumeHomePushVideo()
DownloadManager.getInstance().addObserver(dataWatcher)
updateHomePushDownloadBtn()
mMainWrapperViewModel?.requestAccelerateNotificationPopup()
}
override fun getInflatedLayout() =

View File

@ -41,7 +41,9 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.databinding.FragmentMainBinding
import com.gh.gamecenter.entity.*
import com.gh.gamecenter.entity.HomeDataEntity
import com.gh.gamecenter.entity.SubjectData
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.eventbus.EBUISwitch
import com.gh.gamecenter.forum.home.CommunityHomeFragment
@ -314,6 +316,8 @@ class MainWrapperFragment : BaseFragment_ViewPager_Checkable(), OnBackPressedLis
mPriorityChain.start()
}
fun isPriorityChainHandlerQueueEmpty() = mPriorityChain.isHandlerQueueEmpty()
private fun applyPkgConfig() {
val pkgLinkEntity = PkgHelper.getPkgConfig()
if (pkgLinkEntity != null) {

View File

@ -12,15 +12,15 @@ import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.DialogEntity
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.core.provider.IFloatingWindowProvider
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.DialogEntity
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.feature.entity.WelcomeDialogEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.WelcomeDialogEntity
import com.gh.gamecenter.floatingwindow.FloatingWindowEntity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.retrofit.RetrofitManager
@ -140,7 +140,7 @@ class MainWrapperViewModel(application: Application, repository: MainWrapperRepo
}
}
private fun requestAccelerateNotificationPopup() {
fun requestAccelerateNotificationPopup() {
if (CheckLoginUtils.isLogin()) {
mApi.getAccelerateNotificationPopup(UserManager.getInstance().userId)
.subscribeOn(Schedulers.io())

View File

@ -3,7 +3,10 @@ package com.gh.gamecenter.game.doublecard
import android.view.View
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.display
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.StringUtils
import com.gh.gamecenter.databinding.GameDoubleCardItemAlBinding
import com.gh.gamecenter.databinding.GameDoubleCardItemBinding
@ -11,6 +14,7 @@ import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.game.GameItemViewHolder
class DoubleCardViewHolder(val binding: GameDoubleCardItemAlBinding) : RecyclerView.ViewHolder(binding.root) {
private val mPosterWidth = (DisplayUtils.getScreenWidth() - 40F.dip2px()) / 2
fun bindView(gameList: MutableList<GameEntity>, subjectName: String, positionInOriginSubject: Int) {
bindSubView(
@ -40,6 +44,7 @@ class DoubleCardViewHolder(val binding: GameDoubleCardItemAlBinding) : RecyclerV
positionInOriginSubject: Int
) {
subBinding.run {
poster.setTag(ImageUtils.TAG_TARGET_WIDTH, mPosterWidth)
poster.post { poster.display(gameEntity.columnImage) }
gameName.text = gameEntity.name
brief.text =

View File

@ -37,6 +37,7 @@ import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.entity.SuggestType
import com.gh.gamecenter.common.eventbus.EBReuse
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.divider.HorizontalDividerItemDecoration
import com.gh.gamecenter.common.view.divider.VerticalDividerItemDecoration
@ -46,7 +47,6 @@ import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.entity.SubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.game.horizontal.GameHorizontalAdapter
import com.gh.gamecenter.game.horizontal.GameHorizontalListType
import com.gh.gamecenter.game.vertical.SpanCountPagerSnapHelper
@ -1048,6 +1048,9 @@ class DescAdapter(
StringUtils.buildString(mEntrance, "游戏详情[", mGameName, "]:自定义栏目"),
""
)
} else if (it.type == "qqqun" && it.link.isNullOrEmpty() && !it.text.isNullOrEmpty()) {
// 若QQ群key填写为空交互改为复制群号
it.text?.copyTextAndToast()
} else {
PageSwitchDataHelper.pushCurrentPageData(
hashMapOf(

View File

@ -62,7 +62,7 @@ class GameDetailInfoItemAdapter(
GameInfoItemData(
title = gameInfo.contact!!.hint,
info = gameInfo.contact!!.qq,
actionStr = if (gameInfo.contact!!.type == "qq") "咨询" else "加入",
actionStr = if (gameInfo.contact!!.type == "qq") "咨询" else if (gameInfo.contact?.key.isNullOrEmpty()) "复制" else "加入",
key = gameInfo.contact!!.key
)
)
@ -182,6 +182,9 @@ class GameDetailInfoItemAdapter(
gameInfoItemData.info.copyTextAndToast("已复制")
}
}
"复制" -> {
gameInfoItemData.info.copyTextAndToast()
}
}
}
}

View File

@ -13,7 +13,6 @@ import android.widget.RelativeLayout
import androidx.appcompat.content.res.AppCompatResources
import androidx.constraintlayout.widget.ConstraintSet
import androidx.core.content.ContextCompat
import androidx.lifecycle.ViewModelProvider
import androidx.recyclerview.widget.GridLayoutManager
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
@ -21,6 +20,7 @@ import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager2.widget.ViewPager2
import com.gh.common.constant.Config
import com.gh.common.databind.BindingAdapters
import com.gh.common.prioritychain.AccelerateNotificationHandler
import com.gh.common.util.*
import com.gh.common.util.LogUtils
import com.gh.common.util.NewFlatLogUtils
@ -40,6 +40,7 @@ import com.gh.gamecenter.entity.BadgeEntity
import com.gh.gamecenter.feature.entity.Badge
import com.gh.gamecenter.feature.eventbus.EBConcernChanged
import com.gh.gamecenter.fragment.MainWrapperFragment
import com.gh.gamecenter.fragment.MainWrapperViewModel
import com.gh.gamecenter.gamecollection.mine.MyGameCollectionActivity
import com.gh.gamecenter.history.HistoryActivity
import com.gh.gamecenter.login.entity.UserInfoEntity
@ -76,6 +77,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
private lateinit var mUnreadViewModel: MessageUnreadViewModel
private lateinit var mUserHomeViewModel: UserHomeViewModel
private lateinit var mHaloPersonalViewModel: HaloPersonalViewModel
private lateinit var mMainWrapperViewModel: MainWrapperViewModel
private lateinit var mPersonalRecommendAdapter: HaloPersonalRecommendAdapter
private lateinit var mPersonalBannerAdapter: HaloPersonalBannerAdapter
@ -261,19 +263,16 @@ class HaloPersonalFragment : BaseLazyFragment() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mDatabase = AppDatabase.getInstance()
val factory = UserViewModel.Factory(requireActivity().application)
mUserViewModel = ViewModelProvider(this, factory).get(UserViewModel::class.java)
mUserHomeViewModel = ViewModelProvider(
this, UserHomeViewModel.Factory(
mUserViewModel = viewModelProvider(UserViewModel.Factory(requireActivity().application))
mUserHomeViewModel = viewModelProvider(
UserHomeViewModel.Factory(
HaloApp.getInstance().application,
UserManager.getInstance().userId
)
).get(UserHomeViewModel::class.java)
mUnreadViewModel = ViewModelProvider(
this,
MessageUnreadViewModel.Factory(HaloApp.getInstance().application)
).get(MessageUnreadViewModel::class.java)
mHaloPersonalViewModel = ViewModelProvider(this).get(HaloPersonalViewModel::class.java)
)
mUnreadViewModel = viewModelProvider(MessageUnreadViewModel.Factory(HaloApp.getInstance().application))
mHaloPersonalViewModel = viewModelProvider()
mMainWrapperViewModel = viewModelProviderFromParent(MainWrapperViewModel.Factory(HaloApp.getInstance()))
mUserHomeViewModel.getUserInfo()
observeNoneUIRelatedChanges()
@ -292,6 +291,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
mUnreadViewModel.retry()
mUserHomeViewModel.getBadgeList()
mUserHomeViewModel.availableBadges()
mMainWrapperViewModel.requestAccelerateNotificationPopup()
}
val currentItem = (parentFragment as MainWrapperFragment).currentItem
if (currentItem == MainWrapperFragment.INDEX_PERSONAL) {
@ -451,6 +451,28 @@ class HaloPersonalFragment : BaseLazyFragment() {
}
}
mMainWrapperViewModel.accelerateNotificationPopup.observeNonNull(viewLifecycleOwner) {
if (parentFragment is MainWrapperFragment && isSupportVisible) {
val isHandlerQueueEmpty = (parentFragment as MainWrapperFragment).isPriorityChainHandlerQueueEmpty()
val accelerateSet =
HashSet(SPUtils.getStringSet(com.gh.gamecenter.common.constant.Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET))
if (isHandlerQueueEmpty && it.isNotEmpty() && !accelerateSet.contains(it[0].messageId)) {
AccelerateNotificationHandler.showAccelerateNotificationPopupWindow(
requireActivity(),
mMainWrapperViewModel,
mBaseHandler as BaseHandler,
it[0],
null
)
accelerateSet.add(it[0].messageId)
SPUtils.setStringSet(
com.gh.gamecenter.common.constant.Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET,
accelerateSet
)
}
}
}
// 微信/签到
RxView.clicks(mStubBinding.personalSign)
.throttleFirst(1, TimeUnit.SECONDS)

View File

@ -8,14 +8,12 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.FragmentManager
import androidx.recyclerview.widget.RecyclerView
import androidx.viewpager.widget.ViewPager.LayoutParams
import androidx.viewpager.widget.ViewPager.OnPageChangeListener
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.adapter.viewholder.UnAvaliableWebviewViewHolder
import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.databinding.TabItemBinding
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.common.utils.visibleIf
import com.gh.gamecenter.core.utils.MtaHelper
@ -53,6 +51,7 @@ class ArticleDetailAdapter(
ITEM_ARTICLE_DETAIL -> {
val isWebViewInstalled = PackageUtils.checkWebViewIsAvailable(mContext)
if (isWebViewInstalled) {
ArticleDetailContentViewHolder.wvLoadTimeInMills = System.currentTimeMillis()
val binding: ItemArticleDetailContentBinding =
ItemArticleDetailContentBinding.inflate(mLayoutInflater, parent, false)
ArticleDetailContentViewHolder(binding, mViewModel).apply { articleDetailVH = this }

View File

@ -5,6 +5,7 @@ import android.app.Activity
import android.graphics.Bitmap
import android.text.SpannableStringBuilder
import android.text.Spanned
import android.util.Log
import android.view.Gravity
import android.view.View
import android.webkit.JavascriptInterface
@ -41,6 +42,33 @@ class ArticleDetailContentViewHolder(
var viewModel: ArticleDetailViewModel
) : RecyclerView.ViewHolder(binding.root) {
companion object {
var wvLoadTimeInMills = 0L
}
val richEditor = ArticleWebCacheManager
.attachToRichEditor(binding.richEditorContainer)
.apply {
setInputEnabled(false)
setPadding(16, 4, 16, 4)
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
setTransparentBackground()
setLayoutCallback { viewModel.articleRenderedLiveData.postValue(true) }
setPageFinishedListener {
Log.d("ArticleDetail", "pageFinished->${System.currentTimeMillis() - wvLoadTimeInMills}")
viewModel.articlePageFinishedLiveData.postValue(true)
}
setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
Log.d("ArticleDetail", "onPageFinished: $url->${System.currentTimeMillis() - wvLoadTimeInMills}")
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
}
})
}
private var mEntrance = ""
val articleImgUrlList = ArrayList<String>()
@ -58,11 +86,41 @@ class ArticleDetailContentViewHolder(
titleTv.setTextColor(R.color.text_title.toColor(binding.root.context))
gameName.setTextColor(R.color.text_subtitleDesc.toColor(binding.root.context))
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
richEditor.setTransparentBackground()
richEditor.setInputEnabled(false)
richEditor.setPadding(16, 4, 16, 4)
richEditor.addJavascriptInterface(JsInterface(article.status ?: ""), "imagelistener")
richEditor.registerDynamicJsInterface("imageArr") { url ->
val defUrl = url.split("\\?".toRegex()).dropLastWhile { it.isEmpty() }.toTypedArray()[0]
if (!articleImgUrlList.contains(defUrl) && !url.contains("web_load_dfimg_icon.png")) {
articleImgUrlList.add(defUrl)
}
}
richEditor.registerDynamicJsInterface("imageClick") { url ->
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
else -> {
val status = article.status ?: ""
clickToastByStatus(status) {
var current = 0
var i = 0
val size = articleImgUrlList.size
while (i < size) {
if (url.contains(articleImgUrlList.get(i))) {
current = i
}
i++
}
val intent = ImageViewerActivity.getIntent(
binding.root.context, articleImgUrlList, current,
mEntrance + "+(帖子详情[" + binding.titleTv.text.toString() + "])"
)
(binding.root.context as Activity).startActivityForResult(
intent,
ImageViewerActivity.REQUEST_FOR_VIEWED_IMAGE
)
}
}
}
}
richEditor.addJavascriptInterface(
OnLinkClickListener(
root.context,
@ -72,23 +130,7 @@ class ArticleDetailContentViewHolder(
"帖子详情"
), "OnLinkClickListener"
)
richEditor.setLayoutCallback(object : EmptyCallback {
override fun onCallback() {
viewModel.articleRenderedLiveData.postValue(true)
}
})
richEditor.setPageFinishedListener {
viewModel.articlePageFinishedLiveData.postValue(true)
}
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "帖子详情")
}
})
approvalStatusTv.goneIf(article.status == "pass")
statusContainer.goneIf(article.status == "pass")
when (article.status) {
@ -320,7 +362,7 @@ class ArticleDetailContentViewHolder(
* 回调列表视频播放结束时的时间
*/
fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position)
richEditor.onVideoPlayedCallback(url, position)
}
fun updateFollowBtn(isFollowed: Boolean) {
@ -344,7 +386,7 @@ class ArticleDetailContentViewHolder(
fun imageClick(url: String) {
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -381,4 +423,14 @@ class ArticleDetailContentViewHolder(
}
}
}
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.removeJavascriptInterface("imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
}
}

View File

@ -26,7 +26,6 @@ import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.entity.NormalShareEntity
import com.gh.gamecenter.common.eventbus.EBReuse
@ -41,6 +40,7 @@ import com.gh.gamecenter.eventbus.EBDeleteCommentDetail
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTopCommunityChanged
import com.gh.gamecenter.feature.entity.ArticleDraftEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.Permissions
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
@ -119,11 +119,11 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
mAdapter?.articleDetailVH?.run {
if (articleImgUrlList.size > 0) {
if (imageSet.size == articleImgUrlList.size) {
binding.richEditor.replaceAllDfImage()
richEditor.replaceAllDfImage()
} else {
for (i in imageSet) {
val url = articleImgUrlList[i.toInt()]
binding.richEditor.replaceDfImageByUrl(url)
richEditor.replaceDfImageByUrl(url)
}
}
}
@ -165,6 +165,9 @@ class ArticleDetailFragment : BaseCommentFragment<CommentItemData, ArticleDetail
override fun onDestroyView() {
super.onDestroyView()
mAdapter?.articleDetailVH?.destroy()
if (mViewModel.detailEntity != null) {
HistoryHelper.insertArticleEntity(mViewModel.detailEntity!!)

View File

@ -0,0 +1,146 @@
package com.gh.gamecenter.qa.article.detail
import android.annotation.SuppressLint
import android.content.Context
import android.content.MutableContextWrapper
import android.view.ViewGroup
import android.webkit.WebResourceResponse
import android.webkit.WebView
import com.gh.common.view.RichEditor
import com.gh.gamecenter.common.utils.EnvHelper
import com.gh.gamecenter.core.utils.MD5Utils
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import okhttp3.OkHttpClient
import okhttp3.ResponseBody
import retrofit2.Retrofit
import retrofit2.adapter.rxjava2.RxJava2CallAdapterFactory
import retrofit2.converter.gson.GsonConverterFactory
import retrofit2.http.GET
import retrofit2.http.Query
import retrofit2.http.Streaming
import retrofit2.http.Url
import java.io.BufferedInputStream
import java.io.File
import java.io.FileOutputStream
import java.util.concurrent.TimeUnit
/**
* 社区文章详情-Web资源缓存管理类
*/
@SuppressLint("StaticFieldLeak")
object ArticleWebCacheManager {
private lateinit var cacheDir: File
private lateinit var cacheRichEditor: RichEditor
private var isInit = false
@SuppressLint("CheckResult")
fun init(applicationContext: Context) {
if (isInit) return
isInit = true
this.cacheRichEditor = RichEditor(
MutableContextWrapper(applicationContext)
)
this.cacheDir = File(applicationContext.cacheDir, "article/web").apply {
if (!exists()) mkdirs()
}
val cacheUrlList = mutableListOf(
"https://static-web.ghzs.com/website-static/lib/polyfill.min.js",
"https://and-static.ghzs.com/web/dplayer/DPlayer.min.js"
)
if (EnvHelper.isDevEnv) {
cacheUrlList.add("https://resource.ghzs.com/js/halo_app_test.js")
cacheUrlList.add("https://resource.ghzs.com/css/halo_app_test.css")
} else {
cacheUrlList.add("https://resource.ghzs.com/js/halo.js")
cacheUrlList.add("https://resource.ghzs.com/css/halo.css")
}
val okHttpClient = OkHttpClient.Builder()
.connectTimeout(30, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.writeTimeout(30, TimeUnit.SECONDS)
.build()
val retrofit = Retrofit.Builder()
.client(okHttpClient)
.baseUrl("https://resource.ghzs.com")
.addConverterFactory(GsonConverterFactory.create())
.addCallAdapterFactory(RxJava2CallAdapterFactory.create())
.build()
val apiService = retrofit.create(ApiService::class.java)
val requestSingleList = cacheUrlList.map { url ->
apiService.downloadFile(url, System.currentTimeMillis() / 1000)
.map {
val file = File(cacheDir, MD5Utils.getUrlMD5(url)).apply {
if (!exists()) createNewFile()
}
val inputStream = it.byteStream()
val outputStream = FileOutputStream(file)
inputStream.copyTo(outputStream, DEFAULT_BUFFER_SIZE)
file
}
}
Single.zip(requestSingleList) {}
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe({}, {})
}
fun attachToRichEditor(parent: ViewGroup): RichEditor {
val richEditor = this.cacheRichEditor.apply {
(this.context as MutableContextWrapper).baseContext = parent.context
setWebResourceRequestInterceptor { view, url -> shouldInterceptRequest(view, url) }
}
parent.addView(
richEditor,
ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
)
return cacheRichEditor
}
fun detachFromRichEditor(parent: ViewGroup) {
this.cacheRichEditor.setHtml("", false)
this.cacheRichEditor.setWebResourceRequestInterceptor(null)
(this.cacheRichEditor.context as MutableContextWrapper).baseContext = parent.context.applicationContext
parent.removeView(this.cacheRichEditor)
}
/**
* 拦截WebView的资源请求并返回URL对应的缓存数据
*/
fun shouldInterceptRequest(view: WebView, url: String): WebResourceResponse? {
val urlWithoutQueryParams = url.split("?")[0]
val cacheFile = File(cacheDir, MD5Utils.getUrlMD5(urlWithoutQueryParams))
if (!cacheFile.exists()) {
return null
}
val mimeType = if (urlWithoutQueryParams.endsWith(".js")) {
"text/javascript"
} else if (urlWithoutQueryParams.endsWith(".css")) {
"text/css"
} else null
return WebResourceResponse(mimeType, "utf-8", cacheFile.inputStream())
}
interface ApiService {
@GET
@Streaming
fun downloadFile(@Url url: String, @Query("timestamp") timestamp: Long): Single<ResponseBody>
}
}

View File

@ -108,11 +108,11 @@ class NewQuestionDetailFragment :
mAdapter?.questionDetailVH?.run {
if (questionImgUrlList.size > 0) {
if (imageSet.size == questionImgUrlList.size) {
binding.richEditor.replaceAllDfImage()
richEditor.replaceAllDfImage()
} else {
for (i in imageSet) {
val url = questionImgUrlList[i.toInt()]
binding.richEditor.replaceDfImageByUrl(url)
richEditor.replaceDfImageByUrl(url)
}
}
}
@ -742,6 +742,11 @@ class NewQuestionDetailFragment :
mBinding.root.setBackgroundColor(Color.TRANSPARENT)
}
override fun onDestroyView() {
super.onDestroyView()
mAdapter?.questionDetailVH?.destroy()
}
override fun onDarkModeChanged() {
super.onDarkModeChanged()
mAdapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }

View File

@ -26,6 +26,7 @@ import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.databinding.ItemArticleDetailContentBinding
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.qa.article.detail.ArticleWebCacheManager
import com.gh.gamecenter.qa.article.detail.EditHistoryDialog
import com.gh.gamecenter.qa.editor.OnLinkClickListener
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
@ -36,40 +37,39 @@ class QuestionDetailContentViewHolder(
var viewModel: NewQuestionDetailViewModel
) : RecyclerView.ViewHolder(binding.root) {
val richEditor = ArticleWebCacheManager
.attachToRichEditor(binding.richEditorContainer)
.apply {
setInputEnabled(false)
setPadding(16, 4, 16, 4)
enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
setTransparentBackground()
setLayoutCallback { viewModel.questionRenderedLiveData.postValue(true) }
setPageFinishedListener { viewModel.questionPageFinishedLiveData.postValue(true) }
setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
}
})
}
private var mEntrance = ""
val questionImgUrlList = ArrayList<String>()
@SuppressLint("AddJavascriptInterface")
fun bindView(question: QuestionsDetailEntity) {
binding.run {
richEditor.setInputEnabled(false)
richEditor.enableForceDark(DarkModeUtils.isDarkModeOn(binding.root.context))
richEditor.setTransparentBackground()
richEditor.setPadding(16, 4, 16, 4)
richEditor.removeJavascriptInterface("imagelistener")
richEditor.addJavascriptInterface(JsInterface(question.status), "imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.addJavascriptInterface(
OnLinkClickListener(
root.context, question.title
?: "", question.status, mEntrance, "问题详情"
), "OnLinkClickListener"
)
richEditor.setLayoutCallback(object : EmptyCallback {
override fun onCallback() {
viewModel.questionRenderedLiveData.postValue(true)
}
})
richEditor.setPageFinishedListener {
viewModel.questionPageFinishedLiveData.postValue(true)
}
richEditor.setChromeClientListener(object : RichEditor.WebChromeClientListener {
override fun onPageFinished(view: WebView?, url: String?) {
}
override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean {
return DefaultUrlHandler.interceptUrl(binding.root.context, url ?: "", "问题详情")
}
})
approvalStatusTv.goneIf(question.status == "pass")
statusContainer.goneIf(question.status == "pass")
when (question.status) {
@ -275,7 +275,7 @@ class QuestionDetailContentViewHolder(
* 回调列表视频播放结束时的时间
*/
fun onVideoPlayedCallback(url: String, position: Int) {
binding.richEditor.onVideoPlayedCallback(url, position)
richEditor.onVideoPlayedCallback(url, position)
}
inner class JsInterface(val status: String) {
@ -283,7 +283,7 @@ class QuestionDetailContentViewHolder(
fun imageClick(url: String) {
when {
url.contains("web_load_dfimg_icon.png") -> {
runOnUiThread { binding.richEditor.replaceAllDfImageExcludeGif() }
runOnUiThread { richEditor.replaceAllDfImageExcludeGif() }
}
// url.contains(RichEditor.IMAGE_FLAG_THUMBNAIL) -> {
// runOnUiThread { binding.richEditor.replaceDfImageByUrl(url) }
@ -320,4 +320,14 @@ class QuestionDetailContentViewHolder(
}
}
}
fun destroy() {
richEditor.setLayoutCallback(null)
richEditor.removeJavascriptInterface("imagelistener")
richEditor.removeJavascriptInterface("OnLinkClickListener")
richEditor.setChromeClientListener(null)
richEditor.setContentOwner(false)
richEditor.setPageFinishedListener(null)
ArticleWebCacheManager.detachFromRichEditor(binding.richEditorContainer)
}
}

View File

@ -193,6 +193,7 @@ class VoteAdapter(
openPlatformWindow = true,
link.platform,
link.packageName,
link.platforms,
traceEvent = mExposureEventSparseArray[position]
)
}

View File

@ -13,11 +13,11 @@ import android.view.Window
import android.view.inputmethod.EditorInfo
import android.widget.TextView
import androidx.core.widget.addTextChangedListener
import androidx.core.widget.doAfterTextChanged
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.*
import com.gh.common.util.DialogUtils
import com.gh.common.util.DirectUtils.directToLinkPage
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
@ -30,8 +30,8 @@ import com.gh.gamecenter.core.utils.MtaHelper.onEvent
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.databinding.DialogAddVoteBinding
import com.gh.gamecenter.databinding.FragmentVoteBinding
import com.gh.gamecenter.feature.entity.InstallGameEntity
import com.gh.gamecenter.entity.VersionVoteEntity
import com.gh.gamecenter.feature.entity.InstallGameEntity
import com.gh.gamecenter.game.upload.GameUploadFragment
import com.gh.gamecenter.game.upload.UploadScheduleDialog
import com.lightgame.utils.Util_System_Keyboard
@ -135,6 +135,9 @@ class VoteFragment : ListFragment<VersionVoteEntity, VoteViewModel>() {
etSearch.addTextChangedListener { s ->
tvBack.visibility = if (s.isNullOrEmpty()) View.GONE else View.VISIBLE
}
etSearch.doAfterTextChanged { editable ->
mListViewModel.autoSearchVersion(editable.toString().trim { it <= ' ' })
}
tvSearch.setOnClickListener {
if (etSearch.text.isNullOrEmpty()) {
toast("请输入搜索关键词")
@ -145,7 +148,6 @@ class VoteFragment : ListFragment<VersionVoteEntity, VoteViewModel>() {
}
tvBack.setOnClickListener {
etSearch.text.clear()
mListViewModel.searchVersion("")
}
}
}
@ -317,9 +319,13 @@ class VoteFragment : ListFragment<VersionVoteEntity, VoteViewModel>() {
mListViewModel.postVersionVote(name, isNewVote, entity.me?.isVersionRequested ?: false, { response ->
try {
waitDialog.dismiss()
val jsonObject = JSONObject(response.string())
val wechatBindStatus = jsonObject.optBoolean("wechat_bind")
if (isNewVote) {
val id = JSONObject(response.string()).getString("_id")
toast(R.string.vote_success)
showVoteSuccessDialog(wechatBindStatus)
val id = jsonObject.getString("_id")
if (!TextUtils.isEmpty(id)) {
mAdapter.updateVote(isNewVote, id, name, true)
val lastView = mLayoutManager.findViewByPosition(mAdapter.itemCount - 1)
@ -329,20 +335,7 @@ class VoteFragment : ListFragment<VersionVoteEntity, VoteViewModel>() {
}
} else {
if (entity.me?.isVersionRequested != true) {
val wechatBindStatus = JSONObject(response.string()).optBoolean("wechat_bind")
if (wechatBindStatus) {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "我知道了")
DialogUtils.showReserveOrVoteSuccessDialog(requireContext(), false)
} else {
DialogUtils.showReserveOrVoteSuccess2WechatBindDialog(requireContext(), false, {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "开启微信提醒")
requireContext().startActivity(WebActivity.getBindWechatIntent(requireContext()))
}, object : CancelListener {
override fun onCancel() {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "关闭")
}
})
}
showVoteSuccessDialog(wechatBindStatus)
}
mAdapter.updateVote(isNewVote, name, null, !(entity.me?.isVersionRequested ?: false))
}
@ -381,6 +374,22 @@ class VoteFragment : ListFragment<VersionVoteEntity, VoteViewModel>() {
})
}
private fun showVoteSuccessDialog(wechatBindStatus: Boolean) {
if (wechatBindStatus) {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "我知道了")
DialogUtils.showReserveOrVoteSuccessDialog(requireContext(), false)
} else {
DialogUtils.showReserveOrVoteSuccess2WechatBindDialog(requireContext(), false, {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "开启微信提醒")
requireContext().startActivity(WebActivity.getBindWechatIntent(requireContext()))
}, object : CancelListener {
override fun onCancel() {
NewFlatLogUtils.logGameDetailClickForVersionVote(mGameId, mGameName, "关闭")
}
})
}
}
override fun onBackPressed(): Boolean {
return if (mListViewModel.keyword.isNotEmpty()) {
mBinding.searchBarInclude.tvBack.performClick()

View File

@ -1,9 +1,11 @@
package com.gh.gamecenter.vote
import android.annotation.SuppressLint
import android.app.Application
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.baselist.ListViewModel
import com.gh.gamecenter.common.baselist.LoadType
import com.gh.gamecenter.common.retrofit.ApiResponse
@ -15,13 +17,16 @@ import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.disposables.CompositeDisposable
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.PublishSubject
import okhttp3.MediaType
import okhttp3.RequestBody
import okhttp3.ResponseBody
import org.json.JSONException
import org.json.JSONObject
import retrofit2.HttpException
import java.util.concurrent.TimeUnit
class VoteViewModel(application: Application, private val mGameId: String = "") :
ListViewModel<VersionVoteEntity, VersionVoteEntity>(application) {
@ -29,6 +34,20 @@ class VoteViewModel(application: Application, private val mGameId: String = "")
var keyword = ""
val uploadSuccess = MutableLiveData<Boolean>()
val disposable = CompositeDisposable()
private val searchKeySubject = PublishSubject.create<String>()
.also {
val disposable = it.debounce(300, TimeUnit.MILLISECONDS)
.distinctUntilChanged()
.observeOn(AndroidSchedulers.mainThread())
.subscribe { keyword ->
searchVersion(keyword)
}
this.disposable.add(disposable)
}
override fun mergeResultLiveData() {
mResultLiveData.addSource(mListLiveData) { list ->
mResultLiveData.postValue(list)
@ -37,6 +56,7 @@ class VoteViewModel(application: Application, private val mGameId: String = "")
override fun provideDataObservable(page: Int): Observable<MutableList<VersionVoteEntity>> =
RetrofitManager.getInstance().api.getVersionVote(mGameId, page, keyword)
.doOnSubscribe { disposable.add(it) }
fun postApkInfo(url: String, installGameEntity: InstallGameEntity) {
val requestMap = HashMap<String, Any>()
@ -52,6 +72,7 @@ class VoteViewModel(application: Application, private val mGameId: String = "")
GsonUtils.toJson(requestMap)
)
RetrofitManager.getInstance().api.postPlatformRequestApk(installGameEntity.platformRequestsId, body)
.doOnSubscribe { disposable.add(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
@ -89,6 +110,7 @@ class VoteViewModel(application: Application, private val mGameId: String = "")
RetrofitManager.getInstance().api.postVersionVote(name) // name is id
}
responseBodyObservable
.doOnSubscribe { disposable.add(it) }
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(object : Response<ResponseBody>() {
@ -110,11 +132,19 @@ class VoteViewModel(application: Application, private val mGameId: String = "")
})
}
override fun onCleared() {
disposable.dispose()
}
fun searchVersion(keyword: String) {
this.keyword = keyword
load(LoadType.REFRESH)
}
fun autoSearchVersion(keyword: String) {
searchKeySubject.onNext(keyword)
}
class Factory(private val gameId: String) : ViewModelProvider.NewInstanceFactory() {
override fun <T : ViewModel> create(modelClass: Class<T>): T {
return VoteViewModel(HaloApp.getInstance().application, gameId) as T

View File

@ -2,12 +2,11 @@ package com.gh.ndownload
import android.text.TextUtils
import androidx.annotation.WorkerThread
import com.gh.common.util.PackageInstaller
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadDataSimpleHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.common.utils.NetworkUtils
import com.gh.gamecenter.common.utils.addMetaExtra
import com.gh.gamecenter.common.utils.deepClone
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.ToastUtils
import com.halo.assistant.HaloApp
@ -20,6 +19,7 @@ import com.lightgame.download.FileUtils
import com.lightgame.utils.Utils
import org.json.JSONException
import org.json.JSONObject
import java.io.File
import java.io.IOException
import java.net.URL
import java.net.URLConnection
@ -34,6 +34,7 @@ import java.util.concurrent.ConcurrentHashMap
object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
private const val TAG = "NDownloadBridge"
private const val REAL_FORMAT = "real_format" // 最终的文件格式
private const val DEFAULT_DOWNLOAD_THREAD_SIZE = 2
private const val NO_NETWORK_MAX_RETRY_COUNT = 5
@ -218,6 +219,21 @@ object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
downloadEntity.status = com.lightgame.download.DownloadStatus.done
downloadEntity.addMetaExtra(com.lightgame.download.DownloadConfig.KEY_DOWNLOAD_ELAPSED_TIME, elapsedTime.toString())
val originalFormat = downloadEntity.format
val realFormat = downloadEntity.getMetaExtra(REAL_FORMAT)
// 若最终的格式有异常,那么根据最后获得的格式进行修改、重命名下载文件
if (realFormat.isNotEmpty() && originalFormat != realFormat) {
Utils.log(TAG, "更新最终的文件格式,更新前的格式为 $originalFormat 更新后的格式为 $realFormat")
val renamedId = PackageInstaller.createDownloadId(downloadEntity.name)
val renamedPath = PackageInstaller.getDownloadPathWithId(
renamedId, downloadEntity.getMetaExtra(REAL_FORMAT)
)
File(downloadEntity.path).renameTo(File(renamedPath))
downloadEntity.path = renamedPath
downloadEntity.format = realFormat
}
DownloadManager.getInstance().onTaskDone(downloadEntity)
NDownloadService.getService()?.removeFromDataChangerAndResumeWaitingTask(downloadEntity, true)
@ -243,6 +259,15 @@ object NDownloadBridge : InnerDownloadListener, IErrorRetryHandler {
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
// 记录重定向时获取到的最终格式,因为缓存问题,原格式与实际下载的格式有区别
val realFormat = connection?.getHeaderField("Halo-Format")
if (!realFormat.isNullOrEmpty() && downloadEntity.format != realFormat) {
Utils.log(TAG, "获取到新的文件格式, $realFormat")
downloadEntity.addMetaExtra(REAL_FORMAT, realFormat)
DownloadDao.getInstance(HaloApp.getInstance()).update(downloadEntity, false)
}
NDataChanger.notifyDataChanged(downloadEntity)
}
}

View File

@ -14,6 +14,7 @@ import android.view.View
import androidx.annotation.WorkerThread
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.*
import com.gh.ad.AdDelegateHelper
import com.gh.common.constant.Config
import com.gh.common.exposure.ExposureUtils
import com.gh.common.history.HistoryHelper
@ -1084,6 +1085,7 @@ object VHelper {
logLaunch(gameId, gameName, downloadEntity?.getGameCategory() ?: "", packageName)
if (showLoading) {
val thirdPartyAd = AdDelegateHelper.vGameLaunchAd?.thirdPartyAd
val intent = mDelegateManager.getStartGameIntent(
packageName,
gameId,
@ -1092,7 +1094,9 @@ object VHelper {
MetaUtil.getBase64EncodedAndroidId(),
HaloApp.getInstance().gid,
com.gh.gamecenter.BuildConfig.VERSION_NAME,
HaloApp.getInstance().channel
HaloApp.getInstance().channel,
thirdPartyAd?.slotId,
thirdPartyAd?.displaySize
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
context.startActivity(intent)

View File

@ -198,7 +198,8 @@ class ShortcutManager private constructor() {
MetaUtil.getBase64EncodedAndroidId(),
HaloApp.getInstance().gid,
com.gh.gamecenter.BuildConfig.VERSION_NAME,
HaloApp.getInstance().channel
HaloApp.getInstance().channel,
"", ""
)
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
return intent

View File

@ -217,8 +217,8 @@
android:textStyle="bold"
tools:text="这是一个很长很长很长很长很长很长很长很长很长很长的标题" />
<com.gh.common.view.RichEditor
android:id="@+id/richEditor"
<FrameLayout
android:id="@+id/richEditorContainer"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="13dp" />

View File

@ -7,8 +7,8 @@ ext {
targetSdkVersion = 28
// application info (每个大版本之间的 versionCode 增加 20)
versionCode = 971
versionName = "5.31.1"
versionCode = 990
versionName = "5.32.0"
applicationId = "com.gh.gamecenter"
// AndroidX

View File

@ -110,6 +110,8 @@ public class EntranceConsts {
public static final String KEY_USER_AVATAR = "user_avatar";
public static final String KEY_PLATFORM = "platform";
public static final String KEY_GAME_NAME = "game_name";
public static final String KEY_PLATFORMS = "platforms";
public static final String KEY_GAME_INFO = "game_info";
public static final String KEY_GAME_TYPE = "game_type";