diff --git a/app/build.gradle b/app/build.gradle
index 514e406611..4434244ad2 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -402,7 +402,7 @@ dependencies {
exclude module: "gsyvideoplayer-androidvideocache"
exclude group: "tv.danmaku.ijk.media"
})
- implementation ("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo") {
+ implementation("com.github.CarGuo.GSYVideoPlayer:gsyVideoPlayer-exo_player2:$gsyVideo") {
exclude group: 'com.google.android.exoplayer', module: 'extension-rtmp'
}
@@ -526,6 +526,18 @@ dependencies {
debugImplementation 'com.bytedance.android:shadowhook:1.0.9'
debugImplementation 'io.github.shiqos:wytrace:1.0.1'
+
+ if (!gradle.ext.excludeOptionalModules || gradle.ext.enableAccelerator) {
+ implementation(project(":feature:accelerator"))
+ }
+
+ if (!gradle.ext.excludeOptionalModules || gradle.ext.enableAliPay) {
+ implementation(project(":feature:ali_pay"))
+ }
+
+ if(!gradle.ext.excludeOptionalModules || gradle.ext.enableWechatPay){
+ implementation(project(":feature:wechat_pay"))
+ }
}
File propFile = file('sign.properties')
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 697763562f..1c7a5ba025 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -10,9 +10,9 @@
-
-
-
+
+
+
@@ -197,7 +197,9 @@
android:name="io.sentry.breadcrumbs.system-events"
android:value="false" />
-
+
@@ -814,6 +816,10 @@
android:name=".video.poster.PosterEditActivity"
android:screenOrientation="portrait" />
+
+
diff --git a/app/src/main/java/com/gh/common/DefaultJsApi.kt b/app/src/main/java/com/gh/common/DefaultJsApi.kt
index 185e12e8fe..80f7285323 100644
--- a/app/src/main/java/com/gh/common/DefaultJsApi.kt
+++ b/app/src/main/java/com/gh/common/DefaultJsApi.kt
@@ -11,7 +11,6 @@ import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
import androidx.fragment.app.FragmentActivity
import androidx.fragment.app.FragmentManager
-import com.therouter.TheRouter
import com.gh.common.exposure.ExposureManager
import com.gh.common.util.*
import com.gh.common.util.LogUtils
@@ -22,7 +21,8 @@ import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.common.callback.BiCallback
import com.gh.gamecenter.common.constant.Constants
-import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.constant.Constants.SP_MEMBER_PAYMENT_BUTTON_CLICK
+import com.gh.gamecenter.common.constant.Constants.SP_MEMBER_RECHARGE_BUTTON_CLICK
import com.gh.gamecenter.common.entity.NotificationUgc
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.loghub.LoghubUtils
@@ -30,20 +30,22 @@ import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
import com.gh.gamecenter.common.tracker.Tracker
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.NewFlatLogUtils
+import com.gh.gamecenter.common.utils.SensorsBridge.EVENT_MEMBER_RECHARGE_BUTTON_CLICK
+import com.gh.gamecenter.common.utils.SensorsBridge.EVENT_NAME
+import com.gh.gamecenter.common.utils.SensorsBridge.KEY_IS_FIRST_TIME
import com.gh.gamecenter.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
-import com.gh.gamecenter.core.utils.CurrentActivityHolder
-import com.gh.gamecenter.core.utils.DisplayUtils
-import com.gh.gamecenter.core.utils.SPUtils
-import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.SensorsEvent
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
+import com.gh.gamecenter.feature.entity.AcctRecordEntity
import com.gh.gamecenter.feature.entity.Badge
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.OrderEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.login.user.LoginTag
import com.gh.gamecenter.login.user.UserManager
@@ -55,10 +57,14 @@ import com.gh.gamecenter.personalhome.border.AvatarBorderActivity
import com.gh.gamecenter.setting.SettingBridge
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
+import com.halo.assistant.accelerator.repository.AccelerationRepository.Companion.PAYMENT_TYPE_ALIPAY
+import com.halo.assistant.accelerator.repository.AccelerationRepository.Companion.PAYMENT_TYPE_WECHAT
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus.*
import com.lightgame.utils.Utils
+import com.therouter.TheRouter
import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -75,6 +81,7 @@ class DefaultJsApi(
private var mBbsId: String? = "",
private var mOriginUrl: String? = "",
private val mForumName: String? = "",
+ private val listener: OnWebClickListener? = null
) {
companion object {
@@ -699,6 +706,78 @@ class DefaultJsApi(
}
}
+ @JavascriptInterface
+ fun preOrderWithAli(json: Any) {
+ val order = json.toString().toObject() ?: return
+ trackMemberPaymentButtonClick(order, PAYMENT_TYPE_ALIPAY)
+ listener?.onPreOrderWithAli(order)
+ }
+
+ @JavascriptInterface
+ fun preOrderWithWechat(json: Any) {
+ val order = json.toString().toObject() ?: return
+ trackMemberPaymentButtonClick(order, PAYMENT_TYPE_WECHAT)
+ listener?.onPreOrderWithWechat(order)
+ }
+
+ private fun trackMemberPaymentButtonClick(order: OrderEntity, paymentType: String) {
+ val isFirstTime = SPUtils.getBoolean(SP_MEMBER_PAYMENT_BUTTON_CLICK, true)
+ SPUtils.setBoolean(SP_MEMBER_PAYMENT_BUTTON_CLICK, false)
+ SensorsBridge.trackMemberPaymentButtonClick(
+ isFirstTime,
+ paymentType,
+ order.setMenuName,
+ order.paymentAmount
+ )
+ }
+
+ @JavascriptInterface
+ fun startGameAccelerate(acctJson: Any) {
+ if (acctJson is String) {
+ val acctRecord = GsonUtils.fromJson(acctJson, AcctRecordEntity::class.java)
+ val accInfo = acctRecord.accInfo
+ listener?.onStartGameAccelerate(accInfo)
+ }
+ }
+
+ @JavascriptInterface
+ fun getCurAcctGameId(): String {
+ return AcceleratorDataHolder.instance.getAcceleratingGameId()
+ }
+
+ @JavascriptInterface
+ fun stopGameAccelerate() {
+ listener?.onStopGameAccelerate()
+ }
+
+ @JavascriptInterface
+ fun refreshToken(token: Any, handler: CompletionHandler) {
+ val accessToken = token.toString()
+ UserManager.getInstance().refreshToken(accessToken, object : UserManager.refreshCallBack {
+ override fun onLogin() {
+ handler.complete(true)
+ }
+
+ override fun onLoginFailure(errorMessage: String?) {
+ handler.complete(false)
+ }
+
+ })
+ }
+
+ @JavascriptInterface
+ fun trackSensorsAnalytics(json: Any) {
+ val hashMap = json.toString().toObject>() ?: return
+ val eventName = hashMap.remove(EVENT_NAME) ?: return
+ when (eventName) {
+ EVENT_MEMBER_RECHARGE_BUTTON_CLICK -> {
+ hashMap[KEY_IS_FIRST_TIME] = SPUtils.getBoolean(SP_MEMBER_RECHARGE_BUTTON_CLICK, true)
+ SPUtils.setBoolean(SP_MEMBER_RECHARGE_BUTTON_CLICK, false)
+ }
+ }
+ SensorsBridge.trackSensorsAnalyticsFromWeb(eventName.toString(), hashMap)
+ }
+
/**
* 获取 ExposureEvent,可能为空
*/
@@ -815,4 +894,15 @@ class DefaultJsApi(
}
}
}
+
+ interface OnWebClickListener {
+
+ fun onPreOrderWithAli(order: OrderEntity)
+
+ fun onPreOrderWithWechat(order: OrderEntity)
+
+ fun onStartGameAccelerate(accInfo: AcctRecordEntity.AccInfo)
+
+ fun onStopGameAccelerate()
+ }
}
diff --git a/app/src/main/java/com/gh/common/chain/PackageCheckHandler.kt b/app/src/main/java/com/gh/common/chain/PackageCheckHandler.kt
index 25b0651c07..72a69cb1c7 100644
--- a/app/src/main/java/com/gh/common/chain/PackageCheckHandler.kt
+++ b/app/src/main/java/com/gh/common/chain/PackageCheckHandler.kt
@@ -8,12 +8,21 @@ import com.gh.gamecenter.feature.entity.GameEntity
class PackageCheckHandler : DownloadChainHandler() {
override fun handleRequest(context: Context, gameEntity: GameEntity, asVGame: Boolean) {
- PackageCheckDialogFragment.show((context as AppCompatActivity), gameEntity) {
+ fun nextOrProcessEnd() {
if (hasNext()) {
getNext()?.handleRequest(context, gameEntity, asVGame)
} else {
processEndCallback?.invoke(asVGame, null)
}
}
+
+ if (gameEntity.canSpeed) {
+ nextOrProcessEnd()
+ } else {
+ PackageCheckDialogFragment.show((context as AppCompatActivity), gameEntity) {
+ nextOrProcessEnd()
+ }
+ }
+
}
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/provider/IAcceleratorDataHolderProviderImpl.kt b/app/src/main/java/com/gh/common/provider/IAcceleratorDataHolderProviderImpl.kt
new file mode 100644
index 0000000000..ebdf875a7b
--- /dev/null
+++ b/app/src/main/java/com/gh/common/provider/IAcceleratorDataHolderProviderImpl.kt
@@ -0,0 +1,18 @@
+package com.gh.common.provider
+
+import com.gh.gamecenter.core.provider.IAcceleratorDataHolderProvider
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+
+@com.therouter.inject.ServiceProvider
+class IAcceleratorDataHolderProviderImpl : IAcceleratorDataHolderProvider {
+ override fun setVipEntity(vip: Any) {
+ if (vip is VipEntity) {
+ AcceleratorDataHolder.instance.setVipEntity(vip)
+ }
+ }
+
+ override fun clear() {
+ AcceleratorDataHolder.instance.clear()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/provider/WechatPayResultProviderImpl.kt b/app/src/main/java/com/gh/common/provider/WechatPayResultProviderImpl.kt
new file mode 100644
index 0000000000..213b43e06e
--- /dev/null
+++ b/app/src/main/java/com/gh/common/provider/WechatPayResultProviderImpl.kt
@@ -0,0 +1,54 @@
+package com.gh.common.provider
+
+import android.annotation.SuppressLint
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.core.provider.IWechatPayResultProvider
+import com.gh.gamecenter.feature.entity.OrderEntity
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.gh.gamecenter.feature.eventbus.EBPayState
+import com.halo.assistant.accelerator.repository.AccelerationRepository
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import org.greenrobot.eventbus.EventBus
+
+@com.therouter.inject.ServiceProvider
+class WechatPayResultProviderImpl : IWechatPayResultProvider {
+
+ private val repository = AccelerationRepository.newInstance()
+
+ @SuppressLint("CheckResult")
+ override fun onPayComplete(nonceStr: String, order: Any?) {
+ val orderEntity = order as? OrderEntity
+ repository.getWechatPayResult(nonceStr)
+ .compose(singleToMain())
+ .subscribe({
+ // 支付成功
+ EventBus.getDefault().post(EBPayState.PaySuccess)
+
+ // 先刷新本地状态,支付成功,肯定是付费会员
+ AcceleratorDataHolder.instance.setVipEntity(
+ VipEntity(
+ _vipStatus = true,
+ _isNewUser = false,
+ _isTryVip = false
+ )
+ )
+
+ SensorsBridge.trackMemberRechargeResult(
+ AccelerationRepository.PAYMENT_TYPE_WECHAT,
+ orderEntity?.setMenuName ?: "",
+ orderEntity?.paymentAmount ?: "",
+ AccelerationRepository.RECHARGE_RESULT_SUCCESS
+ )
+ }, {
+ // 支付失败
+ EventBus.getDefault().post(EBPayState.PayFail)
+ SensorsBridge.trackMemberRechargeResult(
+ AccelerationRepository.PAYMENT_TYPE_WECHAT,
+ orderEntity?.setMenuName ?: "",
+ orderEntity?.paymentAmount ?: "",
+ AccelerationRepository.RECHARGE_RESULT_FAILURE
+ )
+ })
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java
index 4a5ae06540..ec74f4e060 100644
--- a/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java
+++ b/app/src/main/java/com/gh/common/util/DetailDownloadUtils.java
@@ -41,7 +41,8 @@ public class DetailDownloadUtils {
/**
* 更新底部下载区域
- * @param viewHolder 下载区域的包裹
+ *
+ * @param viewHolder 下载区域的包裹
* @param ignoreDownloadEntity 忽略下载实体(往往用于下载异常时)
*/
public static void updateViewHolder(DetailViewHolder viewHolder, boolean ignoreDownloadEntity) {
@@ -53,6 +54,8 @@ public class DetailDownloadUtils {
viewHolder.getMultiVersionDownloadTv().setVisibility(View.GONE);
}
+ viewHolder.setSpeedViewsVisible(false);
+
// 根据预置的配置更新 ViewHolder 的状态 (譬如青少年模式、下载内容为空等)
if (updateViewHolderWithPredefinedConfig(viewHolder, gameEntity)) {
return;
@@ -135,6 +138,12 @@ public class DetailDownloadUtils {
showDualDownloadButton,
downloadEntity
);
+
+ if(!showVGame){
+ String rawBtnText = GameUtils.getDownloadBtnText(viewHolder.getContext(), gameEntity, false, showVGame, PluginLocation.only_game);
+ viewHolder.checkIfShowSpeedUi(rawBtnText);
+ }
+
} else {
// 游戏包含多 APK 的情况
viewHolder.getMultiVersionDownloadTv().setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord));
@@ -219,7 +228,7 @@ public class DetailDownloadUtils {
downloadButton.setText("");
}
} else {
- decoratedBtnText = rawBtnText + (containsAddWord? "" : downloadAddWord) + getWrappedDownloadSizeText(viewHolder);
+ decoratedBtnText = rawBtnText + (containsAddWord ? "" : downloadAddWord) + getWrappedDownloadSizeText(viewHolder);
if (overlayTv != null && downloadButton.getVisibility() != View.GONE) {
if (context.getString(com.gh.gamecenter.feature.R.string.launch).equals(rawBtnText)
diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt
index ab0ea11702..a6ef4bfb07 100644
--- a/app/src/main/java/com/gh/common/util/DirectUtils.kt
+++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt
@@ -195,7 +195,8 @@ object DirectUtils {
"simulator",
"teen_mode",
"message_center",
- "archive"
+ "archive",
+ "my_assets",
)
fun directToLinkPage(
@@ -618,6 +619,8 @@ object DirectUtils {
"archive" -> directToCloudArchive(context, linkEntity.link ?: "", linkEntity.text ?: "", "", entrance)
+ "my_assets" -> navigateToMyAssetsPage(context, entrance)
+
"" -> {
// do nothing
}
@@ -2315,7 +2318,13 @@ object DirectUtils {
// 跳转云存档详情页
@JvmStatic
- fun directToCloudArchive(context: Context, gameId: String, gameName: String, configUrl: String = "", entrance: String = "") {
+ fun directToCloudArchive(
+ context: Context,
+ gameId: String,
+ gameName: String,
+ configUrl: String = "",
+ entrance: String = ""
+ ) {
val bundle = Bundle()
val gameEntity = GameEntity(id = gameId, name = gameName)
bundle.putParcelable(KEY_GAME_ENTITY, gameEntity)
@@ -2324,4 +2333,14 @@ object DirectUtils {
bundle.putBoolean(KEY_USE_ALTERNATIVE_LAYOUT, true)
context.startActivity(ShellActivity.getIntent(context, Type.CLOUD_ARCHIVE, bundle))
}
+
+ @JvmStatic
+ fun navigateToMyAssetsPage(context: Context, entrance: String?) {
+ if (CheckLoginUtils.isLogin()) {
+ TheRouter.build(RouteConsts.activity.myAssetsActivity).navigation(context)
+ } else {
+ CheckLoginUtils.checkLogin(context, entrance, null)
+ }
+
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt
index 4cd3abc103..0fb715e969 100644
--- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt
+++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.kt
@@ -176,7 +176,8 @@ object DownloadItemUtils {
pluginLocation: PluginLocation? = PluginLocation.only_game,
hideDownloadBtnIfNoAvailableContent: Boolean = false,
briefStyle: String? = null,
- isShowRecommendStar: Boolean = false
+ isShowRecommendStar: Boolean = false,
+ listener: DownloadButton.OnUpdateListener? = null
) {
holder.gameDownloadBtn.putObject(gameEntity)
@@ -189,7 +190,8 @@ object DownloadItemUtils {
holder.gameDownloadBtn,
gameEntity,
hideDownloadBtnIfNoAvailableContent,
- pluginLocation
+ pluginLocation,
+ listener
)
return
}
@@ -210,7 +212,8 @@ object DownloadItemUtils {
holder.gameDownloadBtn,
gameEntity,
hideDownloadBtnIfNoAvailableContent,
- pluginLocation
+ pluginLocation,
+ listener
)
}
@@ -219,7 +222,8 @@ object DownloadItemUtils {
downloadBtn: DownloadButton,
gameEntity: GameEntity,
hideDownloadBtnIfNoAvailableContent: Boolean = false,
- pluginLocation: PluginLocation? = PluginLocation.only_game
+ pluginLocation: PluginLocation? = PluginLocation.only_game,
+ listener: DownloadButton.OnUpdateListener?
) {
// 控制是否显示下载按钮
downloadBtn.goneIf(context.getString(R.string.app_name) == gameEntity.name)
@@ -227,6 +231,7 @@ object DownloadItemUtils {
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE) || gameEntity.isSpecialDownload()) {
downloadBtn.text = "查看"
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.TEENAGER_MODE
+ listener?.completion(downloadBtn.text)
return
}
if (gameEntity.isReservable) {
@@ -239,6 +244,7 @@ object DownloadItemUtils {
buttonStyle = DownloadButton.ButtonStyle.RESERVED
}
}
+ listener?.completion(downloadBtn.text)
return
}
if (RegionSettingHelper.getGameH5DownloadByGameId(gameEntity.id) != null) {
@@ -248,6 +254,7 @@ object DownloadItemUtils {
setBackgroundResource(com.gh.gamecenter.common.R.drawable.download_button_normal_style)
setTextColor(com.gh.gamecenter.common.R.color.white.toColor(context))
}
+ listener?.completion(downloadBtn.text)
return
}
if (gameEntity.isMiniGame()) {
@@ -265,6 +272,7 @@ object DownloadItemUtils {
text = context.getString(com.gh.gamecenter.feature.R.string.quick_play)
}
}
+ listener?.completion(downloadBtn.text)
return
}
if (gameEntity.getApk().isEmpty() || gameEntity.downloadOffStatus != null) {
@@ -296,6 +304,7 @@ object DownloadItemUtils {
downloadBtn.isClickable = false
}
}
+ listener?.completion(downloadBtn.text)
} else if (gameEntity.getApk().size == 1) {
// 来自于下载管理的实体快照
val entityFromDownloadManager = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
@@ -354,43 +363,60 @@ object DownloadItemUtils {
downloadBtn.apply {
when (downloadEntity.status) {
DownloadStatus.done -> {
- if (downloadEntity.isSimulatorGame() && gameEntity.simulator != null) {
- GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation)
- } else if (isVGamePreferred) {
- buttonStyle =
- if (PackagesManager.isCanUpdate(
- downloadEntity.gameId,
- downloadEntity.packageName,
- asVGame = true
- )
- ) {
- setText(com.gh.gamecenter.feature.R.string.update)
- DownloadButton.ButtonStyle.NORMAL
- } else {
- setText(com.gh.gamecenter.feature.R.string.launch)
- DownloadButton.ButtonStyle.LAUNCH_OR_OPEN
- }
- } else {
- val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
- if (XapkUnzipStatus.SUCCESS.name == xapkStatus && isInstalling(downloadEntity.path)) {
+ val xapkStatus = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS]
+ when {
+ downloadEntity.isSimulatorGame() && gameEntity.simulator != null -> {
+ GameUtils.setDownloadBtnStatus(
+ context,
+ gameEntity,
+ downloadBtn,
+ pluginLocation,
+ listener
+ )
+ }
+
+ isVGamePreferred -> {
+ buttonStyle =
+ if (PackagesManager.isCanUpdate(
+ downloadEntity.gameId,
+ downloadEntity.packageName,
+ asVGame = true
+ )
+ ) {
+ setText(com.gh.gamecenter.feature.R.string.update)
+ DownloadButton.ButtonStyle.NORMAL
+ } else {
+ setText(com.gh.gamecenter.feature.R.string.launch)
+ DownloadButton.ButtonStyle.LAUNCH_OR_OPEN
+ }
+ listener?.completion(downloadBtn.text)
+ }
+
+ XapkUnzipStatus.SUCCESS.name == xapkStatus && isInstalling(downloadEntity.path) -> {
progress = 100
setText(com.gh.gamecenter.feature.R.string.installing)
buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
+ listener?.completion(downloadBtn.text)
return
}
- if (XapkUnzipStatus.UNZIPPING.name == xapkStatus) {
+
+ XapkUnzipStatus.UNZIPPING.name == xapkStatus -> {
val percent = downloadEntity.meta[XapkInstaller.XAPK_UNZIP_PERCENT]
progress = (java.lang.Float.valueOf(percent) * 10).toInt()
text = "$percent%"
buttonStyle = DownloadButton.ButtonStyle.XAPK_UNZIPPING
- return
- } else if (XapkUnzipStatus.FAILURE.name == xapkStatus) {
- setText(com.gh.gamecenter.feature.R.string.install)
- buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
+ listener?.completion(downloadBtn.text)
return
}
- if (PackagesManager.isInstalled(downloadEntity.packageName) && !downloadEntity.isUpdate) {
+ XapkUnzipStatus.FAILURE.name == xapkStatus -> {
+ setText(com.gh.gamecenter.feature.R.string.install)
+ buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
+ listener?.completion(downloadBtn.text)
+ return
+ }
+
+ PackagesManager.isInstalled(downloadEntity.packageName) && !downloadEntity.isUpdate -> {
// 双下载按钮快速安装时存在已下载的安装包过时,需要重新下载的情况
if (PackagesManager.isCanUpdate(
downloadEntity.gameId,
@@ -403,9 +429,13 @@ object DownloadItemUtils {
buttonStyle = DownloadButton.ButtonStyle.LAUNCH_OR_OPEN
setText(com.gh.gamecenter.feature.R.string.launch)
}
- } else {
+ listener?.completion(downloadBtn.text)
+ }
+
+ else -> {
buttonStyle = DownloadButton.ButtonStyle.INSTALL_NORMAL
setText(com.gh.gamecenter.feature.R.string.install)
+ listener?.completion(downloadBtn.text)
}
}
buttonStyle =
@@ -425,22 +455,24 @@ object DownloadItemUtils {
DownloadStatus.overflow -> {
buttonStyle = DownloadButton.ButtonStyle.NORMAL
setText(com.gh.gamecenter.feature.R.string.resume)
+ listener?.completion(downloadBtn.text)
}
DownloadStatus.cancel -> {
- GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation)
+ GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation, listener)
}
else -> {
// do nothing
+ listener?.completion(downloadBtn.text)
}
}
}
} else {
- GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation)
+ GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation, listener)
}
} else {
- GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation)
+ GameUtils.setDownloadBtnStatus(context, gameEntity, downloadBtn, pluginLocation, listener)
}
}
@@ -510,13 +542,18 @@ object DownloadItemUtils {
DownloadStatus.downloading -> {
if (isMultiVersion) {
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
- val darkMode = (holder.gameDownloadTips?.getTag(com.gh.gamecenter.common.R.string.is_dark_mode_on_id) as? Boolean) ?: false
+ val darkMode =
+ (holder.gameDownloadTips?.getTag(com.gh.gamecenter.common.R.string.is_dark_mode_on_id) as? Boolean)
+ ?: false
val isDarkModeChanged = DarkModeUtils.isDarkModeOn(context) != darkMode
if (holder.gameDownloadTips?.visibility == View.GONE || holder.gameDownloadTips?.isAnimating == false || isDarkModeChanged) {
holder.gameDownloadTips?.visibility = View.VISIBLE
holder.gameDownloadTips?.setDownloadTipsAnimation(true)
}
- holder.gameDownloadTips?.setTag(com.gh.gamecenter.common.R.string.is_dark_mode_on_id, DarkModeUtils.isDarkModeOn(context))
+ holder.gameDownloadTips?.setTag(
+ com.gh.gamecenter.common.R.string.is_dark_mode_on_id,
+ DarkModeUtils.isDarkModeOn(context)
+ )
} else {
holder.gameDownloadTips?.visibility = View.GONE
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
@@ -961,7 +998,8 @@ object DownloadItemUtils {
traceEvent: ExposureEvent? = null,
refreshCallback: EmptyCallback? = null
) {
- val str = if (downloadBtn is DownloadButton) downloadBtn.text else context.getString(com.gh.gamecenter.feature.R.string.download)
+ val str =
+ if (downloadBtn is DownloadButton) downloadBtn.text else context.getString(com.gh.gamecenter.feature.R.string.download)
if (gameEntity.getApk().isEmpty()) return
val apk = gameEntity.getApk().safelyGetInRelease(0) ?: return
@@ -980,7 +1018,16 @@ object DownloadItemUtils {
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
- download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
+ download(
+ context,
+ gameEntity,
+ downloadBtn,
+ entrance,
+ location,
+ asVGame,
+ isSubscribe as Boolean,
+ traceEvent
+ )
}
.buildHandlerChain()
?.handleRequest(context, gameEntity, shouldPerformAsVGame)
@@ -999,7 +1046,16 @@ object DownloadItemUtils {
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
- download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
+ download(
+ context,
+ gameEntity,
+ downloadBtn,
+ entrance,
+ location,
+ asVGame,
+ isSubscribe as Boolean,
+ traceEvent
+ )
}
.buildHandlerChain()
?.handleRequest(context, gameEntity, shouldPerformAsVGame)
@@ -1018,7 +1074,16 @@ object DownloadItemUtils {
addHandler(CheckDownloadHandler())
}
.setProcessEndCallback(gameEntity.id) { asVGame, isSubscribe ->
- download(context, gameEntity, downloadBtn, entrance, location, asVGame, isSubscribe as Boolean, traceEvent)
+ download(
+ context,
+ gameEntity,
+ downloadBtn,
+ entrance,
+ location,
+ asVGame,
+ isSubscribe as Boolean,
+ traceEvent
+ )
}
.buildHandlerChain()
?.handleRequest(context, gameEntity, shouldPerformAsVGame)
diff --git a/app/src/main/java/com/gh/common/util/GameUtils.kt b/app/src/main/java/com/gh/common/util/GameUtils.kt
index 5419d343da..806c15ad73 100644
--- a/app/src/main/java/com/gh/common/util/GameUtils.kt
+++ b/app/src/main/java/com/gh/common/util/GameUtils.kt
@@ -25,7 +25,10 @@ object GameUtils {
/**
* 去除与重复sourceList相同的数据
*/
- fun removeDuplicateData(sourceList: MutableList?, rawList: MutableList?): MutableList? {
+ fun removeDuplicateData(
+ sourceList: MutableList?,
+ rawList: MutableList?
+ ): MutableList? {
if (sourceList.isNullOrEmpty() || rawList.isNullOrEmpty()) {
return rawList
}
@@ -52,7 +55,8 @@ object GameUtils {
context: Context,
gameEntity: GameEntity,
downloadBtn: DownloadButton,
- pluginLocation: PluginLocation?
+ pluginLocation: PluginLocation?,
+ listener: DownloadButton.OnUpdateListener? = null
) {
// getDownloadBtnText 里包括查询数据库、根据包名读取包体 meta 信息等
lightWeightIoExecutor.execute {
@@ -73,6 +77,7 @@ object GameUtils {
} else {
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
}
+ listener?.completion(downloadBtn.text)
}
}
}
@@ -85,11 +90,13 @@ object GameUtils {
*/
@WorkerThread
@JvmStatic
- fun getDownloadBtnText(context: Context,
- gameEntity: GameEntity,
- isFromList: Boolean,
- fixedAsVGame: Boolean,
- pluginLocation: PluginLocation?): String {
+ fun getDownloadBtnText(
+ context: Context,
+ gameEntity: GameEntity,
+ isFromList: Boolean,
+ fixedAsVGame: Boolean,
+ pluginLocation: PluginLocation?
+ ): String {
if (gameEntity.getApk().size > 1) {
return ""
}
@@ -138,7 +145,8 @@ object GameUtils {
} else if (!isFromList) {
if (!performAsVGame
&& gameEntity.isDualBtnModeEnabled()
- && downloadEntity?.isVGameDownloadInDualDownloadMode() == true) {
+ && downloadEntity?.isVGameDownloadInDualDownloadMode() == true
+ ) {
// 下载的任务是由畅玩触发的,并且双下载按钮启用,游戏详情页不需判定为需要安装
downloadEntity = null
} else if (performAsVGame && downloadEntity?.isLocalDownloadInDualDownloadMode() == true) {
diff --git a/app/src/main/java/com/gh/common/util/TempCertificationUtils.kt b/app/src/main/java/com/gh/common/util/TempCertificationUtils.kt
index d9e217f39d..7cac8c17ef 100644
--- a/app/src/main/java/com/gh/common/util/TempCertificationUtils.kt
+++ b/app/src/main/java/com/gh/common/util/TempCertificationUtils.kt
@@ -6,6 +6,7 @@ import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.login.user.UserManager
import com.gh.ndownload.NHttpClient
import com.lightgame.utils.AppManager
import org.json.JSONObject
@@ -89,4 +90,12 @@ object TempCertificationUtils {
return stringBuffer.toString()
}
+ /**
+ * 检测光环是否实名
+ */
+ fun isUserVerified(): Boolean {
+ val idCard = UserManager.getInstance().userInfoEntity?.idCard
+ // 账号已实名
+ return idCard != null && idCard.status == 0
+ }
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt
index 81c72a3567..d593350b69 100644
--- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt
+++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt
@@ -14,8 +14,8 @@ import androidx.core.app.NotificationCompat
import androidx.core.text.bold
import androidx.core.text.buildSpannedString
import androidx.core.text.color
-import com.therouter.router.Route
import com.therouter.TheRouter
+import com.therouter.router.Route
import com.gh.common.dialog.NewPrivacyPolicyDialogFragment
import com.gh.common.util.DeviceTokenUtils
import com.gh.common.util.DialogUtils
@@ -91,7 +91,8 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
} else {
val spanBuilder = buildSpannedString {
append("这个弹窗只会在右上角有环境标签的测试包出现" +
- "\n进入应用以后还可以到关于我们页面长按应用图标重新选择")
+ "\n进入应用以后还可以到关于我们页面长按应用图标重新选择"
+ )
bold {
color(com.gh.gamecenter.common.R.color.text_theme.toColor(this@SplashScreenActivity)) {
append("\n点击这里进行预设置渠道")
diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt
index 54fe174f95..f7e95d5636 100644
--- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/DetailViewHolder.kt
@@ -4,8 +4,11 @@ import android.content.Context
import android.content.Intent
import android.text.TextUtils
import android.view.View
+import android.widget.ImageView
import android.widget.TextView
import androidx.appcompat.app.AppCompatActivity
+import androidx.constraintlayout.widget.ConstraintLayout
+import androidx.constraintlayout.widget.Group
import androidx.fragment.app.DialogFragment
import androidx.fragment.app.FragmentActivity
import com.airbnb.lottie.LottieAnimationView
@@ -13,6 +16,7 @@ import com.gh.common.chain.*
import com.gh.common.constant.Config
import com.gh.common.dialog.DeviceRemindDialog
import com.gh.common.dialog.GameOffServiceDialogFragment
+import com.gh.common.dialog.PackageCheckDialogFragment
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.history.HistoryHelper
import com.gh.common.simulator.NewSimulatorGameManager
@@ -44,6 +48,7 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.feature.view.DownloadButton.ButtonStyle
import com.gh.gamecenter.gamedetail.GameDetailViewModel
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
import com.gh.gamecenter.teenagermode.TeenagerModeActivity.Companion.getIntent
@@ -63,7 +68,8 @@ class DetailViewHolder(
name: String?,
title: String?,
val traceEvent: ExposureEvent?,
- val isSupportDualButton: Boolean = false, // 是否支持双下载按钮,不支持的时候跟普通列表意义选用优先级高的那个来显示
+ val isSupportDualButton: Boolean = false, // 是否支持双下载按钮,不支持的时候跟普通列表意义选用优先级高的那个来显示,
+ val acceleratorUiHelper: GameDetailAcceleratorUiHelper? = null, // 网速加速,只有游戏详情才有
onDownloadClickAction: ((Boolean) -> Unit)? = null
) {
var context: Context
@@ -92,6 +98,12 @@ class DetailViewHolder(
// 多版本下载文字
var multiVersionDownloadTv: TextView?
+ // 加速按钮
+ val speedContainer: ConstraintLayout?
+ private val ivFreeVipTag: ImageView?
+ private val gMoreZone: Group?
+
+
// 注意 View 的命名
init {
downloadBottom = view.findViewById(R.id.detail_ll_bottom)
@@ -106,6 +118,10 @@ class DetailViewHolder(
localDownloadTitleTv = view.findViewById(R.id.localDownloadTitleTv)
localDownloadButton = view.findViewById(R.id.localDownloadButton)
+ speedContainer = view.findViewById(R.id.cl_speed_container)
+ ivFreeVipTag = view.findViewById(R.id.iv_free_vip_tag)
+ gMoreZone = view.findViewById(R.id.g_more_zone)
+
context = view.context
var gameDownloadMode = gameEntity.getGameDownloadButtonMode()
@@ -162,7 +178,9 @@ class DetailViewHolder(
localDownloadButton?.putObject(gameEntity)
localDownloadButton?.setTag(
com.gh.gamecenter.feature.R.string.download, context.getString(
- com.gh.gamecenter.feature.R.string.download_local))
+ com.gh.gamecenter.feature.R.string.download_local
+ )
+ )
}
}
downloadPb.putWidgetBusinessName("游戏详情页")
@@ -180,6 +198,33 @@ class DetailViewHolder(
gamePermissionDialogFragment?.dismissAllowingStateLoss()
}
+
+ fun checkIfShowSpeedUi(rawBtnText: String) {
+ acceleratorUiHelper?.let {
+ when {
+ rawBtnText == "启动" && gameEntity.canSpeed -> {
+ downloadPb.goneIf(true)
+ localDownloadButton?.goneIf(true)
+ it.checkIfShowSpeedUi(true)
+ }
+
+ rawBtnText == "更新" && gameEntity.canSpeed -> {
+ it.checkIfShowSpeedUi(true)
+ }
+
+ else -> {
+ it.checkIfShowSpeedUi(false)
+ }
+ }
+ }
+
+ }
+
+ fun setSpeedViewsVisible(isVisible: Boolean) {
+ ivFreeVipTag?.goneIf(!isVisible)
+ speedContainer?.goneIf(!isVisible)
+ }
+
internal class OnDetailDownloadClickListener(
private val mViewHolder: DetailViewHolder,
private val mEntrance: String?,
@@ -286,11 +331,13 @@ class DetailViewHolder(
showLandPageAddressDialogIfNeeded()
}
}
+
"toast" -> {
mViewHolder.viewModel?.performTabSelected(GameDetailTabEntity.TYPE_COMMENT)
ToastUtils.toast("该游戏因故暂不提供下载,具体详情可在相关评论中查看,敬请谅解~")
showLandPageAddressDialogIfNeeded()
}
+
"third_party" -> {
showLandPageAddressDialogIfNeeded()
}
@@ -339,7 +386,19 @@ class DetailViewHolder(
if (mAsVGame) {
VHelper.installOrLaunch(mViewHolder.context, mGameEntity, null)
} else {
- PackageLauncher.launchApp(mViewHolder.context, mGameEntity, mGameEntity.getUniquePackageName())
+ // 如果游戏配置了加速,则启动时需要进行包名检测
+ if (mGameEntity.canSpeed) {
+ PackageCheckDialogFragment.show(mViewHolder.context as AppCompatActivity, mGameEntity) {
+ PackageLauncher.launchApp(
+ mViewHolder.context, mGameEntity, mGameEntity.getUniquePackageName()
+ )
+ }
+ } else {
+ PackageLauncher.launchApp(
+ mViewHolder.context, mGameEntity, mGameEntity.getUniquePackageName()
+ )
+ }
+
}
} else {
GamePermissionDialogFragment.show(
@@ -600,8 +659,9 @@ class DetailViewHolder(
val apkEntity = mGameEntity.getApk().firstOrNull()
val msg = FileUtils.isCanDownload(mViewHolder.context, apkEntity?.size ?: "")
if (TextUtils.isEmpty(msg)) {
- val btnContainsUpdateText = mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update_v) == buttonText
- || buttonText.contains(mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update))
+ val btnContainsUpdateText =
+ mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update_v) == buttonText
+ || buttonText.contains(mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update))
if (asVGame && btnContainsUpdateText) {
VHelper.updateOrReDownload(mGameEntity)
@@ -710,11 +770,16 @@ class DetailViewHolder(
private fun performDownloadClickAction() {
val buttonText = if (mShowDualDownloadButton && !mAsVGame) {
- mViewHolder.localDownloadTitleTv?.text?.ifEmpty { mViewHolder.downloadPb.text.ifEmpty { mViewHolder.overlayTv?.text ?: "" } }
+ mViewHolder.localDownloadTitleTv?.text?.ifEmpty {
+ mViewHolder.downloadPb.text.ifEmpty {
+ mViewHolder.overlayTv?.text ?: ""
+ }
+ }
} else {
mViewHolder.downloadPb.text.ifEmpty { mViewHolder.overlayTv?.text ?: "" }
}
- val isUpdate = buttonText?.contains(mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update)) == true
+ val isUpdate =
+ buttonText?.contains(mViewHolder.context.getString(com.gh.gamecenter.feature.R.string.update)) == true
onDownloadClickAction?.invoke(isUpdate)
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/cloudarchive/CloudArchiveManagerActivity.kt b/app/src/main/java/com/gh/gamecenter/cloudarchive/CloudArchiveManagerActivity.kt
index 2c96ea5843..97786122ee 100644
--- a/app/src/main/java/com/gh/gamecenter/cloudarchive/CloudArchiveManagerActivity.kt
+++ b/app/src/main/java/com/gh/gamecenter/cloudarchive/CloudArchiveManagerActivity.kt
@@ -216,7 +216,7 @@ class CloudArchiveManagerActivity : BaseActivity_TabLayout(), ArchiveLimitSelect
private fun initDownloadBtn() {
mGameEntity?.let { gameEntity ->
- DownloadItemUtils.updateDownloadButton(this, mBinding.downloadBtn, gameEntity)
+ DownloadItemUtils.updateDownloadButton(this, mBinding.downloadBtn, gameEntity, listener = null)
DownloadItemUtils.setOnClickListener(this, mBinding.downloadBtn, gameEntity, 0, null, mEntrance, "")
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/eventbus/EBStartupAcceleration.kt b/app/src/main/java/com/gh/gamecenter/eventbus/EBStartupAcceleration.kt
new file mode 100644
index 0000000000..50e0b6ad94
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/eventbus/EBStartupAcceleration.kt
@@ -0,0 +1,5 @@
+package com.gh.gamecenter.eventbus
+
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+
+class EBStartupAcceleration(val acctGameInfo: AcctGameInfo)
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/AcceleratorZoneViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/AcceleratorZoneViewModel.kt
new file mode 100644
index 0000000000..e1aab2c5cb
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/AcceleratorZoneViewModel.kt
@@ -0,0 +1,13 @@
+package com.gh.gamecenter.gamedetail
+
+import androidx.lifecycle.ViewModel
+import com.halo.assistant.accelerator.AccelerationUseCase
+
+class AcceleratorZoneViewModel : ViewModel() {
+
+ val useCase = AccelerationUseCase()
+
+ override fun onCleared() {
+ useCase.onClear()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt
index fad10e0cb6..9d935bb95a 100644
--- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailViewModel.kt
@@ -8,10 +8,7 @@ import android.os.Build
import android.text.TextUtils
import android.util.SparseBooleanArray
import androidx.collection.arrayMapOf
-import androidx.lifecycle.AndroidViewModel
-import androidx.lifecycle.MutableLiveData
-import androidx.lifecycle.ViewModel
-import androidx.lifecycle.ViewModelProvider
+import androidx.lifecycle.*
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.history.HistoryHelper
import com.gh.common.util.CheckLoginUtils
@@ -40,15 +37,20 @@ import com.gh.gamecenter.gamedetail.detail.viewholder.GameDetailContentRecommend
import com.gh.gamecenter.gamedetail.entity.*
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.login.user.UserRepository
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.vspace.VHelper
import com.google.gson.JsonArray
import com.google.gson.reflect.TypeToken
import com.halo.assistant.HaloApp
+import com.halo.assistant.accelerator.repository.AccelerationRepository
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.utils.Utils
import io.reactivex.Single
import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.disposables.Disposable
import io.reactivex.schedulers.Schedulers
import okhttp3.ResponseBody
import org.json.JSONObject
@@ -64,6 +66,7 @@ class GameDetailViewModel(
private val api = RetrofitManager.getInstance().api
private val newApi = RetrofitManager.getInstance().newApi
+ private val accelerationRepository = AccelerationRepository.newInstance()
val concernLiveData = MutableLiveData()
val gameLiveData = MutableLiveData?>()
@@ -127,6 +130,8 @@ class GameDetailViewModel(
var isGameInstalled = false
private var isGameUpdatable = false
+ private val compositeDisposable = CompositeDisposable()
+
init {
loadData()
}
@@ -138,11 +143,6 @@ class GameDetailViewModel(
}
when {
- game != null -> {
- gameLiveData.postValue(Resource.success(game))
- loadGameDetailData()
- }
-
gameId != null -> getGameDigest()
else -> gameLiveData.postValue(null)
}
@@ -188,6 +188,13 @@ class GameDetailViewModel(
game = response
gameLiveData.postValue(Resource.success(game))
loadGameDetailData()
+ if (game?.canSpeed == true) {
+ // 如果当前游戏支持加速,则刷新vip状态
+ val userId = UserManager.getInstance().userId
+ if (userId.isNotBlank()) {
+ UserRepository.getInstance().refreshVipStatus(userId, true)
+ }
+ }
}
override fun onFailure(e: HttpException?) {
@@ -196,6 +203,7 @@ class GameDetailViewModel(
})
}
+
/**
* 过滤被屏蔽的内容
*/
@@ -205,7 +213,10 @@ class GameDetailViewModel(
if (ContentBlockedHelper.isContentBlocked(item.type)) {
continue
}
- if (item.type == GameDetailData.TYPE_BASIC_INFO && ContentBlockedHelper.isContentBlocked(ContentBlockedHelper.GAME_TAG)) {
+ if (item.type == GameDetailData.TYPE_BASIC_INFO && ContentBlockedHelper.isContentBlocked(
+ ContentBlockedHelper.GAME_TAG
+ )
+ ) {
item.linkBasicInfo?.gameTags = arrayListOf()
}
finalItemList.add(item)
@@ -445,7 +456,8 @@ class GameDetailViewModel(
data.linkVideoImgArea != null -> {
val coverList = transformVideoImgAreaData(data.linkVideoImgArea)
if (coverList.isNotEmpty()) {
- defaultCoverEntity = coverList.find { coverEntity -> coverEntity.isDefault } ?: coverList.first()
+ defaultCoverEntity =
+ coverList.find { coverEntity -> coverEntity.isDefault } ?: coverList.first()
coverListLiveData.postValue(coverList)
}
}
@@ -1011,6 +1023,61 @@ class GameDetailViewModel(
})
}
+ private val _lastAcctGame = MutableLiveData()
+ val lastAcctGame: LiveData = _lastAcctGame
+
+ private val _hasAnyRecord = MutableLiveData()
+ val hasAnyRecord: LiveData = _hasAnyRecord
+
+ fun loadRecordDataInDatabase(gameId: String) {
+ accelerationRepository.getLastAcctGameInfoObservable(gameId)
+ .compose(observableToMain())
+ .subscribe(object : Response>() {
+ override fun onSubscribe(d: Disposable) {
+ super.onSubscribe(d)
+ compositeDisposable.add(d)
+ }
+
+ override fun onResponse(response: List?) {
+ super.onResponse(response)
+ _lastAcctGame.value = response?.firstOrNull()
+ }
+
+ override fun onFailure(e: HttpException?) {
+ super.onFailure(e)
+ _lastAcctGame.value = null
+ }
+ })
+
+
+ accelerationRepository.getHasAnyAcctRecordObservable()
+ .compose(observableToMain())
+ .subscribe(
+ object : Response() {
+ override fun onSubscribe(d: Disposable) {
+ super.onSubscribe(d)
+ compositeDisposable.add(d)
+ }
+
+ override fun onResponse(response: Boolean?) {
+ super.onResponse(response)
+ _hasAnyRecord.value = response ?: false
+ }
+
+ override fun onFailure(e: HttpException?) {
+ super.onFailure(e)
+ _hasAnyRecord.value = false
+ }
+ })
+
+
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ compositeDisposable.clear()
+ }
+
class Factory(
private val application: Application,
private val gameId: String?,
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailWrapperFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailWrapperFragment.kt
index 6166611fed..41ec944c19 100644
--- a/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailWrapperFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/GameDetailWrapperFragment.kt
@@ -66,6 +66,7 @@ import com.gh.gamecenter.feature.entity.SimpleGame
import com.gh.gamecenter.feature.eventbus.EBConcernChanged
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.utils.ApkActiveUtils
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper
import com.gh.gamecenter.gamedetail.cloudarchive.CloudArchiveFragment
import com.gh.gamecenter.gamedetail.detail.GameDetailFragment
import com.gh.gamecenter.gamedetail.dialog.GameDetailMoreDialog
@@ -95,7 +96,7 @@ import org.greenrobot.eventbus.ThreadMode
import retrofit2.HttpException
import java.util.*
-class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
+class GameDetailWrapperFragment : BaseLazyFragment(), IScrollable {
private val binding by lazy { FragmentGameDetailWrapperBinding.inflate(layoutInflater) }
private val bodyBinding by lazy { binding.bodyContainer }
private val downloadBinding by lazy { binding.detailLlBottom }
@@ -103,6 +104,7 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
private lateinit var viewModel: GameDetailViewModel
private val packageViewModel: PackageViewModel by lazy { viewModelProvider(PackageViewModel.Factory()) }
private val userViewModel: UserViewModel by lazy { viewModelProvider(UserViewModel.Factory(HaloApp.getInstance().application)) }
+ private val acceleratorUiHelper: GameDetailAcceleratorUiHelper by lazy { GameDetailAcceleratorUiHelper(binding.detailLlBottom) }
private var moreMenuItem: MenuItem? = null
private var downloadMenuItem: MenuItem? = null
@@ -243,6 +245,7 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
name = "游戏详情",
title = gameEntity!!.name ?: "",
traceEvent = traceEvent,
+ acceleratorUiHelper = acceleratorUiHelper,
isSupportDualButton = true
) {
viewModel.isDownloadClick = true
@@ -341,6 +344,7 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
"is_download" to viewModel.isDownloadClick
})
}
+ acceleratorUiHelper.clear()
}
private fun initMenu() {
@@ -386,6 +390,7 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
if (gameResource.status == Status.SUCCESS) {
viewModel.logHistory(gameResource.data!!)
gameEntity = gameResource.data
+ initAccelerator()
showBrowserInstallHintIfNeeded()
controlInstallHint()
// 添加启动弹窗的相关信息
@@ -407,6 +412,9 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
controlReserveBtn()
gameEntity?.run { showVModeIconAtBottomBarIfNeeded(it.smoothRelatedGame, this) }
showConcernIconAtBottomBarIfAvailable()
+ // 底部按钮尺寸已确定,可以显示按钮上的引导页
+ acceleratorUiHelper.isButtonSizeDetermined = true
+ acceleratorUiHelper.showGuideLayerIfNeed()
}
viewModel.gameDetailTabListLiveData.observeNonNull(viewLifecycleOwner) {
if (it.status == Status.SUCCESS) {
@@ -429,20 +437,34 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
SensorsBridge.trackEventWithExposureSource(
"GameDetailPageShow",
traceEvent?.source ?: listOf(ExposureSource(mEntrance)),
- "game_id", gameEntity?.id ?: "",
- "game_name", gameEntity?.name ?: "",
- "download_status", gameEntity?.downloadStatusChinese ?: "",
- "cloud_save_tab_status", if (data.find { tabEntity -> tabEntity.type == GameDetailTabEntity.TYPE_ARCHIVE } != null) "开启" else "关闭",
- "game_type", gameEntity?.categoryChinese ?: "",
- "page_name", getCurrentPageEntity().pageName,
- "page_id", getCurrentPageEntity().pageId,
- "page_business_id", getCurrentPageEntity().pageBusinessId,
- "last_page_name", getLastPageEntity().pageName,
- "last_page_id", getLastPageEntity().pageId,
- "last_page_business_id", getLastPageEntity().pageBusinessId,
- "button_name", buttonName,
- "game_schema_type", gameEntity?.gameBitChinese ?: "",
- "download_type", gameEntity?.downloadType ?: "",
+ "game_id",
+ gameEntity?.id ?: "",
+ "game_name",
+ gameEntity?.name ?: "",
+ "download_status",
+ gameEntity?.downloadStatusChinese ?: "",
+ "cloud_save_tab_status",
+ if (data.find { tabEntity -> tabEntity.type == GameDetailTabEntity.TYPE_ARCHIVE } != null) "开启" else "关闭",
+ "game_type",
+ gameEntity?.categoryChinese ?: "",
+ "page_name",
+ getCurrentPageEntity().pageName,
+ "page_id",
+ getCurrentPageEntity().pageId,
+ "page_business_id",
+ getCurrentPageEntity().pageBusinessId,
+ "last_page_name",
+ getLastPageEntity().pageName,
+ "last_page_id",
+ getLastPageEntity().pageId,
+ "last_page_business_id",
+ getLastPageEntity().pageBusinessId,
+ "button_name",
+ buttonName,
+ "game_schema_type",
+ gameEntity?.gameBitChinese ?: "",
+ "download_type",
+ gameEntity?.downloadType ?: "",
*(traceEvent?.additional ?: emptyArray())
)
}, 120)
@@ -472,7 +494,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
val downloadEntitySnapshot = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
if (isSpecialDownloadDialogAvailable(downloadEntitySnapshot)
- && downloadEntitySnapshot != null) {
+ && downloadEntitySnapshot != null
+ ) {
updateSpecialDownloadDialogIcon(true)
}
binding.expandSpecialDownloadIv.setOnClickListener {
@@ -553,6 +576,24 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
}
}
+ private fun initAccelerator() {
+ gameEntity?.let { game ->
+ if (game.canSpeed) {
+ acceleratorUiHelper.initUi(game, requireContext())
+
+ viewModel.lastAcctGame.observe(viewLifecycleOwner) {
+ acceleratorUiHelper.setCurrentAcctGameInfo(it)
+ }
+
+ viewModel.hasAnyRecord.observe(viewLifecycleOwner) {
+ acceleratorUiHelper.setHasAnyAcctRecord(it)
+ }
+
+ viewModel.loadRecordDataInDatabase(game.id)
+ }
+ }
+ }
+
private fun updateArchiveTabUI(hasNew: Boolean) {
if (hasNew) {
val archivePosition = tabEntityList.indexOfFirst { it.type == GameDetailTabEntity.TYPE_ARCHIVE }
@@ -597,7 +638,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
}
private fun doShowAlertDialog(dialog: GameEntity.Dialog) {
- SensorsBridge.trackEvent("GameDetailDialogShow",
+ SensorsBridge.trackEvent(
+ "GameDetailDialogShow",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"game_type", gameEntity?.categoryChinese ?: ""
@@ -609,7 +651,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
dialog.confirmButton.text.toString(),
dialog.closeButtonText,
{
- SensorsBridge.trackEvent("GameDetailDialogClick",
+ SensorsBridge.trackEvent(
+ "GameDetailDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"game_type", gameEntity?.categoryChinese ?: "",
@@ -622,7 +665,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
DirectUtils.directToLinkPage(requireContext(), dialog.confirmButton, mEntrance, "")
},
{
- SensorsBridge.trackEvent("GameDetailDialogClick",
+ SensorsBridge.trackEvent(
+ "GameDetailDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"game_type", gameEntity?.categoryChinese ?: "",
@@ -630,7 +674,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
)
},
{
- SensorsBridge.trackEvent("GameDetailDialogClick",
+ SensorsBridge.trackEvent(
+ "GameDetailDialogClick",
"game_id", gameEntity?.id ?: "",
"game_name", gameEntity?.name ?: "",
"game_type", gameEntity?.categoryChinese ?: "",
@@ -654,7 +699,10 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
EntranceConsts.KEY_SCROLL_TO_LIBAO,
arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_LIBAO) ?: false
)
- bundle.putBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER, arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER) ?: false)
+ bundle.putBoolean(
+ EntranceConsts.KEY_SCROLL_TO_SERVER,
+ arguments?.getBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER) ?: false
+ )
bundle.putBoolean(
EntranceConsts.KEY_OPEN_VIDEO_STREAMING,
arguments?.getBoolean(EntranceConsts.KEY_OPEN_VIDEO_STREAMING) ?: false
@@ -664,19 +712,23 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
fragment = fragment ?: GameDetailFragment()
bundle.putAll(arguments)
}
+
GameDetailTabEntity.TYPE_ARCHIVE -> {
fragment = fragment ?: CloudArchiveFragment()
bundle.putParcelable(EntranceConsts.KEY_GAME, gameEntity ?: GameEntity())
bundle.putString(EntranceConsts.KEY_ARCHIVE_CONFIG_URL, tabEntity.archiveConfigUrl)
}
+
GameDetailTabEntity.TYPE_COMMENT -> {
fragment = fragment ?: RatingFragment()
bundle.putBoolean(EntranceConsts.KEY_COMMENT_AS_DEFAULT_TAB, skipGameComment)
bundle.putBoolean(EntranceConsts.KEY_DIRECT_COMMENT, gameEntity?.directComment ?: false)
}
+
GameDetailTabEntity.TYPE_BBS -> {
fragment = fragment ?: Fragment()
}
+
GameDetailTabEntity.TYPE_ZONE -> {
val zone = gameEntity?.zone
if (zone?.style == "link") {
@@ -688,16 +740,19 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
fragment = fragment ?: FuLiFragment()
}
}
+
GameDetailTabEntity.TYPE_WEB -> {
fragment = fragment ?: WebFragment()
bundle.putString(EntranceConsts.KEY_URL, tabEntity.link?.link)
}
+
GameDetailTabEntity.TYPE_GIFT -> {
fragment = fragment ?: LibaoListFragment()
gameEntity?.let {
bundle.putAll(LibaoListFragment.getBundle(it, true))
}
}
+
GameDetailTabEntity.TYPE_CUSTOM_PAGE -> {
fragment = fragment ?: CustomPageFragment()
bundle.putString(EntranceConsts.KEY_CUSTOM_PAGE_ID, tabEntity.link?.link)
@@ -831,7 +886,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
val tabItemBinding = GameDetailTabItemBinding.inflate(layoutInflater).apply {
tabTitle.text = tabEntity?.name
if (tabEntity?.type == GameDetailTabEntity.TYPE_ARCHIVE) {
- val hasEnterArchiveGameDetail = SPUtils.getBoolean(SP_HAS_ENTER_ARCHIVE_GAME_DETAIL_PREFIX + (gameEntity?.id ?: ""))
+ val hasEnterArchiveGameDetail =
+ SPUtils.getBoolean(SP_HAS_ENTER_ARCHIVE_GAME_DETAIL_PREFIX + (gameEntity?.id ?: ""))
val hasNewArchive = viewModel.archiveStatusLiveData.value ?: false
newIv.goneIf(hasEnterArchiveGameDetail && !hasNewArchive) {
if (i == tabEntityList.size - 1) {
@@ -859,9 +915,11 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
override fun onTabSelected(tab: TabLayout.Tab) {
updateTabStyle(tab, true)
}
+
override fun onTabUnselected(tab: TabLayout.Tab) {
updateTabStyle(tab, false)
}
+
override fun onTabReselected(tab: TabLayout.Tab) {
updateTabStyle(tab, true)
}
@@ -869,9 +927,12 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
}
private fun updateTabStyle(tab: TabLayout.Tab, isChecked: Boolean) {
- tab.customView?.findViewById(R.id.tab_title)?.setTypeface(if (isChecked) Typeface.DEFAULT_BOLD else Typeface.DEFAULT)
+ tab.customView?.findViewById(R.id.tab_title)
+ ?.setTypeface(if (isChecked) Typeface.DEFAULT_BOLD else Typeface.DEFAULT)
tab.customView?.findViewById(R.id.tab_title)?.setTextColor(
- if (isChecked) com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()) else com.gh.gamecenter.common.R.color.text_tertiary.toColor(requireContext())
+ if (isChecked) com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()) else com.gh.gamecenter.common.R.color.text_tertiary.toColor(
+ requireContext()
+ )
)
}
@@ -1187,7 +1248,8 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
fixedAsVGame = false,
pluginLocation = PluginLocation.only_game
)
- val isLaunch = (status == getString(com.gh.gamecenter.feature.R.string.launch) || status == getString(com.gh.gamecenter.feature.R.string.open))
+ val isLaunch =
+ (status == getString(com.gh.gamecenter.feature.R.string.launch) || status == getString(com.gh.gamecenter.feature.R.string.open))
if (SPUtils.getBoolean(
Constants.SP_SHOULD_SHOW_GAME_DETAIL_INSTALL_GUIDE,
false
@@ -1447,6 +1509,9 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
}
override fun onBackPressed(): Boolean {
+ if (acceleratorUiHelper.onBack()) {
+ return true
+ }
return when (val fragment = fragmentsList.getOrNull(bodyBinding.viewPager.currentItem)) {
is GameDetailFragment -> {
fragment.onBackPressed()
@@ -1492,8 +1557,16 @@ class GameDetailWrapperFragment: BaseLazyFragment(), IScrollable {
controlReserveBtn()
// 更新分割线样式
updateDivider()
- binding.detailLlBottom.detailLlBottom.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext()))
- binding.errorReuseToolbar.normalToolbar.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
+ binding.detailLlBottom.detailLlBottom.setBackgroundColor(
+ com.gh.gamecenter.common.R.color.ui_background.toColor(
+ requireContext()
+ )
+ )
+ binding.errorReuseToolbar.normalToolbar.setBackgroundColor(
+ com.gh.gamecenter.common.R.color.ui_surface.toColor(
+ requireContext()
+ )
+ )
initSkeleton()
skeleton?.hide()
}
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/StartingAcceleratorViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/StartingAcceleratorViewModel.kt
new file mode 100644
index 0000000000..df20247058
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/StartingAcceleratorViewModel.kt
@@ -0,0 +1,71 @@
+package com.gh.gamecenter.gamedetail
+
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.ViewModel
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.feature.entity.BaseEntity
+import com.gh.gamecenter.feature.entity.TrialEntity
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.gh.gamecenter.livedata.Event
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.login.user.UserRepository
+import com.halo.assistant.accelerator.AccelerationUseCase
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import io.reactivex.disposables.CompositeDisposable
+
+class StartingAcceleratorViewModel : ViewModel() {
+
+ private val compositeDisposable = CompositeDisposable()
+
+ val useCase = AccelerationUseCase()
+
+ private val _restartingAcceleratorAction = MutableLiveData>()
+ val restartingAcceleratorAction: LiveData> = _restartingAcceleratorAction
+ fun loadAcceleratorToken() {
+ val userId = UserManager.getInstance().userId
+ if (userId.isNotBlank()) {
+ UserRepository.getInstance().setAcceleratorToken(userId) {
+ _restartingAcceleratorAction.value = Event(it)
+ }
+ }
+ }
+
+ private val _rechargeTrailResult = MutableLiveData>()
+ val rechargeTrailResult: LiveData> = _rechargeTrailResult
+ fun rechargeTrial() {
+ val userId = UserManager.getInstance().userId
+ useCase.rechargeTrial(userId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse>() {
+ override fun onSuccess(data: BaseEntity) {
+ if (data.data?.result == true) {
+ // 刷新vip状态
+ // 这里先刷新内存数据
+ AcceleratorDataHolder.instance.setVipEntity(
+ VipEntity(
+ _vipStatus = true,
+ _isNewUser = false,
+ _isTryVip = true
+ )
+ )
+
+ _rechargeTrailResult.value = Event(true)
+ } else {
+ _rechargeTrailResult.value = Event(false)
+ }
+ }
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ _rechargeTrailResult.value = Event(false)
+ }
+ }).let(compositeDisposable::add)
+ }
+
+ override fun onCleared() {
+ super.onCleared()
+ compositeDisposable.clear()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDao.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDao.kt
new file mode 100644
index 0000000000..ffb2426d5d
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDao.kt
@@ -0,0 +1,49 @@
+package com.gh.gamecenter.gamedetail.accelerator
+
+import androidx.room.Dao
+import androidx.room.Query
+import androidx.room.Upsert
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.AcctRecord
+import io.reactivex.Observable
+import io.reactivex.Single
+
+@Dao
+interface AccelerationDao {
+
+ @Upsert
+ fun upsertAcctGameInfo(gameInfo: AcctGameInfo): Single
+
+ @Upsert
+ fun upsertAcctRecord(record: AcctRecord): Single
+
+ /**
+ * 为什么这里定义成 Observable> 而不是 Observable
+ * 因为当返回值为 Observable 时,当表中未查询到数据,则不会收到任何回调
+ * 使用 Observable>时,为查询到数据时会返回空数组
+ */
+ @Query("SELECT * FROM AcctGameInfo WHERE gameId = :gameId")
+ fun getAcctGameInfoByGameIdObservable(gameId: String): Observable>
+
+ @Query("SELECT EXISTS(SELECT 1 FROM AcctGameInfo LIMIT 1)")
+ fun hasAnyAcctGameInfo(): Single
+
+ @Query("SELECT * FROM AcctGameInfo WHERE gameId IN (:gameIds)")
+ fun queryAcctGameInfoByGameId(gameIds: List): List
+
+ @Query("SELECT * FROM AcctRecord ORDER BY createTime DESC LIMIT 20")
+ fun loadAcctRecordsObservable(): Observable>
+
+ @Query("SELECT * FROM AcctRecord ORDER BY createTime DESC LIMIT 20")
+ fun loadAcctRecords(): List
+
+ /**
+ * 获取最新的记录(根据createTime排序)
+ * @return 最新的AcctRecord记录
+ */
+ @Query("SELECT * FROM AcctRecord ORDER BY createTime DESC LIMIT 1")
+ fun loadLatestRecord(): Single
+
+ @Query("SELECT EXISTS(SELECT 1 FROM AcctRecord LIMIT 1)")
+ fun getAnyAcctRecordObservable(): Observable
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDataBase.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDataBase.kt
new file mode 100644
index 0000000000..b2e68dabd7
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AccelerationDataBase.kt
@@ -0,0 +1,93 @@
+package com.gh.gamecenter.gamedetail.accelerator
+
+import android.content.Context
+import androidx.room.Database
+import androidx.room.Room
+import androidx.room.RoomDatabase
+import androidx.room.TypeConverters
+import androidx.room.migration.Migration
+import androidx.sqlite.db.SupportSQLiteDatabase
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.AcctRecord
+import com.gh.gamecenter.room.converter.AcctGameInfoConverter
+import com.halo.assistant.HaloApp
+
+@Database(
+ entities = [AcctGameInfo::class, AcctRecord::class],
+ version = 2,
+ exportSchema = false
+)
+@TypeConverters(
+ AcctGameInfoConverter::class
+)
+abstract class AccelerationDataBase : RoomDatabase() {
+
+ abstract fun accelerationDao(): AccelerationDao
+
+ companion object {
+ private const val DATABASE_NAME: String = "acceleration_db"
+
+ val instance: AccelerationDataBase by lazy { buildDatabase(HaloApp.getInstance().application) }
+
+ private fun buildDatabase(context: Context): AccelerationDataBase {
+ return Room.databaseBuilder(context, AccelerationDataBase::class.java, DATABASE_NAME)
+ .addMigrations(MIGRATION_1_2)
+ .allowMainThreadQueries()
+ .build()
+ }
+
+ private val MIGRATION_1_2 = object : Migration(1, 2) {
+ override fun migrate(db: SupportSQLiteDatabase) {
+ // 删除表 AcctZoneListBean
+ db.execSQL("DROP TABLE IF EXISTS AcctZoneListBean")
+ // 创建新表的SQL ,游戏加速成功记录
+ createAcctRecordTab(db)
+
+ renamePrimaryKeyWithAcctGameInfoTab(db)
+
+ }
+
+ private fun createAcctRecordTab(db: SupportSQLiteDatabase) {
+ db.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS AcctRecord (
+ gameId TEXT PRIMARY KEY NOT NULL,
+ game TEXT NOT NULL,
+ zoneInfo TEXT NOT NULL,
+ hasMultiZone INTEGER NOT NULL,
+ createTime INTEGER NOT NULL
+ )
+ """
+ )
+ }
+
+ /**
+ * 重命名表 AcctGameInfo 主键
+ * 游戏旧表中没有 gameId 字段,这里将旧表数据直接清空
+ */
+ private fun renamePrimaryKeyWithAcctGameInfoTab(db: SupportSQLiteDatabase) {
+ // 1. 创建临时表(包含新的结构)
+ db.execSQL(
+ """
+ CREATE TABLE IF NOT EXISTS AcctGameInfo_temp (
+ gameId TEXT PRIMARY KEY NOT NULL,
+ accGamePkgName TEXT NOT NULL,
+ zoneInfo TEXT NOT NULL,
+ notifyGameName TEXT,
+ notifyGameTxtTitle TEXT,
+ notifyGameSmallLogo INTEGER,
+ notifyGameLargeLogo INTEGER,
+ notifyClickParam TEXT
+ )
+ """
+ )
+
+ // 2. 删除旧表
+ db.execSQL("DROP TABLE AcctGameInfo")
+
+ // 3. 重命名新表
+ db.execSQL("ALTER TABLE AcctGameInfo_temp RENAME TO AcctGameInfo")
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AcceleratorGuideView.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AcceleratorGuideView.kt
new file mode 100644
index 0000000000..b2b4d3ddd7
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/AcceleratorGuideView.kt
@@ -0,0 +1,182 @@
+package com.gh.gamecenter.gamedetail.accelerator
+
+import android.app.Activity
+import android.content.Context
+import android.graphics.*
+import android.util.AttributeSet
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import android.widget.FrameLayout
+import androidx.core.view.updateLayoutParams
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.toColor
+import com.gh.gamecenter.core.AppExecutor
+import com.gh.gamecenter.databinding.LayoutAcceleratorGuidePageBinding
+
+class AcceleratorGuideView @JvmOverloads constructor(
+ context: Context,
+ attrs: AttributeSet? = null,
+ def: Int = 0
+) : FrameLayout(context, attrs, def) {
+
+ private val binding: LayoutAcceleratorGuidePageBinding
+
+ private val rect = Rect()
+
+ private var _isShowing = false
+ val isShowing: Boolean
+ get() = _isShowing
+
+ private var isCanDismiss = false
+
+ private val guideBackgroundColor =
+ com.gh.gamecenter.common.R.color.black_alpha_60.toColor(context)
+
+ private val xfermode by lazy {
+ val mode = PorterDuff.Mode.CLEAR
+ PorterDuffXfermode(mode)
+ }
+
+ private val paint by lazy {
+ Paint().apply {
+ color = Color.YELLOW
+ isAntiAlias = true
+ isDither = true
+ }
+ }
+
+ private var onStartSpeed: (() -> Unit)? = null
+
+ init {
+ val inflater = LayoutInflater.from(context)
+ binding = LayoutAcceleratorGuidePageBinding.inflate(inflater, this, true)
+
+ binding.root.setOnClickListener {
+ // 点击透明区域消失
+ dismiss()
+ }
+
+ binding.clContainer.setOnClickListener {
+ //覆盖外部点击事件,点击此区域 guideView 不消失
+ }
+ binding.vIKnow.setOnClickListener {
+ dismiss()
+ }
+
+ binding.vSpeed.setOnClickListener {
+ dismiss()
+ onStartSpeed?.invoke()
+ }
+ }
+
+ override fun onLayout(changed: Boolean, l: Int, t: Int, r: Int, b: Int) {
+ super.onLayout(changed, left, top, right, bottom)
+
+ val bottom = rect.bottom
+ val top = bottom - binding.clContainer.height
+ val right = rect.right + BORDER_WIDTH.dip2px()
+ val left = right - binding.clContainer.width
+ binding.clContainer.layout(left, top, right, bottom)
+ }
+
+ override fun dispatchDraw(canvas: Canvas) {
+
+ val saveCount = canvas.saveLayer(0f, 0f, width.toFloat(), height.toFloat(), paint)
+ paint.color = guideBackgroundColor
+ canvas.drawRect(0f, 0f, width.toFloat(), height.toFloat(), paint)
+
+ onDrawDecoration(canvas)
+ paint.xfermode = xfermode
+
+ canvas.drawRoundRect(
+ RectF(rect),
+ HIGH_LIGHT_RADIUS.dip2px().toFloat(),
+ HIGH_LIGHT_RADIUS.dip2px().toFloat(),
+ paint
+ )
+
+ paint.xfermode = null
+ canvas.restoreToCount(saveCount)
+
+ super.dispatchDraw(canvas)
+ }
+
+ private fun onDrawDecoration(canvas: Canvas) {
+ paint.color = com.gh.gamecenter.common.R.color.ui_surface.toColor(context)
+ val border = BORDER_WIDTH.dip2px()
+ val radius = HIGH_LIGHT_RADIUS.dip2px().toFloat()
+ val dRectF = RectF(
+ rect.left.toFloat() - border,
+ rect.top.toFloat() - border,
+ rect.right.toFloat() + border,
+ rect.bottom.toFloat() + border
+ )
+ canvas.drawRoundRect(dRectF, radius, radius, paint)
+ }
+
+ private fun setHighLightRect(newRect: Rect) {
+ rect.set(newRect)
+ binding.vSpeed.updateLayoutParams {
+ width = rect.width()
+ }
+ }
+
+ fun show(anchor: View, activity: Activity, block: () -> Unit) {
+ onStartSpeed = block
+ val newRect = getLocationInWindow(anchor)
+ val content = activity.window.decorView as ViewGroup
+ layoutParams = ViewGroup.LayoutParams(
+ ViewGroup.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT
+ )
+ setHighLightRect(newRect)
+
+ content.addView(this)
+
+ setOnClickListener {
+ if (isCanDismiss) {
+ dismiss()
+ }
+ }
+ setOnClickListener {
+
+ }
+ _isShowing = true
+
+ isCanDismiss = false
+
+ AppExecutor.uiExecutor.executeWithDelay({
+ isCanDismiss = true
+ }, SHOW_MIN_DURATION)
+ }
+
+ fun dismiss() {
+ (parent as? ViewGroup)?.removeView(this)
+ _isShowing = false
+ onDismissListener?.invoke()
+ }
+
+ private var onDismissListener: (() -> Unit)? = null
+ fun setDismissListener(listener: () -> Unit) {
+ onDismissListener = listener
+ }
+
+ private fun getLocationInWindow(anchor: View): Rect {
+ val result = Rect()
+ val pos = IntArray(2)
+ anchor.getLocationInWindow(pos)
+ result.left = pos[0]
+ result.top = pos[1]
+ result.right = result.left + anchor.width
+ result.bottom = result.top + anchor.height
+ return result
+ }
+
+ companion object {
+
+ private const val BORDER_WIDTH = 4F
+ private const val HIGH_LIGHT_RADIUS = 100F
+ private const val SHOW_MIN_DURATION = 500L
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/GameDetailAcceleratorUiHelper.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/GameDetailAcceleratorUiHelper.kt
new file mode 100644
index 0000000000..d8c7f49776
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/GameDetailAcceleratorUiHelper.kt
@@ -0,0 +1,368 @@
+package com.gh.gamecenter.gamedetail.accelerator
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import com.gh.common.util.CheckLoginUtils
+import com.gh.common.util.PackageLauncher
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.core.callback.AccelerateState
+import com.gh.gamecenter.core.callback.OnAccelerateListener
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.core.utils.SPUtils
+import com.gh.gamecenter.databinding.DetailDownloadItemBinding
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_STOP
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorZoneDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.StartingAcceleratorDialogFragment
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import com.therouter.TheRouter
+
+class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBinding) {
+
+ private var iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
+
+ private var guideView: AcceleratorGuideView? = null
+
+ private lateinit var game: GameEntity
+
+ private var isGuideLayerShowing = false
+
+ private var _last: AcctGameInfo? = null
+ private val last: AcctGameInfo?
+ get() = _last
+
+ private val hasMultiZone: Boolean
+ get() = game.serviceArea.size > 1
+
+ private val isVip: Boolean
+ get() = AcceleratorDataHolder.instance.isVip
+
+ val isNewUser: Boolean
+ get() = AcceleratorDataHolder.instance.isNewUser
+
+ private val isInit: Boolean
+ get() = ::game.isInitialized
+
+ val context: Context
+ get() = binding.root.context
+
+ private var hasAnyAcctRecord = false
+
+ private val accelerationListener = object : OnAccelerateListener {
+ override fun onStateChanged(state: AccelerateState) {
+ when (state) {
+ is AccelerateState.Success -> {
+ updateSpeedUi()
+
+ }
+
+ is AccelerateState.Normal -> {
+ updateSpeedUi()
+ }
+
+ else -> Unit
+ }
+ }
+
+ override fun onProgress(progress: Int, curGamePkgName: String?, curGameZoneFlag: String?) = Unit
+
+ }
+
+ private val onDataHolderListener = object : AcceleratorDataHolder.OnDataHolderListener {
+ override fun onVipStateChanged(vip: VipEntity) {
+ showFreeTag()
+ }
+ }
+
+ fun initUi(game: GameEntity, context: Context) {
+ this.game = game
+ iAcceleratorProvider?.bindAccRelatedListener(game.getUniquePackageName() ?: "", accelerationListener)
+ AcceleratorDataHolder.instance.addListener(onDataHolderListener)
+
+ binding.vSpeedContent.setOnClickListener {
+ startAccelerating(context, game, false)
+ }
+ binding.vMoreZone.setOnClickListener {
+ showZoneList(context, game)
+ }
+
+ binding.tvStopSpeed.setOnClickListener {
+ SensorsBridge.trackNetworkAccelerationOtherButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ AcceleratorDataHolder.instance.memberType,
+ BUTTON_NAME_STOP_ACCELERATOR,
+ SOURCE_ENTRANCE_GAME_DETAIL
+ )
+
+ AcceleratorDialogFragment.show(
+ SPEED_STOP, game.getUniquePackageName() ?: "", game.id, game.name ?: "",
+ SOURCE_ENTRANCE_GAME_DETAIL, context
+ )
+ }
+
+ binding.tvEnterGame.setOnClickListener {
+ SensorsBridge.trackNetworkAccelerationOtherButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ AcceleratorDataHolder.instance.memberType,
+ BUTTON_NAME_ENTER_GAME,
+ SOURCE_ENTRANCE_GAME_DETAIL
+ )
+ PackageLauncher.launchApp(context, game, game.getUniquePackageName())
+ }
+
+ updateSpeedUi()
+ }
+
+ fun updateSpeedUi() {
+ if (!isInit || !showSpeedUi) {
+ return
+ }
+
+ val isCurrentGameAccelerating = AcceleratorDataHolder.instance.isCurrentGameAccelerating(game.id)
+
+ when {
+ isCurrentGameAccelerating -> {// 如果当前游戏正处于加速状态,则需要隐藏当前下载按钮
+ binding.detailProgressbar.goneIf(true)
+ }
+
+ binding.detailProgressbar.text == "更新" -> { // 游戏没有处于加速状态,如果 下载按钮为 “更新” 状态,则需要显示出来
+ binding.detailProgressbar.goneIf(false)
+ }
+ }
+
+ binding.clSpeed.goneIf(isCurrentGameAccelerating)
+ binding.gAccelerating.goneIf(!isCurrentGameAccelerating)
+ binding.gMoreZone.goneIf(!hasMultiZone)
+ showFreeTag()
+ binding.tvSpeed.text = if (hasMultiZone) {
+ last?.zoneName
+ } else {
+ null
+ } ?: R.string.network_acceleration.toResString()
+ }
+
+ fun checkIfShowSpeedUi(show: Boolean) {
+ if (!isInit) {
+ return
+ }
+ showSpeedUi = show
+ binding.clSpeedContainer.goneIf(!showSpeedUi) {
+ binding.detailProgressbar.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue)
+ binding.detailProgressbar.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
+ // 是否需要展示弹窗
+ showGuideLayerIfNeed()
+ updateSpeedUi()
+ }
+ }
+
+ private fun showFreeTag() {
+ binding.ivFreeVipTag.visibleIf(
+ showSpeedUi && last == null && !isGuideLayerShowing &&
+ (!CheckLoginUtils.isLogin() || isNewUser)
+ )
+ }
+
+ /**
+ * 需要同时满足这四个条件,才需要判断是否需要展示弹窗
+ */
+ var isButtonSizeDetermined = false // 底部下载按钮尺寸是否已确定
+ private var isLastLoaded = false // 本地存在区服记录(用户点击过选择区服/启动加速)
+ private var hasAnyAcctRecordLoaded = false // 已获取:是否存在加速成功的游戏记录
+ private var showSpeedUi = false // 显示加速器ui
+ fun showGuideLayerIfNeed() {
+ if (isButtonSizeDetermined && hasAnyAcctRecordLoaded && isLastLoaded && showSpeedUi) {
+ if (shouldShowGuideLayer()) {
+ isGuideLayerShowing = true
+ showGuideLayer(context)
+ }
+ showFreeTag()
+ }
+
+ }
+
+ private fun shouldShowGuideLayer() =
+ !SPUtils.getBoolean(Constants.SP_HAS_SHOW_ACCELERATION_GUIDE_LAYER) &&
+ !hasAnyAcctRecord &&
+ last == null &&
+ !isGuideLayerShowing
+
+ private fun showGuideLayer(context: Context) {
+ binding.root.post {
+ val uiListener = object : OnAcceleratorListener {
+ override fun onStartAccelerator() {
+ startAccelerating(context, game, true)
+ }
+
+ override fun onGuideLayerDismiss() {
+ isGuideLayerShowing = false
+ showFreeTag()
+ }
+
+ }
+ SPUtils.setBoolean(Constants.SP_HAS_SHOW_ACCELERATION_GUIDE_LAYER, true)
+ if (guideView == null) {
+ guideView = AcceleratorGuideView(context).apply {
+ setDismissListener(uiListener::onGuideLayerDismiss)
+ }
+ }
+ (guideView?.parent as? ViewGroup)?.removeView(guideView)
+ if (context is AppCompatActivity) {
+ SensorsBridge.trackNetworkAccelerationGuidanceDiagramShow(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: ""
+ )
+ guideView?.show(binding.clSpeedContainer, context, uiListener::onStartAccelerator)
+ }
+ }
+
+ }
+
+ fun onBack(): Boolean {
+ val isShowing = guideView?.isShowing ?: false
+ if (isShowing) {
+ guideView?.dismiss()
+ return true
+ } else {
+ return false
+ }
+ }
+
+ private fun startAccelerating(context: Context, game: GameEntity, isShowing: Boolean) {
+ if (isInvalidClick()) {
+ return
+ }
+ val lastAcctGameInfo = last
+
+ val memberType = AcceleratorDataHolder.instance.memberType
+ val districtServer = when {
+ hasMultiZone && lastAcctGameInfo != null -> lastAcctGameInfo.zoneName
+ hasMultiZone -> DISTRICT_SERVER_EMPTY
+ else -> DISTRICT_SERVER_HAVA
+ }
+ SensorsBridge.trackNetworkAccelerationButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ memberType,
+ districtServer,
+ if (isShowing) SCENE_TYPE_HAVE_GUIDE_LAYER else SCENE_TYPE_NO_GUIDE_LAYER,
+ SOURCE_ENTRANCE_GAME_DETAIL
+ )
+
+ when {
+ lastAcctGameInfo != null ->
+ doStartAccelerating(context, game, lastAcctGameInfo.zoneInfo)
+
+ hasMultiZone -> context.ifLogin("[网络加速]") {
+ AcceleratorZoneDialogFragment.show(
+ context, last?.zoneInfo?.id, game.serviceArea, game,
+ SOURCE_ENTRANCE_GAME_DETAIL
+ )
+ }
+
+ else -> {
+ val gameInfo = game.serviceArea.firstOrNull() ?: AcctGameInfo.ZoneInfo(0)
+ doStartAccelerating(context, game, gameInfo)
+ }
+ }
+ }
+
+ private fun showZoneList(context: Context, game: GameEntity) {
+ if (isInvalidClick()) {
+ return
+ }
+ context.ifLogin("[网络加速]") {
+ AcceleratorZoneDialogFragment.show(
+ context, last?.zoneInfo?.id, game.serviceArea, game,
+ SOURCE_ENTRANCE_GAME_DETAIL
+ )
+ }
+ }
+
+ private var clickTime = 0L
+
+ private fun doStartAccelerating(context: Context, game: GameEntity, zoneInfo: AcctGameInfo.ZoneInfo) {
+
+ val request = AcceleratorValidator.Request(isVip, isNewUser, game, SOURCE_ENTRANCE_GAME_DETAIL)
+ AcceleratorClient.newInstance()
+ .execute(context, request, object : AcceleratorValidator.ValidateListener {
+ override fun finished(context: Context) {
+ StartingAcceleratorDialogFragment.show(
+ context, zoneInfo, game, true, hasMultiZone,
+ SOURCE_ENTRANCE_GAME_DETAIL,
+ )
+ }
+ })
+ }
+
+ fun setCurrentAcctGameInfo(acctGameInfo: AcctGameInfo?) {
+ isLastLoaded = true
+ if (last?.zoneInfo?.id != acctGameInfo?.zoneInfo?.id) {
+ _last = acctGameInfo
+ updateSpeedUi()
+ }
+ showGuideLayerIfNeed()
+ }
+
+ fun setHasAnyAcctRecord(hasAnyAcctRecord: Boolean) {
+ hasAnyAcctRecordLoaded = true
+ this.hasAnyAcctRecord = hasAnyAcctRecord
+ showGuideLayerIfNeed()
+ }
+
+ private fun isInvalidClick(): Boolean {
+ // 300ms内只能调用一次
+ val currentTime = System.currentTimeMillis()
+ val difTime = currentTime - clickTime
+ if (difTime <= CLICK_DURATION) {
+ return true
+ }
+ clickTime = currentTime
+ return false
+ }
+
+ fun clear() {
+ iAcceleratorProvider?.unBindAccRelatedListener(accelerationListener)
+ AcceleratorDataHolder.instance.removeListener(onDataHolderListener)
+ guideView?.dismiss()
+ }
+
+
+ companion object {
+
+ private const val CLICK_DURATION = 300
+
+
+ const val DISTRICT_SERVER_EMPTY = "空"
+ const val DISTRICT_SERVER_HAVA = "有"
+ private const val SCENE_TYPE_HAVE_GUIDE_LAYER = "有引导图"
+ const val SCENE_TYPE_NO_GUIDE_LAYER = "无引导图"
+ const val SOURCE_ENTRANCE_GAME_DETAIL = "游戏详情页"
+ const val SOURCE_ENTRANCE_MY_ASSETS = "我的资产"
+ const val SOURCE_ENTRANCE_SEARCH = "搜索页"
+ const val SOURCE_ENTRANCE_RECENTLY_PLAYED = "最近在玩"
+ const val BUTTON_NAME_ENTER_GAME = "进入游戏"
+ const val BUTTON_NAME_STOP_ACCELERATOR = "停止加速"
+
+ }
+
+ interface OnAcceleratorListener {
+
+ fun onStartAccelerator()
+
+ fun onGuideLayerDismiss()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/UserVerifyDialogUtils.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/UserVerifyDialogUtils.kt
new file mode 100644
index 0000000000..a33fa9a6e9
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/UserVerifyDialogUtils.kt
@@ -0,0 +1,54 @@
+package com.gh.gamecenter.gamedetail.accelerator
+
+import android.content.Context
+import com.gh.common.util.NewLogUtils
+import com.gh.gamecenter.R
+import com.gh.gamecenter.ShellActivity
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.utils.DialogHelper
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.common.utils.toResString
+
+/**
+ * 会员功能 实名认证相关弹窗
+ */
+object UserVerifyDialogUtils {
+
+ fun showUnVerifiedDialog(context: Context, title: String, content: String) {
+ DialogHelper.showDialog(
+ context,
+ title = title,
+ content = content,
+ confirmText = R.string.go_to_real_name_authentication.toResString(),
+ cancelText = "取消",
+ confirmClickCallback = {
+ context.startActivity(
+ ShellActivity.getIntent(
+ context,
+ ShellActivity.Type.REAL_NAME_INFO
+ ).apply {
+ putExtra(EntranceConsts.KEY_SOURCE_ENTRANCE, "游戏实名")
+ putExtra(EntranceConsts.KEY_IS_FORCED_TO_CERTIFICATE, true)
+ }
+ )
+
+ NewLogUtils.logCertificationHintDialogOptionsClicked("前往实名认证")
+ SensorsBridge.trackVerificationPopupClick("前往实名认证")
+ },
+ cancelClickCallback = {
+ NewLogUtils.logCertificationHintDialogOptionsClicked("取消")
+ SensorsBridge.trackVerificationPopupClick("取消")
+ }
+ )
+ }
+
+ fun showMinorsOrVerifyingDialog(context: Context, title: String, content: String) {
+ DialogHelper.showDialog(
+ context,
+ title = title,
+ content = content,
+ confirmText = R.string.dialog_hint_confirm.toResString(),
+ cancelText = "",
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorCheckInstallInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorCheckInstallInterceptor.kt
new file mode 100644
index 0000000000..11578ce9b2
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorCheckInstallInterceptor.kt
@@ -0,0 +1,28 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import com.gh.common.util.PackageUtils
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_NOT_INSTALLED
+
+class AcceleratorCheckInstallInterceptor : AcceleratorValidator.Interceptor {
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ val request = chain.request
+ if (PackageUtils.isInstalled(context, request.game.getUniquePackageName() ?: "")) {
+ chain.proceed(context, request, listener)
+ } else {
+ val game = request.game
+ AcceleratorDialogFragment.show(
+ SPEED_NOT_INSTALLED,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ request.sourceEntrance, context
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorClient.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorClient.kt
new file mode 100644
index 0000000000..6a730f03e0
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorClient.kt
@@ -0,0 +1,29 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+
+class AcceleratorClient(
+ private vararg val interceptors: AcceleratorValidator.Interceptor,
+) {
+
+ fun execute(
+ context: Context,
+ request: AcceleratorValidator.Request,
+ listener: AcceleratorValidator.ValidateListener
+ ) {
+ val chain = RealAcceleratorInterceptorChain(interceptors.toList(), 0, request)
+ chain.proceed(context, request, listener)
+ }
+
+ companion object {
+
+ fun newInstance() = AcceleratorClient(
+ AcceleratorCheckInstallInterceptor(),
+ AcceleratorLoginInterceptor(),
+ AcceleratorPackageCheckInterceptor(),
+ AcceleratorRealNameInterceptor(),
+ AcceleratorVipInterceptor(),
+ AcceleratorStateInterceptor()
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorLoginInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorLoginInterceptor.kt
new file mode 100644
index 0000000000..ef986b418c
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorLoginInterceptor.kt
@@ -0,0 +1,18 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import com.gh.gamecenter.common.utils.ifLogin
+
+class AcceleratorLoginInterceptor : AcceleratorValidator.Interceptor {
+
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ context.ifLogin("[网络加速]") {
+ val request = chain.request
+ chain.proceed(context, request, listener)
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorPackageCheckInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorPackageCheckInterceptor.kt
new file mode 100644
index 0000000000..df60545e00
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorPackageCheckInterceptor.kt
@@ -0,0 +1,20 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import androidx.appcompat.app.AppCompatActivity
+import com.gh.common.dialog.PackageCheckDialogFragment
+
+class AcceleratorPackageCheckInterceptor : AcceleratorValidator.Interceptor {
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ val request = chain.request
+ if (context is AppCompatActivity) {
+ PackageCheckDialogFragment.show(context, request.game) {
+ chain.proceed(context, request, listener)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorRealNameInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorRealNameInterceptor.kt
new file mode 100644
index 0000000000..17825a1473
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorRealNameInterceptor.kt
@@ -0,0 +1,26 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import com.gh.common.util.TempCertificationUtils
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.gamedetail.accelerator.UserVerifyDialogUtils
+
+class AcceleratorRealNameInterceptor : AcceleratorValidator.Interceptor {
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ val request = chain.request
+ if (TempCertificationUtils.isUserVerified()) {
+ chain.proceed(context, request, listener)
+ } else {
+ UserVerifyDialogUtils.showUnVerifiedDialog(
+ context,
+ R.string.real_name_tips.toResString(),
+ R.string.acceleration_service_without_real_name_description.toResString()
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorStateInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorStateInterceptor.kt
new file mode 100644
index 0000000000..d9c073fc53
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorStateInterceptor.kt
@@ -0,0 +1,52 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_CURRENT_ACCELERATING
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorReplaceDialogFragment
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import com.therouter.TheRouter
+
+class AcceleratorStateInterceptor : AcceleratorValidator.Interceptor {
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ val request = chain.request
+ val iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
+ val isAccelerating = iAcceleratorProvider?.isCurAccSuccess() ?: false
+ if (isAccelerating) {
+ val game = request.game
+ val isCurrentAccelerating = AcceleratorDataHolder.instance.isCurrentGameAccelerating(game.id)
+ if (isCurrentAccelerating) {
+ AcceleratorDialogFragment.show(
+ SPEED_CURRENT_ACCELERATING,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ request.sourceEntrance,
+ context
+ )
+ } else {
+ AcceleratorReplaceDialogFragment.show(
+ context,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ request.sourceEntrance
+ ) {
+ if (chain.isValidContext(context)) {
+ listener?.finished(context)
+ }
+ }
+ }
+
+ } else {
+ chain.proceed(context, request, listener)
+ }
+
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorValidator.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorValidator.kt
new file mode 100644
index 0000000000..c22aff32dc
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorValidator.kt
@@ -0,0 +1,71 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import android.os.Parcelable
+import androidx.appcompat.app.AppCompatActivity
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator.Request
+import kotlinx.parcelize.Parcelize
+
+class AcceleratorValidator {
+
+ interface ValidateListener {
+
+ fun finished(context: Context)
+ }
+
+ interface Interceptor {
+
+ fun intercept(context: Context, chain: Chain, listener: ValidateListener?)
+ }
+
+ interface Chain {
+
+ val request: Request
+
+ fun proceed(context: Context, request: Request, listener: ValidateListener?)
+
+ fun isValidContext(context: Context) =
+ context is AppCompatActivity && !context.isFinishing && !context.isDestroyed
+ }
+
+ @Parcelize
+ data class Request(
+ val isVip: Boolean,
+ val isNewUser: Boolean,
+ val game: GameEntity,
+ val sourceEntrance: String
+ ):Parcelable
+
+}
+
+class RealAcceleratorInterceptorChain(
+ private val interceptors: List,
+ private val index: Int,
+ private val _request: Request
+) : AcceleratorValidator.Chain {
+
+ override val request: Request
+ get() = _request
+
+ override
+ fun proceed(context: Context, request: Request, listener: AcceleratorValidator.ValidateListener?) {
+ if (!isValidContext(context)) {
+ // 如果 context失效,说明当前页面已销毁,直接中断流程
+ return
+ }
+ if (index >= interceptors.size) {
+ // 验证完成
+ listener?.finished(context)
+ return
+ }
+ val next = RealAcceleratorInterceptorChain(
+ interceptors,
+ index + 1,
+ _request
+ )
+ interceptors[index].intercept(context, next, listener)
+ }
+}
+
+
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorVipInterceptor.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorVipInterceptor.kt
new file mode 100644
index 0000000000..be3f11be1f
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/chain/AcceleratorVipInterceptor.kt
@@ -0,0 +1,31 @@
+package com.gh.gamecenter.gamedetail.accelerator.chain
+
+import android.content.Context
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_ENABLE_VIP
+
+class AcceleratorVipInterceptor : AcceleratorValidator.Interceptor {
+
+ override fun intercept(
+ context: Context,
+ chain: AcceleratorValidator.Chain,
+ listener: AcceleratorValidator.ValidateListener?
+ ) {
+ val request = chain.request
+ val isVip = request.isVip
+ val isNewUser = request.isNewUser
+ if (isVip || isNewUser) {
+ chain.proceed(context, request, listener)
+ } else {
+ val game = request.game
+ AcceleratorDialogFragment.show(
+ SPEED_ENABLE_VIP,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ request.sourceEntrance,
+ context
+ )
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorDialogFragment.kt
new file mode 100644
index 0000000000..002a21db30
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorDialogFragment.kt
@@ -0,0 +1,260 @@
+package com.gh.gamecenter.gamedetail.accelerator.dialog
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.annotation.IntDef
+import androidx.appcompat.app.AppCompatActivity
+import com.gh.common.util.DirectUtils
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.HaloApp
+import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toColor
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.core.utils.CurrentActivityHolder
+import com.gh.gamecenter.databinding.DialogFragmentSpeedReplaceGameBinding
+import com.therouter.TheRouter
+
+/**
+ * 加速器相关的dialog
+ */
+class AcceleratorDialogFragment : BaseDialogFragment() {
+
+ private lateinit var binding: DialogFragmentSpeedReplaceGameBinding
+ private lateinit var uiHelper: SpeedDialogUiHelper
+ private var pkgName = ""
+ private var gameId = ""
+ private var gameName = ""
+ private var sourceEntrance = ""
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val type = arguments?.getInt(EntranceConsts.KEY_SPEED_TYPE) ?: 0
+ pkgName = arguments?.getString(EntranceConsts.KEY_PACKAGENAME) ?: ""
+ gameId = arguments?.getString(EntranceConsts.KEY_GAMEID) ?: ""
+ gameName = arguments?.getString(EntranceConsts.KEY_GAMENAME) ?: ""
+ sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
+ uiHelper = when (type) {
+ SPEED_ENABLE_VIP -> {
+ SensorsBridge.trackMembershipActivationDialogShow(pkgName, gameId, gameName, sourceEntrance)
+ EnableVipUi()
+ }
+
+ SPEED_START_FAILURE -> {
+ SensorsBridge.trackNetworkAccelerationFailureDialogShow(pkgName, gameId, gameName, sourceEntrance)
+ SpeedFailureUi()
+ }
+
+ SPEED_STOP -> StopSpeedUi()
+ SPEED_NOT_INSTALLED -> NotInstalledUi()
+ SPEED_CURRENT_ACCELERATING -> {
+ SensorsBridge.trackStopAcceleratingDialogShow(gameId, gameName, pkgName)
+ CurrentAcceleratingUi()
+ }
+
+ else -> throw IllegalArgumentException("请传递正确的参数 SpeedType !")
+ }
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ val createDialog = super.onCreateDialog(savedInstanceState)
+ createDialog.setCanceledOnTouchOutside(true)
+ createDialog.setCancelable(true)
+ return createDialog
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return DialogFragmentSpeedReplaceGameBinding.inflate(inflater, container, false)
+ .also {
+ binding = it
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.tvContent.setText(uiHelper.contentResId)
+ binding.tvCancel.goneIf(!uiHelper.isShowCancelButton) {
+ if (uiHelper.cancelResId != -1) {
+ binding.tvCancel.setText(uiHelper.cancelResId)
+ }
+ binding.tvCancel.setOnClickListener {
+ dismiss()
+ }
+ }
+ binding.tvSubmit.setText(uiHelper.submitResId)
+ binding.tvSubmit.setTextColor(uiHelper.submitTextColorResId.toColor(view.context))
+ binding.tvSubmit.setBackgroundResource(uiHelper.submitBackgroundResId)
+
+ binding.tvSubmit.setOnClickListener {
+ dismiss()
+ when (uiHelper) {
+
+ is EnableVipUi -> {
+ SensorsBridge.trackMembershipActivationDialogClick(
+ pkgName,
+ gameId,
+ gameName,
+ sourceEntrance
+ )
+ SensorsBridge.trackMyAssetsPageShow(pkgName, gameId, gameName, sourceEntrance)
+ DirectUtils.navigateToMyAssetsPage(requireContext(), sourceEntrance)
+ }
+
+ is SpeedFailureUi -> {
+ SensorsBridge.trackNetworkAccelerationFailureDialogClick(pkgName, gameId, gameName, sourceEntrance)
+ context?.let {
+ DirectUtils.directToWebView(it, Constants.QQ_QIDIAN_ADDRESS, "")
+ }
+ }
+
+ is StopSpeedUi, is CurrentAcceleratingUi -> {
+ TheRouter.get(IAcceleratorProvider::class.java)?.stopQyGameAccelerate()
+ }
+
+ }
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ val width = HaloApp.getInstance().resources.displayMetrics.widthPixels - 60F.dip2px()
+ val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
+ dialog?.window?.setLayout(width, height)
+ }
+
+ companion object {
+
+ const val SPEED_ENABLE_VIP = 1
+ const val SPEED_START_FAILURE = 2
+ const val SPEED_STOP = 3
+ const val SPEED_NOT_INSTALLED = 4
+ const val SPEED_CURRENT_ACCELERATING = 5
+
+ fun show(
+ @SpeedType type: Int,
+ pkgName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String,
+ context: Context
+ ) {
+ if (context is AppCompatActivity) {
+ context.supportFragmentManager
+ } else {
+ (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
+ }?.let {
+ val fragment = AcceleratorDialogFragment().apply {
+ arguments = Bundle().apply {
+ putInt(EntranceConsts.KEY_SPEED_TYPE, type)
+ putString(EntranceConsts.KEY_PACKAGENAME, pkgName)
+ putString(EntranceConsts.KEY_GAMEID, gameId)
+ putString(EntranceConsts.KEY_GAMENAME, gameName)
+ putString(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance)
+ }
+ }
+ fragment.show(it, AcceleratorDialogFragment::class.java.simpleName)
+ }
+
+ }
+ }
+
+ @IntDef(SPEED_ENABLE_VIP, SPEED_START_FAILURE, SPEED_STOP, SPEED_NOT_INSTALLED, SPEED_CURRENT_ACCELERATING)
+ @Retention(AnnotationRetention.SOURCE)
+ annotation class SpeedType
+
+ abstract class SpeedDialogUiHelper {
+
+ abstract val contentResId: Int
+
+ abstract val isShowCancelButton: Boolean
+
+ open val cancelResId: Int = -1
+
+ abstract val submitResId: Int
+
+ open val submitTextColorResId = com.gh.gamecenter.common.R.color.text_aw_primary
+
+ open val submitBackgroundResId = com.gh.gamecenter.common.R.drawable.bg_common_button_fill_blue
+ }
+
+ class EnableVipUi : SpeedDialogUiHelper() {
+ override val contentResId: Int
+ get() = R.string.enable_vip_tips
+
+ override val isShowCancelButton: Boolean
+ get() = false
+
+ override val submitResId: Int
+ get() = R.string.go_to_activate
+
+ }
+
+ class SpeedFailureUi : SpeedDialogUiHelper() {
+ override val contentResId: Int
+ get() = R.string.speed_failure_tips
+
+ override val isShowCancelButton: Boolean
+ get() = false
+
+ override val submitResId: Int
+ get() = R.string.contact_service
+
+ override val submitTextColorResId: Int
+ get() = com.gh.gamecenter.common.R.color.primary_theme
+
+ override val submitBackgroundResId: Int
+ get() = com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue
+
+ }
+
+ class StopSpeedUi : SpeedDialogUiHelper() {
+ override val contentResId: Int
+ get() = R.string.stop_speed_tips
+
+ override val isShowCancelButton: Boolean
+ get() = true
+
+ override val submitResId: Int
+ get() = R.string.stop_speed
+
+ override val cancelResId: Int
+ get() = R.string.cancel
+
+ }
+
+ class NotInstalledUi : SpeedDialogUiHelper() {
+
+ override val contentResId: Int
+ get() = R.string.speed_not_installed_tips
+
+ override val isShowCancelButton: Boolean
+ get() = false
+
+ override val submitResId: Int
+ get() = R.string.dialog_hint_confirm
+ }
+
+ class CurrentAcceleratingUi : SpeedDialogUiHelper() {
+ override val contentResId: Int
+ get() = R.string.speed_current_game_accelerating
+
+ override val isShowCancelButton: Boolean
+ get() = true
+
+ override val submitResId: Int
+ get() = R.string.stop_speed
+
+ override val cancelResId: Int
+ get() = R.string.cancel
+
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorReplaceDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorReplaceDialogFragment.kt
new file mode 100644
index 0000000000..7c07a85849
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorReplaceDialogFragment.kt
@@ -0,0 +1,116 @@
+package com.gh.gamecenter.gamedetail.accelerator.dialog
+
+import android.content.Context
+import android.os.Bundle
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.HaloApp
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.core.utils.CurrentActivityHolder
+import com.gh.gamecenter.databinding.DialogFragmentSpeedReplaceGameBinding
+import com.lightgame.dialog.BaseDialogFragment
+
+class AcceleratorReplaceDialogFragment : BaseDialogFragment() {
+
+ private lateinit var binding: DialogFragmentSpeedReplaceGameBinding
+ private var pkgName = ""
+ private var gameId = ""
+ private var gameName = ""
+ private var sourceEntrance = ""
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ pkgName = arguments?.getString(EntranceConsts.KEY_PACKAGENAME) ?: ""
+ gameId = arguments?.getString(EntranceConsts.KEY_GAMEID) ?: ""
+ gameName = arguments?.getString(EntranceConsts.KEY_GAMENAME) ?: ""
+ sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return DialogFragmentSpeedReplaceGameBinding.inflate(inflater, container, false)
+ .also {
+ binding = it
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ SensorsBridge.trackNetworkAccelerationConflictDialogShow(pkgName, gameId, gameName, sourceEntrance)
+
+ binding.tvContent.text = getString(R.string.speed_replace_game_tips, gameName)
+
+ binding.tvCancel.setOnClickListener {
+ SensorsBridge.trackNetworkAccelerationConflictDialogClick(
+ pkgName,
+ gameId,
+ gameName,
+ BUTTON_NAME_CANCEL,
+ sourceEntrance
+ )
+ dismiss()
+ }
+
+ binding.tvSubmit.setOnClickListener {
+ SensorsBridge.trackNetworkAccelerationConflictDialogClick(
+ pkgName,
+ gameId,
+ gameName,
+ BUTTON_NAME_CONTINUE,
+ sourceEntrance
+ )
+ // 加速
+ dismiss()
+ callback?.invoke()
+ }
+ }
+
+ override fun onStart() {
+ super.onStart()
+ val width = HaloApp.getInstance().resources.displayMetrics.widthPixels - 60F.dip2px()
+ val height = dialog?.window?.attributes?.height ?: ViewGroup.LayoutParams.WRAP_CONTENT
+ dialog?.window?.setLayout(width, height)
+ }
+
+ private var callback: (() -> Unit)? = null
+ fun setOnSubmitListener(callback: () -> Unit) {
+ this.callback = callback
+ }
+
+ companion object {
+
+ private const val BUTTON_NAME_CANCEL = "暂不启动"
+ private const val BUTTON_NAME_CONTINUE = "继续启动"
+
+ fun show(
+ context: Context,
+ pkgName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String,
+ callback: () -> Unit
+ ) {
+ if (context is AppCompatActivity) {
+ context.supportFragmentManager
+ } else {
+ (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
+ }?.let {
+ val fragment = AcceleratorReplaceDialogFragment().apply {
+ setOnSubmitListener(callback)
+ arguments = Bundle().apply {
+ putString(EntranceConsts.KEY_PACKAGENAME, pkgName)
+ putString(EntranceConsts.KEY_GAMEID, gameId)
+ putString(EntranceConsts.KEY_GAMENAME, gameName)
+ putString(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance)
+ }
+ }
+ fragment.show(it, AcceleratorDialogFragment::class.java.simpleName)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorZoneDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorZoneDialogFragment.kt
new file mode 100644
index 0000000000..e7ab8ebea3
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/AcceleratorZoneDialogFragment.kt
@@ -0,0 +1,213 @@
+package com.gh.gamecenter.gamedetail.accelerator.dialog
+
+import android.content.Context
+import android.graphics.Rect
+import android.os.Build
+import android.os.Bundle
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.GridLayoutManager
+import androidx.recyclerview.widget.ListAdapter
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.ViewHolder
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.utils.*
+import com.gh.gamecenter.core.utils.CurrentActivityHolder
+import com.gh.gamecenter.databinding.DialogFragmentAcceleratorZoneBinding
+import com.gh.gamecenter.databinding.RecyclerAcceleratorZoneBinding
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.gamedetail.AcceleratorZoneViewModel
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SCENE_TYPE_NO_GUIDE_LAYER
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import kotlin.math.roundToInt
+
+class AcceleratorZoneDialogFragment : BaseBottomDialogFragment() {
+
+ private val viewModel by viewModels()
+
+ private lateinit var adapter: ZoneAdapter
+
+ private var sourceEntrance = ""
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
+ }
+
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ val data =
+ if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
+ arguments?.getParcelableArrayList(EntranceConsts.KEY_DATA, AcctGameInfo.ZoneInfo::class.java)
+ } else {
+ arguments?.getParcelableArrayList(EntranceConsts.KEY_DATA)
+ } ?: listOf()
+
+ val game = arguments?.getParcelable(EntranceConsts.KEY_GAME)!!
+ val selectedId = arguments?.getInt(KEY_SELECTED_ID) ?: -1
+
+ mBinding.titleView.setOnRightClickListener {
+ dismiss()
+ }
+
+ adapter = ZoneAdapter {
+ startAccelerating(game, it)
+ dismiss()
+ }
+ mBinding.recyclerZone.layoutManager = GridLayoutManager(requireContext(), 3)
+ mBinding.recyclerZone.addItemDecoration(MyDecorationItem())
+ mBinding.recyclerZone.adapter = adapter
+ adapter.setData(selectedId, data)
+ }
+
+ private fun startAccelerating(game: GameEntity, zoneInfo: AcctGameInfo.ZoneInfo) {
+ context?.let {
+ viewModel.useCase.insertAcctGameInfo(AcctGameInfo(game.id, game.getUniquePackageName() ?: "", zoneInfo))
+ val acceleratorDataHolder = AcceleratorDataHolder.instance
+ val memberType = acceleratorDataHolder.memberType
+
+ SensorsBridge.trackNetworkAccelerationButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ memberType,
+ zoneInfo.cnName ?: "",
+ SCENE_TYPE_NO_GUIDE_LAYER,
+ sourceEntrance
+ )
+
+ val isVip = acceleratorDataHolder.isVip
+ val isNewUser = acceleratorDataHolder.isNewUser
+ val request = AcceleratorValidator.Request(isVip, isNewUser, game, sourceEntrance)
+ AcceleratorClient.newInstance()
+ .execute(it, request, object : AcceleratorValidator.ValidateListener {
+ override fun finished(context: Context) {
+ StartingAcceleratorDialogFragment.show(
+ context,
+ zoneInfo,
+ game,
+ isNeedRecord = false,
+ hasMultiZone = true,
+ sourceEntrance = sourceEntrance
+ )
+ }
+
+ })
+ }
+
+ }
+
+ companion object {
+ private const val KEY_SELECTED_ID = "key_selected_id"
+ fun show(
+ context: Context,
+ selectedId: Int? = null,
+ data: List,
+ game: GameEntity,
+ sourceEntrance: String
+ ) {
+ if (context is AppCompatActivity) {
+ context.supportFragmentManager
+ } else {
+ (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
+ }?.let {
+ val fragment = AcceleratorZoneDialogFragment().apply {
+ arguments = Bundle().apply {
+ putParcelableArrayList(EntranceConsts.KEY_DATA, data.toArrayList())
+ putParcelable(EntranceConsts.KEY_GAME, game)
+ putInt(KEY_SELECTED_ID, selectedId ?: -1)
+ putString(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance)
+ }
+ }
+ fragment.show(it, fragment::class.java.simpleName)
+ }
+ }
+ }
+
+ private class MyDecorationItem : RecyclerView.ItemDecoration() {
+
+ override fun getItemOffsets(outRect: Rect, view: View, parent: RecyclerView, state: RecyclerView.State) {
+ val position = parent.getChildAdapterPosition(view)
+ val space = (16F.dip2px() / 3F).roundToInt()
+ val (left, right) = when (position % 3) {
+ 0 -> 0 to space
+ 1 -> space to space
+ else -> space to 0
+ }
+ outRect.top = 8F.dip2px()
+ outRect.left = left
+ outRect.right = right
+ }
+ }
+
+ class ZoneAdapter(private val click: (AcctGameInfo.ZoneInfo) -> Unit) :
+ ListAdapter(diffCallback) {
+
+ private var selectedId = -1
+
+ fun setData(selectedId: Int, data: List) {
+ this.selectedId = selectedId
+ submitList(data)
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ZoneViewHolder {
+ return ZoneViewHolder(parent.toBinding())
+ }
+
+ override fun onBindViewHolder(holder: ZoneViewHolder, position: Int, payloads: MutableList) {
+ if (payloads.isNotEmpty()) {
+ updateSelectedState(holder, position)
+ } else {
+ super.onBindViewHolder(holder, position, payloads)
+ }
+ }
+
+ override fun onBindViewHolder(holder: ZoneViewHolder, position: Int) {
+ val item = getItem(position) ?: return
+ updateSelectedState(holder, item.id)
+ holder.binding.tvName.text = item.cnName
+ holder.itemView.setOnClickListener {
+ click(item)
+ }
+ }
+
+ private fun updateSelectedState(holder: ZoneViewHolder, zoneId: Int) {
+ val (textColorResId, backgroundResId) = if (selectedId == zoneId) {
+ com.gh.gamecenter.common.R.color.text_theme to R.drawable.bg_shape_2496ff_alpha_10_radius_8
+ } else {
+ com.gh.gamecenter.common.R.color.text_secondary to R.drawable.bg_shape_f8_radius_8
+ }
+ holder.binding.tvName.setTextColor(textColorResId.toColor(holder.itemView.context))
+ holder.binding.tvName.setBackgroundResource(backgroundResId)
+ }
+
+ companion object {
+
+ private val diffCallback = object : DiffUtil.ItemCallback() {
+ override fun areItemsTheSame(oldItem: AcctGameInfo.ZoneInfo, newItem: AcctGameInfo.ZoneInfo): Boolean {
+ return oldItem.id == newItem.id
+ }
+
+ override fun areContentsTheSame(
+ oldItem: AcctGameInfo.ZoneInfo,
+ newItem: AcctGameInfo.ZoneInfo
+ ): Boolean {
+ return oldItem == newItem
+ }
+
+ }
+ }
+
+ class ZoneViewHolder(val binding: RecyclerAcceleratorZoneBinding) : ViewHolder(binding.root)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/StartingAcceleratorDialogFragment.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/StartingAcceleratorDialogFragment.kt
new file mode 100644
index 0000000000..8e13e6565b
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/gamedetail/accelerator/dialog/StartingAcceleratorDialogFragment.kt
@@ -0,0 +1,280 @@
+package com.gh.gamecenter.gamedetail.accelerator.dialog
+
+import android.app.Dialog
+import android.content.Context
+import android.os.Bundle
+import android.os.Handler
+import android.os.Looper
+import android.view.LayoutInflater
+import android.view.View
+import android.view.ViewGroup
+import androidx.appcompat.app.AppCompatActivity
+import androidx.fragment.app.viewModels
+import com.gh.common.util.PackageLauncher
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
+import com.gh.gamecenter.common.constant.EntranceConsts
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.core.callback.AccelerateState
+import com.gh.gamecenter.core.callback.OnAccelerateListener
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.core.utils.CurrentActivityHolder
+import com.gh.gamecenter.core.utils.ToastUtils
+import com.gh.gamecenter.databinding.DialogFragmentStartingAcceleratorBinding
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.AcctRecord
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.gamedetail.StartingAcceleratorViewModel
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_HAVA
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_GAME_DETAIL
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_ENABLE_VIP
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_START_FAILURE
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.login.user.UserRepository
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+import com.therouter.TheRouter
+import io.reactivex.disposables.CompositeDisposable
+import kotlin.math.max
+
+class StartingAcceleratorDialogFragment : BaseDialogFragment() {
+
+ private val viewModel by viewModels()
+
+ private lateinit var binding: DialogFragmentStartingAcceleratorBinding
+
+ private lateinit var zoneInfo: AcctGameInfo.ZoneInfo
+ private lateinit var game: GameEntity
+ private var isNeedRecord: Boolean = false
+ private var iAcceleratorProvider: IAcceleratorProvider? = null
+ private var hasMultiZone = false
+ private var sourceEntrance = SOURCE_ENTRANCE_GAME_DETAIL
+
+ private val compositeDisposable = CompositeDisposable()
+ private var _progress = 0
+ private val handler = Handler(Looper.getMainLooper())
+
+ private var refreshTokenCount = 0
+
+ private val acceleratorDataHolder = AcceleratorDataHolder.instance
+
+ private val accelerationListener = object : OnAccelerateListener {
+ override fun onStateChanged(state: AccelerateState) {
+ // 如果状态能成功回,则移除计时
+ handler.removeCallbacksAndMessages(null)
+ when (state) {
+ is AccelerateState.Success -> {
+ ToastUtils.showToast("加速成功")
+ // 加速成功,启动游戏
+ PackageLauncher.launchApp(requireContext(), game, game.getUniquePackageName())
+ // 记录加速成功的游戏
+ viewModel.useCase.recordAcctGameInfo(game, zoneInfo, hasMultiZone)
+
+ trackNetworkAccelerationStartupResult("成功")
+ dismissAllowingStateLoss()
+ }
+
+ is AccelerateState.Failure -> {
+ // 有些错误需要额外处理,这里直接跳过错误提示(如:token失效,登录时token设置失败)
+ if ((state.isTokenExpired || state.isTokenEmpty) && refreshTokenCount < REFRESH_TOKEN_MAX_COUNT) {
+ // do nothing
+ } else {
+ if (!state.isSkipError) {
+ context?.let {
+ AcceleratorDialogFragment.show(
+ SPEED_START_FAILURE,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ sourceEntrance,
+ it
+ )
+ // 加速失败,上传奇游加速器log
+ unloadAcceleratorErrorLog(game)
+ }
+ }
+
+ if (state.isPermissionExpired) {
+ // 加速会员过期,刷新本地vip状态
+ val userId = UserManager.getInstance().userId
+ UserRepository.getInstance().refreshVipStatus(userId, true)
+ // 加速失败,上传奇游加速器log
+ unloadAcceleratorErrorLog(game)
+ }
+
+ dismissAllowingStateLoss()
+ trackNetworkAccelerationStartupResult("失败(${state.code})")
+ }
+ }
+
+ is AccelerateState.Normal -> {
+
+ if (state.isTokenExpired || state.isTokenEmpty) {
+ // token过期/登录时token设置失败,在此获取token并重新启动加速(最多重试三次)
+ if (refreshTokenCount < REFRESH_TOKEN_MAX_COUNT) {
+ viewModel.loadAcceleratorToken()
+ refreshTokenCount++
+ }
+ }
+ }
+
+ else -> Unit
+ }
+ }
+
+ override fun onProgress(progress: Int, curGamePkgName: String?, curGameZoneFlag: String?) {
+ _progress = max(_progress, progress)
+ binding.tvProgress.text = getString(R.string.accelerating_with_progress, "$progress")
+ }
+
+ }
+
+ private fun unloadAcceleratorErrorLog(game: GameEntity) {
+ viewModel.useCase.unloadAcceleratorErrorLog(game)
+ }
+
+ private fun trackNetworkAccelerationStartupResult(result: String) {
+ SensorsBridge.trackNetworkAccelerationStartupResult(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ acceleratorDataHolder.memberType,
+ if (hasMultiZone) zoneInfo.cnName ?: "" else DISTRICT_SERVER_HAVA,
+ result,
+ sourceEntrance
+ )
+ }
+
+ override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
+ return super.onCreateDialog(savedInstanceState).apply {
+ setCancelable(false)
+ setCanceledOnTouchOutside(false)
+ this@StartingAcceleratorDialogFragment.isCancelable = false
+ }
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
+ zoneInfo = arguments?.getParcelable(EntranceConsts.KEY_ACCT_ZONE_INFO)!!
+ game = arguments?.getParcelable(EntranceConsts.KEY_GAME)!!
+ isNeedRecord = arguments?.getBoolean(EntranceConsts.KEY_IS_NEED_RECORD) ?: false
+ hasMultiZone = arguments?.getBoolean(EntranceConsts.KEY_HAS_MULTI_ZONE) ?: false
+ sourceEntrance = arguments?.getString(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: SOURCE_ENTRANCE_GAME_DETAIL
+ }
+
+ override fun onBack(): Boolean {
+ return true
+ }
+
+ override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
+ return DialogFragmentStartingAcceleratorBinding.inflate(inflater, container, false)
+ .also {
+ binding = it
+ }.root
+ }
+
+ override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
+ super.onViewCreated(view, savedInstanceState)
+
+ binding.tvProgress.text = getString(R.string.accelerating_with_progress, "0")
+ iAcceleratorProvider?.bindAccRelatedListener("", accelerationListener)
+ val isVip = acceleratorDataHolder.isVip
+ val isNewUser = acceleratorDataHolder.isNewUser
+ if (isNewUser && !isVip) {
+ // 新用户,并且还不是vip
+ viewModel.rechargeTrial()
+ } else {
+ startGameAccelerate()
+ }
+
+ viewModel.restartingAcceleratorAction.observe(viewLifecycleOwner, EventObserver {
+ startGameAccelerate()
+ })
+
+ viewModel.rechargeTrailResult.observe(viewLifecycleOwner, EventObserver {
+ if (it) {
+ startGameAccelerate()
+ } else {
+ // 充值失败
+ AcceleratorDialogFragment.show(
+ SPEED_START_FAILURE,
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ sourceEntrance,
+ requireContext()
+ )
+ dismissAllowingStateLoss()
+ }
+
+ })
+ }
+
+
+ private fun startGameAccelerate() {
+ handler.removeCallbacksAndMessages(null)
+ handler.postDelayed({
+ // 15s以后,不管成功还是失败,都要关闭当前页面
+ dismissAllowingStateLoss()
+ }, TIME_OUT)
+ AcceleratorDataHolder.instance.acctGameRecord = AcctRecord(game.id, game, zoneInfo, hasMultiZone, 0)
+ iAcceleratorProvider?.startQyGameAccelerate(game.id, game.getUniquePackageName() ?: "", zoneInfo)
+ if (isNeedRecord) {
+ viewModel.useCase.insertAcctGameInfo(
+ AcctGameInfo(game.id, game.getUniquePackageName() ?: "", zoneInfo)
+ )
+ }
+ if (refreshTokenCount == 0) {
+
+ SensorsBridge.trackNetworkAccelerationStartup(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ acceleratorDataHolder.memberType,
+ if (hasMultiZone) zoneInfo.cnName ?: "" else DISTRICT_SERVER_HAVA,
+ sourceEntrance
+ )
+ }
+
+ }
+
+ override fun onDestroyView() {
+ handler.removeCallbacksAndMessages(null)
+ iAcceleratorProvider?.unBindAccRelatedListener(accelerationListener)
+ super.onDestroyView()
+ compositeDisposable.clear()
+
+ }
+
+ companion object {
+ private const val TIME_OUT = 1000 * 15L
+ private const val REFRESH_TOKEN_MAX_COUNT = 3
+
+ fun show(
+ context: Context,
+ zoneInfo: AcctGameInfo.ZoneInfo,
+ game: GameEntity,
+ isNeedRecord: Boolean,
+ hasMultiZone: Boolean,
+ sourceEntrance: String
+ ) {
+ if (context is AppCompatActivity) {
+ context.supportFragmentManager
+ } else {
+ (CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
+ }?.let {
+ val fragment = StartingAcceleratorDialogFragment().apply {
+ arguments = Bundle().apply {
+ putParcelable(EntranceConsts.KEY_ACCT_ZONE_INFO, zoneInfo)
+ putParcelable(EntranceConsts.KEY_GAME, game)
+ putBoolean(EntranceConsts.KEY_IS_NEED_RECORD, isNeedRecord)
+ putBoolean(EntranceConsts.KEY_HAS_MULTI_ZONE, hasMultiZone)
+ putString(EntranceConsts.KEY_SOURCE_ENTRANCE, sourceEntrance)
+ }
+ }
+ fragment.show(it, fragment::class.java.simpleName)
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt
index 5733faa506..2650e393d7 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/CustomPageViewModel.kt
@@ -84,6 +84,7 @@ class CustomPageViewModel(
addSource(repository.recentGamesItemLiveData, ::notifyItemChanged)
addSource(repository.customPluginItemLiveData, ::notifyItemChanged)
addSource(repository.wechatMiniGameCPMItemLiveData, ::notifyWechatMiniGameCPMSubjectItemChanged)
+ addSource(repository.customRecentAcceleratorItemLiveData, ::notifyItemChanged)
addSource(repository.pkDataListLiveData, ::notifyPKDataChanged)
}
@@ -415,7 +416,7 @@ class CustomPageViewModel(
private fun notifyWGameCPMABatchChanged(gameList: List, subjectId: String, page: Int) {
val nextPage: Int
- val cpmGameList = if(gameList.isEmpty()) {
+ val cpmGameList = if (gameList.isEmpty()) {
nextPage = 1// 已经到最后一页了,下一页是第一页
subjectChangedMap[SubjectChanged(subjectId, 1)]!!
} else {
@@ -475,7 +476,7 @@ class CustomPageViewModel(
if (!newData.isNullOrEmpty() && newGameList.isNotEmpty()) {
// 替换当前专题游戏数据
val position = newData.indexOfFirst {
- when(it) {
+ when (it) {
is CustomSubjectItem -> it.data.id == id
is CustomSplitSubjectItem -> it.data.id == id
else -> false
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt
index 89271acbd5..fa9660ca41 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomPageAdapter.kt
@@ -20,6 +20,7 @@ import com.gh.gamecenter.game.GameAndPosition
import com.gh.gamecenter.home.custom.CustomPageViewModel
import com.gh.gamecenter.home.custom.IGameChangedNotifier
import com.gh.gamecenter.home.custom.model.*
+import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_ACCELERATOR_RECENT_PLAYED
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_AMWAY_WALL
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_LANE
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_PAGE_ITEM_TYPE_COLLECTION_REFRESH_ICON_MATRIX
@@ -99,27 +100,29 @@ class CustomPageAdapter(
is CustomRecentGamesItem -> {
if (item.data.isNotEmpty()) {
_firstShowPosition.value = index
- return
}
}
is CustomRecentWeChatMiniGamesItem -> {
if (item.data.isNotEmpty()) {
_firstShowPosition.value = index
- return
}
}
is CustomRecentQqMiniGamesItem -> {
if (item.data.isNotEmpty()) {
_firstShowPosition.value = index
- return
+ }
+ }
+
+ is CustomRecentAcceleratorItem -> {
+ if (item.data.isNotEmpty()) {
+ _firstShowPosition.value = index
}
}
else -> {
_firstShowPosition.value = index
- return
}
}
}
@@ -273,6 +276,10 @@ class CustomPageAdapter(
CUSTOM_PAGE_ITEM_TYPE_DOUBLE_GAME_WELFARE_CARD ->
CustomDoubleWelfareCardViewHolder(viewModel, parent.toBinding())
+ CUSTOM_PAGE_ITEM_TYPE_ACCELERATOR_RECENT_PLAYED -> {
+ CustomRecentAcceleratorViewHolder(viewModel, parent.toBinding())
+ }
+
CUSTOM_PAGE_ITEM_TYPE_FOOTER -> CustomFooterViewHolder(viewModel, parent.toBinding())
CUSTOM_PAGE_ITEM_TYPE_PK -> CustomPKItemViewHolder(viewModel, lifecycleOwner, parent.toBinding())
@@ -467,7 +474,8 @@ class CustomPageAdapter(
is CustomGameTestV2Item -> {
val positionList = ArrayList()
- viewModel.getNewGameTestUserCase(item.link.link ?: "none").getDataListLiveData().value?.forEach { gameDataWrapper ->
+ viewModel.getNewGameTestUserCase(item.link.link ?: "none")
+ .getDataListLiveData().value?.forEach { gameDataWrapper ->
val gameEntity = gameDataWrapper.gameData
if (gameEntity != null) {
for (apkEntity in gameEntity.getApk()) {
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRecentAcceleratorAdapter.kt b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRecentAcceleratorAdapter.kt
new file mode 100644
index 0000000000..f73bade1be
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/adapter/CustomRecentAcceleratorAdapter.kt
@@ -0,0 +1,84 @@
+package com.gh.gamecenter.home.custom.adapter
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.common.utils.toBinding
+import com.gh.gamecenter.databinding.ItemHomeVgameBinding
+import com.gh.gamecenter.feature.entity.AcctRecord
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SCENE_TYPE_NO_GUIDE_LAYER
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_RECENTLY_PLAYED
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
+import com.gh.gamecenter.gamedetail.accelerator.dialog.StartingAcceleratorDialogFragment
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
+
+class CustomRecentAcceleratorAdapter(
+ context: Context
+) : CustomBaseChildAdapter(context) {
+
+ private val acceleratorDataHolder = AcceleratorDataHolder.instance
+
+ override fun getKey(t: AcctRecord): String {
+ return t.gameId
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): AcceleratorViewHolder =
+ AcceleratorViewHolder(acceleratorDataHolder, parent.toBinding())
+
+ override fun onBindViewHolder(holder: AcceleratorViewHolder, position: Int) {
+ holder.bindView(getItem(position))
+ }
+
+ class AcceleratorViewHolder(
+ private val acceleratorDataHolder: AcceleratorDataHolder,
+ private var mBinding: ItemHomeVgameBinding
+ ) :
+ RecyclerView.ViewHolder(mBinding.root) {
+
+ fun bindView(acctRecord: AcctRecord) {
+ val game = acctRecord.game
+ mBinding.gameIconIv.displayGameIcon(game)
+
+ mBinding.maskView.visibility = View.GONE
+ mBinding.controlTv.visibility = View.GONE
+ mBinding.progressBar.visibility = View.GONE
+ mBinding.dotView.visibility = View.GONE
+ mBinding.updateHintIv.visibility = View.GONE
+ mBinding.controlTv.visibility = View.GONE
+ mBinding.progressBar.visibility = View.GONE
+
+ val context = mBinding.root.context
+ mBinding.root.setOnClickListener {
+ SensorsBridge.trackNetworkAccelerationButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ AcceleratorDataHolder.instance.memberType,
+ acctRecord.zoneInfo.cnName ?: "",
+ SCENE_TYPE_NO_GUIDE_LAYER,
+ SOURCE_ENTRANCE_RECENTLY_PLAYED
+ )
+ val isVip = acceleratorDataHolder.isVip
+ val isNewUser = acceleratorDataHolder.isNewUser
+ val request = AcceleratorValidator.Request(isVip, isNewUser, game, SOURCE_ENTRANCE_RECENTLY_PLAYED)
+ AcceleratorClient.newInstance()
+ .execute(context, request, object : AcceleratorValidator.ValidateListener {
+ override fun finished(context: Context) {
+ StartingAcceleratorDialogFragment.show(
+ context,
+ acctRecord.zoneInfo,
+ game,
+ true,
+ acctRecord.hasMultiZone,
+ SOURCE_ENTRANCE_RECENTLY_PLAYED
+ )
+ }
+
+ })
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt
index 9bb1b09ecc..d689f85151 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageData.kt
@@ -79,8 +79,9 @@ class CustomPageData(
@SerializedName("link_common_collection")
val linkCommonCollection: CommonContentCollection? = null,
@SerializedName("link_qq_game_recently_played")
- val linkQqGameRecentlyPlayed: LinkQqGameRecentlyPlayed? = null,
- @SerializedName("link_pk")
+ val linkQqGameRecentlyPlayed: LinkRecentlyPlayed? = null,
+ @SerializedName("link_accelerator_recently_played")
+ val linkAcceleratorRecentlyPlayed: LinkRecentlyPlayed? = null,
var linkPK: PKEntity? = null,
) {
// 游戏专题
@@ -674,7 +675,7 @@ class CustomPageData(
)
}
- data class LinkQqGameRecentlyPlayed(
+ data class LinkRecentlyPlayed(
@SerializedName("home")
private val _home: String? = null,
@SerializedName("more_link")
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt
index 4a8b2625ed..04859ed5f9 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageItem.kt
@@ -9,6 +9,7 @@ import com.gh.gamecenter.entity.AmwayCommentEntity
import com.gh.gamecenter.entity.DiscoveryCardEntity
import com.gh.gamecenter.entity.HomeItemTestV2Entity
import com.gh.gamecenter.entity.SubjectEntity
+import com.gh.gamecenter.feature.entity.AcctRecord
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.vspace.VGameItemData
@@ -83,9 +84,11 @@ abstract class CustomPageItem(
const val CUSTOM_LINK_TYPE_CWZS_RECENTLY_PLAYED = "cwzs_recently_played"
const val CUSTOM_LINK_TYPE_QQ_GAME_RECENTLY_PLAYED = "qq_game_recently_played"
const val CUSTOM_LINK_TYPE_WECHAT_GAME_RECENTLY_PLAYED = "wechat_game_recently_played"
+ const val CUSTOM_LINK_TYPE_ACCELERATOR_RECENTLY_PLAYED = "accelerator_recently_played"
const val CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL = ViewPagerFragmentHelper.TYPE_QQ_MINI_GAME_COLUMN
const val CUSTOM_LINK_TYPE_WECHAT_MINI_GAME_COLUMN_DETAIL = ViewPagerFragmentHelper.TYPE_WECHAT_GAME_COLUMN
- const val CUSTOM_LINK_TYPE_WECHAT_WECHAT_GAME_CPM_COLUMN_DETAIL = ViewPagerFragmentHelper.TYPE_WECHAT_GAME_CPM_COLUMN
+ const val CUSTOM_LINK_TYPE_WECHAT_WECHAT_GAME_CPM_COLUMN_DETAIL =
+ ViewPagerFragmentHelper.TYPE_WECHAT_GAME_CPM_COLUMN
const val CUSTOM_LINK_TYPE_HALO_RECOMMEND = "halo_recommend"
const val CUSTOM_LINK_TYPE_COLUMN_COLLECTION = "column_collection" // 专题合集
const val CUSTOM_LINK_TYPE_GAME_LIST_COLLECTION = "game_list_collection"
@@ -176,6 +179,7 @@ abstract class CustomPageItem(
const val CUSTOM_PAGE_ITEM_TYPE_SINGLE_GAME_CARD = 36
const val CUSTOM_PAGE_ITEM_TYPE_DOUBLE_GAME_WELFARE_CARD = 37
const val CUSTOM_PAGE_ITEM_TYPE_PK = 38
+ const val CUSTOM_PAGE_ITEM_TYPE_ACCELERATOR_RECENT_PLAYED = 39
// 专题样式 to itemType
val subjectTypeMap: HashMap = hashMapOf(
@@ -651,7 +655,7 @@ data class CustomWeChatMiniGamesCPMSubjectItem(
data class CustomRecentQqMiniGamesItem(
private val _link: LinkEntity,
val data: List,
- val linkQqGameRecentlyPlayed: CustomPageData.LinkQqGameRecentlyPlayed,
+ val linkQqGameRecentlyPlayed: CustomPageData.LinkRecentlyPlayed,
private val _position: Int,
private val _componentPosition: Int
) : CustomPageItem(_link, _position, _componentPosition) {
@@ -671,6 +675,29 @@ data class CustomRecentQqMiniGamesItem(
}
}
+data class CustomRecentAcceleratorItem(
+ private val _link: LinkEntity,
+ val data: List,
+ val moreLink: LinkEntity?,
+ private val _position: Int,
+ private val _componentPosition: Int
+) : CustomPageItem(_link, _position, _componentPosition) {
+
+ override val itemType: Int
+ get() = CUSTOM_PAGE_ITEM_TYPE_ACCELERATOR_RECENT_PLAYED
+
+ override fun doAreContentsTheSames(other: CustomPageItem): Boolean {
+ return other is CustomRecentAcceleratorItem
+ && position == other.position
+ && componentPosition == other.componentPosition
+ && isSameData(other.data)
+ }
+
+ private fun isSameData(other: List) =
+ data.size == other.size &&
+ data.joinToString("_") { it.gameId } == other.joinToString("_") { it.gameId }
+}
+
// 插件化区域
data class CustomPluginItem(
private val _link: LinkEntity,
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt
index 3ebc95dcf8..36e6ed2455 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageRepository.kt
@@ -22,10 +22,7 @@ import com.gh.gamecenter.entity.DiscoveryCardEntity
import com.gh.gamecenter.entity.GameUpdateEntity
import com.gh.gamecenter.entity.PullDownPush
import com.gh.gamecenter.entity.SubjectEntity
-import com.gh.gamecenter.feature.entity.FloatingWindowEntity
-import com.gh.gamecenter.feature.entity.GameEntity
-import com.gh.gamecenter.feature.entity.PluginLocation
-import com.gh.gamecenter.feature.entity.TagStyleEntity
+import com.gh.gamecenter.feature.entity.*
import com.gh.gamecenter.feature.utils.ApkActiveUtils
import com.gh.gamecenter.gamedetail.rating.edit.RatingEditActivity
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.COMMON_CONTENT_COLLECTION_LAYOUT_BANNER
@@ -57,6 +54,7 @@ import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_PK
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_QQ_GAME_RECENTLY_PLAYED
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_QQ_MINI_GAME_COLUMN_DETAIL
+import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_ACCELERATOR_RECENTLY_PLAYED
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_GAME_RECENTLY_PLAYED
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_MINI_GAME_COLUMN_DETAIL
import com.gh.gamecenter.home.custom.model.CustomPageItem.Companion.CUSTOM_LINK_TYPE_WECHAT_WECHAT_GAME_CPM_COLUMN_DETAIL
@@ -103,6 +101,8 @@ class CustomPageRepository private constructor(
private var discoverCardItem: CustomDiscoverCardItem? = null
+ private var recentAcceleratorItem: CustomRecentAcceleratorItem? = null
+
private var pluginGameList = arrayListOf()
private var displayingGameIdSet = hashSetOf() // 当前正在显示的游戏 id 列表
@@ -145,12 +145,20 @@ class CustomPageRepository private constructor(
}
}
+ val customRecentAcceleratorItemLiveData: LiveData =
+ MediatorLiveData().apply {
+ addSource(shareRepository.recentlyAcctRecord) {
+ refreshRecentAcceleratorItemIfNeed(it)?.let(this::setValue)
+ }
+ }
+
val wechatMiniGameCPMItemLiveData: LiveData>> =
wGameSubjectCPMListUseCase.wechatMiniGameCPMListLiveData
val hiddenNotifications: LiveData>>
get() = shareRepository.hiddenNotifications
+
val pkDataListLiveData = MutableLiveData>()
private var lastComponentType = ""
@@ -213,6 +221,14 @@ class CustomPageRepository private constructor(
}
}
+ private fun refreshRecentAcceleratorItemIfNeed(data: List): CustomRecentAcceleratorItem? {
+ return recentAcceleratorItem?.let {
+ recentAcceleratorItem =
+ CustomRecentAcceleratorItem(it.link, data, it.moreLink, it.position, it.componentPosition)
+ recentAcceleratorItem
+ }
+ }
+
fun loadSuspendedWindow(pageId: String): Single> =
remoteDataSource.loadSuspendedWindow(pageId)
.map {
@@ -494,6 +510,23 @@ class CustomPageRepository private constructor(
}
}
+ item.link.type == CUSTOM_LINK_TYPE_ACCELERATOR_RECENTLY_PLAYED -> {
+ // 最近在玩-加速器
+ val acctGameInfos = shareRepository.loadRecentlyAcctGameInfos()
+ recentAcceleratorItem = CustomRecentAcceleratorItem(
+ item.link,
+ acctGameInfos,
+ item.linkAcceleratorRecentlyPlayed?.moreLink,
+ pageInfo.position,
+ pageInfo.componentPosition
+ ).also {
+ list.add(it)
+ }
+
+ pageInfo.positionIncrement()
+ pageInfo.componentPositionIncrement()
+ }
+
item.link.type == "plugin_area" -> {
// 插件化区域
pluginItem =
@@ -886,7 +919,7 @@ class CustomPageRepository private constructor(
COMPONENTS_SUBJECT_TYPE_GAME_HORIZONTAL_SLIDE,
COMPONENTS_SUBJECT_TYPE_GALLERY,
COMPONENTS_SUBJECT_TYPE_GALLERY_SLIDE
- -> {
+ -> {
GameSubstituteRepositoryHelper.replaceGames(
gameList = gameSubject.data!!,
displayingGameIdSet = displayingGameIdSet,
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt
index e72d5123d9..169936c86f 100644
--- a/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/model/CustomPageShareRepository.kt
@@ -3,18 +3,21 @@ package com.gh.gamecenter.home.custom.model
import androidx.annotation.MainThread
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
+import androidx.lifecycle.asLiveData
import com.gh.common.filter.RegionSettingHelper
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.Constants.SP_HIDDEN_NOTIFICATIONS
import com.gh.gamecenter.common.utils.observableToMain
+import com.gh.gamecenter.common.utils.singleToMain
import com.gh.gamecenter.common.utils.toJson
-import com.gh.gamecenter.core.utils.GsonUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.DiscoveryCardEntity
import com.gh.gamecenter.entity.DiscoveryGameCardEntity
import com.gh.gamecenter.entity.DiscoveryGameCardLabel
+import com.gh.gamecenter.feature.entity.AcctRecord
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.gamedetail.accelerator.AccelerationDataBase
import com.gh.gamecenter.retrofit.RetrofitManager
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
@@ -144,6 +147,34 @@ class CustomPageShareRepository private constructor() {
}
}
+ private val _recentlyAcctRecord = MutableLiveData>()
+ val recentlyAcctRecord: LiveData> = _recentlyAcctRecord
+ private fun loadAcctRecordsFlow() {
+ if (recentlyAcctRecord.value == null) {
+ AccelerationDataBase.instance.accelerationDao().loadAcctRecordsObservable()
+ .compose(observableToMain())
+ .subscribe({
+ _recentlyAcctRecord.value = it
+ }) {
+ /* no implement */
+ }.let(compositeDisposable::add)
+ }
+ }
+
+ /**
+ * 请注意,这个方法只能在子线程调用
+ * 此方法用于与自定义页面同步返回数据使用
+ */
+ fun loadRecentlyAcctGameInfos(): List {
+ loadAcctRecordsFlow()
+ return try {
+ AccelerationDataBase.instance.accelerationDao().loadAcctRecords()
+ } catch (e: Exception) {
+ emptyList()
+ }
+ }
+
+
// 这个方法要在用户退出首页时调用
fun onClear() {
compositeDisposable.clear()
diff --git a/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecentAcceleratorViewHolder.kt b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecentAcceleratorViewHolder.kt
new file mode 100644
index 0000000000..fab4c4bee5
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/home/custom/viewholder/CustomRecentAcceleratorViewHolder.kt
@@ -0,0 +1,113 @@
+package com.gh.gamecenter.home.custom.viewholder
+
+import android.view.ViewGroup.MarginLayoutParams
+import androidx.core.view.updateLayoutParams
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import androidx.recyclerview.widget.RecyclerView.LayoutParams
+import androidx.recyclerview.widget.RecyclerView.OnScrollListener
+import com.gh.gamecenter.R
+import com.gh.gamecenter.common.utils.dip2px
+import com.gh.gamecenter.common.utils.goneIf
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.common.utils.visibleIf
+import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
+import com.gh.gamecenter.databinding.ItemHomeRecentVgameCustomBinding
+import com.gh.gamecenter.home.custom.CustomPageViewModel
+import com.gh.gamecenter.home.custom.adapter.CustomPageAdapter
+import com.gh.gamecenter.home.custom.adapter.CustomRecentAcceleratorAdapter
+import com.gh.gamecenter.home.custom.eventlistener.RecentMiniGameItemEventHelper
+import com.gh.gamecenter.home.custom.model.CustomPageItem
+import com.gh.gamecenter.home.custom.model.CustomRecentAcceleratorItem
+
+/**
+ * 最近在玩-加速器快速启动
+ */
+class CustomRecentAcceleratorViewHolder(
+ viewModel: CustomPageViewModel,
+ var binding: ItemHomeRecentVgameCustomBinding
+) : BaseCustomViewHolder(viewModel, binding.root) {
+
+ override val childEventHelper: RecentMiniGameItemEventHelper by lazy { RecentMiniGameItemEventHelper(viewModel) }
+
+ private val adapter by lazy(LazyThreadSafetyMode.NONE) {
+ CustomRecentAcceleratorAdapter(itemView.context)
+ }
+
+ init {
+ binding.recyclerView.itemAnimator = null
+ binding.recyclerView.addItemDecoration(
+ GridSpacingItemColorDecoration(binding.root.context, 4, 0, com.gh.gamecenter.common.R.color.transparent)
+ )
+
+ binding.recyclerView.addOnScrollListener(object : OnScrollListener() {
+ override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
+ val layoutManager = binding.recyclerView.layoutManager
+ if (layoutManager is LinearLayoutManager) {
+ val scrollToEnd =
+ layoutManager.findLastCompletelyVisibleItemPosition() == (binding.recyclerView.adapter!!.itemCount - 1)
+ val item = _item
+ if (item is CustomRecentAcceleratorItem && item.moreLink != null) {
+ binding.divider.visibleIf(scrollToEnd)
+ }
+ }
+ }
+ })
+ }
+
+ override fun bindView(item: CustomPageItem) {
+ super.bindView(item)
+ binding.moreTv.goneIf(true)
+
+ if (item is CustomRecentAcceleratorItem) {
+
+ if (item.moreLink != null) {
+ binding.vspaceIv.goneIf(false)
+ binding.vspaceIv.setImageResource(R.drawable.ic_accelerator_recently_played)
+ binding.vspaceIv.setOnClickListener {
+ val link = item.moreLink
+ viewModel.navigateToLinkPage(item, link, "更多", null)
+ }
+ } else {
+ binding.vspaceIv.goneIf(true)
+ }
+ binding.divider.goneIf(item.moreLink == null)
+
+ binding.titleTv.text = R.string.recent_played.toResString()
+ val entities = item.data
+ if (entities.isEmpty()) {
+ binding.root.goneIf(true)
+ binding.clRoot.updateLayoutParams {
+ height = if (bindingAdapterPosition == 0) {
+ 1
+ } else {
+ 0
+ }
+ width = LayoutParams.MATCH_PARENT
+ if (this is MarginLayoutParams) {
+ topMargin = 0
+ }
+ }
+ } else {
+ binding.clRoot.goneIf(false)
+ binding.clRoot.updateLayoutParams {
+ height = 122F.dip2px()
+ width = LayoutParams.MATCH_PARENT
+ if (this is MarginLayoutParams) {
+ topMargin = 16F.dip2px()
+ }
+ }
+
+ if (binding.recyclerView.adapter == null) {
+ binding.recyclerView.adapter = adapter
+ }
+ adapter.submitList(entities)
+ }
+
+ val bindingAdapter = bindingAdapter
+ if (bindingAdapter is CustomPageAdapter) {
+ bindingAdapter.updateFirstItemId()
+ }
+ }
+ }
+}
diff --git a/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java
index 00e5739e7c..854b061a26 100644
--- a/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java
+++ b/app/src/main/java/com/gh/gamecenter/libao/LibaoDetailFragment.java
@@ -144,7 +144,7 @@ public class LibaoDetailFragment extends ToolbarFragment implements LibaoDetailA
private DetailViewHolder getDetailViewHolder() {
// 每次获取需要重新创建, 防止数据刷新
- return new DetailViewHolder(mCachedView, null, mGameEntity, false, mEntrance, mName, mTitle, null, false, null); // 下载按钮ViewHolder
+ return new DetailViewHolder(mCachedView, null, mGameEntity, false, mEntrance, mName, mTitle, null, false, null, null); // 下载按钮ViewHolder
}
public LibaoEntity getLibaoEntity() {
diff --git a/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java
index 95f1d8e5bb..67d7675e1b 100644
--- a/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java
+++ b/app/src/main/java/com/gh/gamecenter/newsdetail/NewsDetailFragment.java
@@ -194,6 +194,7 @@ public class NewsDetailFragment extends ToolbarFragment {
adapter.getTitle(),
mExposureEvent,
false,
+ null,
null); // 下载按钮ViewHolder
}
diff --git a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt
index 38ea7207db..38eb4bd14f 100644
--- a/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/personal/HaloPersonalFragment.kt
@@ -118,9 +118,11 @@ class HaloPersonalFragment : BaseLazyFragment() {
com.tencent.connect.common.Constants.REQUEST_LOGIN -> {
LoginHelper.onQQLoginCallback(requestCode, resultCode, data)
}
+
32973 -> {
LoginHelper.onWeiboLoginCallback(requireActivity(), requestCode, resultCode, data)
}
+
REQUEST_MESSAGE -> {
mUnreadViewModel.retry()
}
@@ -137,15 +139,24 @@ class HaloPersonalFragment : BaseLazyFragment() {
SensorsBridge.trackHaloSelfClick("用户信息", "立即登录", "", "", "")
CheckLoginUtils.checkLogin(context, "我的光环-立即登录", null)
} else {
- mStubBinding.ivArrow.performClick()
+ NewFlatLogUtils.logHaloSelfClick("用户信息", "个人主页")
+ SensorsBridge.trackHaloSelfClick("用户信息", "个人主页", "", "", "")
+ DirectUtils.directToHomeActivity(
+ requireContext(),
+ UserManager.getInstance().userId,
+ "",
+ "我的光环"
+ )
}
}
+
mStubBinding.toolbarContainer -> {
if (mUserInfoEntity == null) {
NewFlatLogUtils.logHaloSelfLogin()
CheckLoginUtils.checkLogin(context, "我的光环-立即登录", null)
}
}
+
mStubBinding.darkModeIv -> {
if (!ClickUtils.isFastDoubleClick(v.id, 1000)) {
NewFlatLogUtils.logHaloSelfClick("右上角", "切换模式")
@@ -182,6 +193,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
}
}
}
+
mStubBinding.personalMsg -> {
// 优先进入有数字提醒的消息tab,其次是有红点提醒的游戏动态,最后是没有提醒的消息tab
val defaultTabIndex = if ((mUnreadViewModel.messageUnreadCountLiveData.value?.message
@@ -208,6 +220,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
DirectUtils.directToMessageCenter(defaultTabIndex, "我的光环-消息")
}
+
mStubBinding.personalUserIcon,
mStubBinding.personalUserName -> {
if (mUserInfoEntity != null) {
@@ -226,22 +239,20 @@ class HaloPersonalFragment : BaseLazyFragment() {
CheckLoginUtils.checkLogin(context, "我的光环-立即登录", null)
}
}
- mStubBinding.ivArrow -> {
+
+ mStubBinding.tvMyAssets -> {
if (mUserInfoEntity != null) {
NewFlatLogUtils.logHaloSelfClick("用户信息", "个人主页")
SensorsBridge.trackHaloSelfClick("用户信息", "个人主页", "", "", "")
- DirectUtils.directToHomeActivity(
- requireContext(),
- UserManager.getInstance().userId,
- "",
- "我的光环"
- )
+ SensorsBridge.trackMyAssetsPageShow("无", "无", "无", "我的光环")
+ DirectUtils.navigateToMyAssetsPage(requireContext(), "我的光环-个人主页")
} else {
NewFlatLogUtils.logHaloSelfClick("用户信息", "立即登录")
SensorsBridge.trackHaloSelfClick("用户信息", "立即登录", "", "", "")
CheckLoginUtils.checkLogin(context, "我的光环-个人主页", null)
}
}
+
mStubBinding.personalBadge -> {
NewFlatLogUtils.logHaloSelfClick("用户信息", "我的徽章")
SensorsBridge.trackHaloSelfClick("用户信息", "我的徽章", "", "", "")
@@ -252,6 +263,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
mUserInfoEntity?.icon
)
}
+
mStubBinding.myGameTv -> {
if (UserManager.getInstance().isLoggedIn) {
NewFlatLogUtils.logHaloSelfClick("常用功能", "我的游戏")
@@ -268,6 +280,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
CheckLoginUtils.checkLogin(requireContext(), "我的光环-我的游戏") {}
}
}
+
mStubBinding.myPostTv -> {
if (UserManager.getInstance().isLoggedIn) {
NewFlatLogUtils.logHaloSelfClick("常用功能", "我的发布")
@@ -284,6 +297,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
CheckLoginUtils.checkLogin(requireContext(), "我的光环-我的发布") { }
}
}
+
mStubBinding.myGameCollectionTv -> {
if (UserManager.getInstance().isLoggedIn) {
NewFlatLogUtils.logHaloSelfClick("常用功能", "我的游戏单")
@@ -300,6 +314,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
CheckLoginUtils.checkLogin(requireContext(), "我的光环-我的游戏单") { }
}
}
+
mStubBinding.historyTv -> {
NewFlatLogUtils.logHaloSelfClick("常用功能", "浏览记录")
SensorsBridge.trackHaloSelfClick(
@@ -311,6 +326,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
)
startActivity(HistoryActivity.getHistoryIntent(requireContext(), "我的光环-浏览记录"))
}
+
mStubBinding.myCollectionTv -> {
if (UserManager.getInstance().isLoggedIn) {
NewFlatLogUtils.logHaloSelfClick("常用功能", "我的收藏")
@@ -606,7 +622,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
null
)
accelerateSet.add(it[0].messageId)
- SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet)
+ SPUtils.setStringSet(Constants.SP_ACCELERATE_NOTIFICATION_POP_UP_SET, accelerateSet)
}
} else {
accelerateNotificationHandler.doPreProcess(
@@ -637,16 +653,30 @@ class HaloPersonalFragment : BaseLazyFragment() {
getConstraintSet(R.id.start).run {
setVisibility(R.id.personal_badge, View.VISIBLE)
clear(R.id.personal_user_name, ConstraintSet.BOTTOM)
+ clear(R.id.tv_my_assets, ConstraintSet.TOP)
+ clear(R.id.tv_my_assets, ConstraintSet.BOTTOM)
if (!mUserInfoEntity?.getShortUserId().isNullOrEmpty()) {
setMargin(R.id.personal_user_name, ConstraintSet.TOP, 14F.dip2px())
setMargin(R.id.personal_badge, ConstraintSet.BOTTOM, 14F.dip2px())
setMargin(R.id.userIdTv, ConstraintSet.TOP, 0)
connect(R.id.userIdTv, ConstraintSet.TOP, R.id.personal_user_name, ConstraintSet.BOTTOM)
connect(R.id.userIdTv, ConstraintSet.BOTTOM, R.id.personal_badge, ConstraintSet.TOP)
+ connect(R.id.tv_my_assets, ConstraintSet.BOTTOM, R.id.personal_badge, ConstraintSet.BOTTOM)
setVisibility(R.id.userIdTv, View.VISIBLE)
} else {
- connect(R.id.personal_user_name, ConstraintSet.BOTTOM, R.id.personal_badge, ConstraintSet.TOP)
- connect(R.id.personal_badge, ConstraintSet.TOP, R.id.personal_user_name, ConstraintSet.BOTTOM)
+ connect(
+ R.id.personal_user_name,
+ ConstraintSet.BOTTOM,
+ R.id.personal_badge,
+ ConstraintSet.TOP
+ )
+ connect(
+ R.id.personal_badge,
+ ConstraintSet.TOP,
+ R.id.personal_user_name,
+ ConstraintSet.BOTTOM
+ )
+ connect(R.id.tv_my_assets, ConstraintSet.BOTTOM, R.id.personal_badge, ConstraintSet.BOTTOM)
setVerticalChainStyle(R.id.personal_user_name, ConstraintSet.CHAIN_PACKED)
setVerticalChainStyle(R.id.personal_badge, ConstraintSet.CHAIN_PACKED)
setMargin(R.id.personal_badge, ConstraintSet.TOP, 6F.dip2px())
@@ -681,6 +711,8 @@ class HaloPersonalFragment : BaseLazyFragment() {
connect(R.id.personal_user_name, ConstraintSet.BOTTOM, R.id.userIdTv, ConstraintSet.TOP)
connect(R.id.userIdTv, ConstraintSet.TOP, R.id.personal_user_name, ConstraintSet.BOTTOM)
connect(R.id.userIdTv, ConstraintSet.BOTTOM, R.id.personal_user_icon, ConstraintSet.BOTTOM)
+ connect(R.id.tv_my_assets, ConstraintSet.BOTTOM, R.id.personal_user_icon, ConstraintSet.BOTTOM)
+ connect(R.id.tv_my_assets, ConstraintSet.TOP, R.id.personal_user_icon, ConstraintSet.TOP)
setMargin(R.id.personal_user_name, ConstraintSet.TOP, 0)
setMargin(R.id.userIdTv, ConstraintSet.TOP, 3F.dip2px())
}
@@ -706,7 +738,6 @@ class HaloPersonalFragment : BaseLazyFragment() {
mStubBinding.statusBar.goneIf(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
mStubBinding.darkModeIv.goneIf(!(Config.getNightModeSetting()?.icon ?: false))
mStubBinding.darkModeIv.setImageResource(if (mIsDarkModeOn) R.drawable.ic_personal_light_mode else R.drawable.ic_personal_dark_mode)
- mStubBinding.ivArrow.enlargeTouchArea()
mStubBinding.loginMessageHint.typeface =
Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
@@ -714,7 +745,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
mStubBinding.toolbarContainer.setOnClickListener(this)
mStubBinding.darkModeIv.setOnClickListener(this)
mStubBinding.personalMsg.setOnClickListener(this)
- mStubBinding.ivArrow.setOnClickListener(this)
+ mStubBinding.tvMyAssets.setOnClickListener(this)
mStubBinding.personalUserName.setOnClickListener(this)
mStubBinding.personalUserIcon.setOnClickListener(this)
mStubBinding.personalBadge.setOnClickListener(this)
@@ -799,7 +830,10 @@ class HaloPersonalFragment : BaseLazyFragment() {
linkId = "",
linkText = ""
)
- DirectUtils.directToHelpAndFeedback(requireContext(), bundleOf(EntranceConsts.KEY_ENTRANCE to "我的光环"))
+ DirectUtils.directToHelpAndFeedback(
+ requireContext(),
+ bundleOf(EntranceConsts.KEY_ENTRANCE to "我的光环")
+ )
}
// 港澳台APP不显示帮助与反馈
root.goneIf(EnvHelper.isGATApp)
@@ -977,6 +1011,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
MotionEvent.ACTION_DOWN -> {
mStubBinding.listRefresh.isEnabled = false
}
+
MotionEvent.ACTION_UP -> {
mStubBinding.listRefresh.isEnabled = true
}
@@ -1084,7 +1119,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
fun onEventMainThread(busNetworkState: EBNetworkState) {
if (busNetworkState.isNetworkConnected
&& UserManager.getInstance().isLoggedIn
- && (mUserInfoEntity == null || TextUtils.isEmpty(UserManager.getInstance().token))
+ && (mUserViewModel.loginObsUserinfo.value == null)
) {
mUserViewModel.retryCheckLogin()
}
@@ -1164,6 +1199,9 @@ class HaloPersonalFragment : BaseLazyFragment() {
R.drawable.ic_personal_my_post
)
)
+ tvMyAssets.background =
+ com.gh.gamecenter.common.R.drawable.bg_common_button_stroke_gray.toDrawable(requireContext())
+ tvMyAssets.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(requireContext()))
}
}
}
diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
index 3ff8e20244..0c4d6f656e 100644
--- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
+++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
@@ -102,12 +102,14 @@ import com.gh.gamecenter.entity.VideoEntity;
import com.gh.gamecenter.entity.VideoTagEntity;
import com.gh.gamecenter.entity.VoteEntity;
import com.gh.gamecenter.entity.WhitePackageListEntity;
+import com.gh.gamecenter.feature.entity.AliPayEntity;
import com.gh.gamecenter.feature.entity.AnswerEntity;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.ArticleDraftEntity;
import com.gh.gamecenter.feature.entity.ArticleEntity;
import com.gh.gamecenter.feature.entity.AvatarBorderEntity;
import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
+import com.gh.gamecenter.feature.entity.BaseEntity;
import com.gh.gamecenter.feature.entity.BigEvent;
import com.gh.gamecenter.feature.entity.CommentEntity;
import com.gh.gamecenter.feature.entity.CommentnumEntity;
@@ -130,9 +132,11 @@ import com.gh.gamecenter.feature.entity.ServerCalendarGame;
import com.gh.gamecenter.feature.entity.ServerCalendarNotifySetting;
import com.gh.gamecenter.feature.entity.SettingsEntity;
import com.gh.gamecenter.feature.entity.SimulatorEntity;
+import com.gh.gamecenter.feature.entity.TrialEntity;
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.WechatPayEntity;
import com.gh.gamecenter.feature.entity.ZoneEntity;
import com.gh.gamecenter.gamedetail.entity.GameDetailSetting;
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity;
@@ -149,6 +153,7 @@ import com.gh.gamecenter.qa.entity.QuestionsIndexEntity;
import com.gh.gamecenter.qa.entity.TopCommunityCategory;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
+import com.google.protobuf.Any;
import java.util.ArrayList;
import java.util.HashMap;
@@ -3421,6 +3426,7 @@ public interface ApiService {
*/
@PATCH("app/{module}/diverter_visit_time")
Single patchDiverterVisitTime(@Path("module") String module, @Body RequestBody body);
+
/**
* 游戏单搜索(复用 游戏单合集-内容列表 (适用于刷新轮换) 接口实体)
*/
@@ -3468,4 +3474,29 @@ public interface ApiService {
*/
@GET("games/{game_id}/libao?is_all_type=true")
Single> getGameLibaoList(@Path("game_id") String gameId);
+
+ @POST("pay/wechat/goods/{goodId}/user/{userId}")
+ Single> preOrderWithWechat(@Path("goodId") String goodId, @Path("userId") String userId, @Query("type") String type);
+
+ @POST("pay/alipay/goods/{goodId}/user/{userId}")
+ Single> preOrderWithAli(@Path("goodId") String goodId, @Path("userId") String userId, @Query("type") String type);
+
+ @GET("pay/alipay/order/{orderNo}/user/{userId}")
+ Single> getAliPayResult(@Path("orderNo") String orderNo, @Path("userId") String userId, @Query("type") String type);
+
+ @GET("pay/wechat/order/{orderNo}/user/{userId}")
+ Single> getWechatPayResult(@Path("orderNo") String orderNo, @Path("userId") String userId, @Query("type") String type);
+
+ @POST("vip/user/{userId}/game")
+ Single> recordAcctGameInfo(@Path("userId") String userId, @Query("type") String type, @Body RequestBody body);
+
+ @POST("vip/user/{userId}/recharge_trial")
+ Single> rechargeTrial(@Path("userId") String userId, @Query("type") String type);
+
+ /**
+ * 上传意见反馈(奇游加速器启动失败)
+ */
+ @Headers({"Content-Type: application/json", "Accept: application/json"})
+ @POST("suggestions")
+ Single uploadAcceleratorErrorLog(@Body RequestBody toRequestBody);
}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/room/converter/AcctGameInfoConverter.kt b/app/src/main/java/com/gh/gamecenter/room/converter/AcctGameInfoConverter.kt
new file mode 100644
index 0000000000..aac5f443ff
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/room/converter/AcctGameInfoConverter.kt
@@ -0,0 +1,44 @@
+package com.gh.gamecenter.room.converter
+
+import androidx.room.TypeConverter
+import com.gh.gamecenter.common.utils.toJson
+import com.gh.gamecenter.common.utils.toObject
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.GameEntity
+import com.google.gson.Gson
+
+class AcctGameInfoConverter {
+
+ @TypeConverter
+ fun fromZoneInfo(zoneInfo: AcctGameInfo.ZoneInfo): String {
+ // 使用Gson将ZoneInfo转换为JSON字符串
+ return zoneInfo.toJson()
+ }
+
+ @TypeConverter
+ fun toZoneInfo(value: String): AcctGameInfo.ZoneInfo {
+ // 将JSON字符串转回ZoneInfo对象
+ return value.toObject() ?: AcctGameInfo.ZoneInfo(0)
+ }
+
+ @TypeConverter
+ fun fromZoneList(zoneList: List): String {
+ return zoneList.toJson()
+ }
+
+ @TypeConverter
+ fun toZoneList(value: String): List {
+ return value.toObject>() ?: listOf()
+ }
+
+ @TypeConverter
+ fun fromGame(game: GameEntity): String {
+ return game.toJson()
+ }
+
+ @TypeConverter
+ fun toGame(value: String): GameEntity? {
+ return value.toObject()
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt
index 3622c08553..4422dc4f42 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameFirstItemViewHolder.kt
@@ -7,24 +7,26 @@ import android.view.LayoutInflater
import android.view.View
import androidx.collection.ArrayMap
import androidx.fragment.app.Fragment
+import androidx.lifecycle.Observer
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.databind.BindingAdapters
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewFlatLogUtils
+import com.gh.gamecenter.SearchType
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.HtmlUtils
import com.gh.gamecenter.databinding.ItemSearchFirstBannerBinding
import com.gh.gamecenter.databinding.ItemSearchFirstCardBinding
import com.gh.gamecenter.databinding.SearchGameFirstItemBinding
+import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.feature.entity.FirstSetting
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.game.GameItemViewHolder
-import com.gh.gamecenter.SearchType
-import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.setItemCLick
import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.showContentTag
import com.lzf.easyfloat.utils.DisplayUtils
@@ -34,14 +36,14 @@ class SearchGameFirstItemViewHolder(
private val searchMap: ArrayMap,
private val entrance: String,
private val sourceEntrance: String,
- fragment: Fragment,
+ private val fragment: Fragment,
val binding: SearchGameFirstItemBinding,
private val dao: ISearchHistoryDao
) : RecyclerView.ViewHolder(binding.root) {
private var contentTag: GameEntity.ContentTag? = null
private var game: GameEntity? = null
- private var key:String = ""
+ private var key: String = ""
private val bannerBinding by lazy {
val inflater = LayoutInflater.from(binding.root.context)
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt
index 0cbcfb7a09..9021196878 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt
@@ -172,9 +172,9 @@ class SearchGameIndexAdapter(
binding.topDivider.goneIf(position == 0)
binding.gameItemIncluded.root.setPadding(
16F.dip2px(),
+ 0,
16F.dip2px(),
- 16F.dip2px(),
- 16F.dip2px()
+ 0
)
SearchGameIndexItemViewHolder.initServerType(gameEntity, binding.gameItemIncluded)
@@ -483,10 +483,21 @@ class SearchGameIndexAdapter(
)
}
+ val viewHolder = GameViewHolder(binding.gameItemIncluded.root).apply {
+ gameDownloadBtn = binding.gameItemIncluded.downloadBtn
+ gameDes = binding.gameItemIncluded.gameDes
+ gameDownloadTips = binding.gameItemIncluded.downloadTipsLottie
+ multiVersionDownloadTv = binding.gameItemIncluded.multiVersionDownloadTv
+ gameRating = binding.gameItemIncluded.gameRating
+ recommendContainer = binding.gameItemIncluded.recommendContainer
+ recommendTv = binding.gameItemIncluded.recommendTv
+ recommendIv = binding.gameItemIncluded.recommendIv
+ recommendStarInfo = binding.gameItemIncluded.recommendStarInfo
+ }
DownloadItemUtils.updateItem(
mContext,
gameEntity,
- GameViewHolder(binding.gameItemIncluded)
+ viewHolder
)
}
} else if (holder is SearchHistoryViewHolder) {
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt
index 3f957ca54c..e92b91bfe6 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexItemViewHolder.kt
@@ -11,20 +11,22 @@ import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.DrawableView
import com.gh.gamecenter.databinding.SearchGameIndexItemBinding
+import com.gh.gamecenter.databinding.SearchGameItemBinding
import com.gh.gamecenter.databinding.SearchSubjectItemBinding
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.entity.SubjectData
-import com.gh.gamecenter.feature.databinding.GameItemBinding
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.subject.SubjectActivity
-class SearchGameIndexItemViewHolder(var binding: SearchGameIndexItemBinding) : RecyclerView.ViewHolder(binding.root) {
+class SearchGameIndexItemViewHolder(
+ var binding: SearchGameIndexItemBinding
+) : RecyclerView.ViewHolder(binding.root) {
var contentTag: GameEntity.ContentTag? = null
companion object {
- fun initServerType(gameEntity: GameEntity, binding: GameItemBinding) {
+ fun initServerType(gameEntity: GameEntity, binding: SearchGameItemBinding) {
val serverLabel = gameEntity.serverLabel
when {
gameEntity.test != null -> {
@@ -38,7 +40,11 @@ class SearchGameIndexItemViewHolder(var binding: SearchGameIndexItemBinding) : R
if (gameEntity.isUseDefaultServerStyle()) {
binding.gameKaifuType.background =
com.gh.gamecenter.feature.R.drawable.server_label_default_bg.toDrawable(binding.root.context)
- binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(binding.root.context))
+ binding.gameKaifuType.setTextColor(
+ com.gh.gamecenter.common.R.color.text_secondary.toColor(
+ binding.root.context
+ )
+ )
} else {
binding.gameKaifuType.background =
DrawableView.getServerDrawable(serverLabel.color)
@@ -86,7 +92,14 @@ class SearchSubjectItemViewHolder(var binding: SearchSubjectItemBinding) : Recyc
binding.run {
subjectRv.layoutManager = LinearLayoutManager(context, RecyclerView.HORIZONTAL, false)
- subjectRv.adapter = SearchSubjectAdapter(context, entity.getFilterGame(), entity.columnId, entity.name, "($type-专题)", key) {
+ subjectRv.adapter = SearchSubjectAdapter(
+ context,
+ entity.getFilterGame(),
+ entity.columnId,
+ entity.name,
+ "($type-专题)",
+ key
+ ) {
dao?.add(key)
if (itemData.adConfig != null) {
NewFlatLogUtils.logClickGameAd(
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt
index 4b15ff5d94..552627a99c 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultAdapter.kt
@@ -7,6 +7,7 @@ import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import androidx.collection.ArrayMap
+import androidx.core.content.ContextCompat
import androidx.core.view.isVisible
import androidx.recyclerview.widget.RecyclerView
import com.gh.ad.AdDelegateHelper
@@ -40,14 +41,27 @@ import com.gh.gamecenter.databinding.*
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBSearch
-import com.gh.gamecenter.feature.databinding.GameItemBinding
+import com.gh.gamecenter.eventbus.EBStartupAcceleration
+import com.gh.gamecenter.feature.entity.AcctGameInfo
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
+import com.gh.gamecenter.feature.view.DownloadButton
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_EMPTY
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_HAVA
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SCENE_TYPE_NO_GUIDE_LAYER
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_SEARCH
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorZoneDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.StartingAcceleratorDialogFragment
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
import com.gh.gamecenter.help.HelpAndFeedbackBridge
+import com.gh.gamecenter.minigame.MiniGameSearchResultFragment
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Util_System_Keyboard
import org.greenrobot.eventbus.EventBus
@@ -66,6 +80,7 @@ class SearchGameResultAdapter(
private var searchMap: ArrayMap = ArrayMap()
private var exposureEventArray: SparseArray? = null
private var _recyclerView: RecyclerView? = null
+ private var gameIdBeingAccelerated = ""
var key: String = ""
@@ -73,11 +88,15 @@ class SearchGameResultAdapter(
private val adIdSet = hashSetOf() // 记录展示过的广告id
+ private val isNormalGameSearchPage: Boolean
+ get() = fragment !is MiniGameSearchResultFragment
+
fun clearAdIdSet() {
adIdSet.clear()
}
override fun setListData(updateData: MutableList?) {
+ gameIdBeingAccelerated = AcceleratorDataHolder.instance.getAcceleratingGameId()
exposureEventArray = SparseArray(updateData?.size ?: 0)
// 记录游戏位置
if (updateData != null) {
@@ -143,7 +162,15 @@ class SearchGameResultAdapter(
ITEM_GAME_FIRST -> {
val binding = SearchGameFirstItemBinding.inflate(mLayoutInflater, viewGroup, false)
- SearchGameFirstItemViewHolder(type, searchMap, entrance, sourceEntrance, fragment, binding, dao)
+ SearchGameFirstItemViewHolder(
+ type,
+ searchMap,
+ entrance,
+ sourceEntrance,
+ fragment,
+ binding,
+ dao
+ )
}
ItemViewType.ITEM_BODY -> {
@@ -315,8 +342,10 @@ class SearchGameResultAdapter(
override fun onViewAttachedToWindow(holder: RecyclerView.ViewHolder) {
super.onViewAttachedToWindow(holder)
- if (holder is SearchGameFirstItemViewHolder) {
- holder.onAttach(_recyclerView)
+ when (holder) {
+ is SearchGameFirstItemViewHolder -> {
+ holder.onAttach(_recyclerView)
+ }
}
}
@@ -381,7 +410,7 @@ class SearchGameResultAdapter(
MiniGameItemHelper.setMiniGameUsage(gamePlayCount, gameEntity)
officialEntryIv.isVisible = gameEntity.officialEntry
}
- binding.gameItemIncluded.root.setPadding(16F.dip2px(), 16F.dip2px(), 16F.dip2px(), 16F.dip2px())
+ binding.gameItemIncluded.root.setPadding(16F.dip2px(), 0, 16F.dip2px(), 0)
SearchGameIndexItemViewHolder.initServerType(gameEntity, binding.gameItemIncluded)
val exposureSources = ArrayList()
@@ -419,7 +448,6 @@ class SearchGameResultAdapter(
sourceEntrance,
item.gamePosition
)
-
} else {
bottomDivider.visibility = View.GONE
}
@@ -508,6 +536,43 @@ class SearchGameResultAdapter(
}
}
+ fun notifyItemAndStartupAcceleration(acctGameInfo: EBStartupAcceleration) {
+ if (isNormalGameSearchPage) {
+ positionAndPackageMap.keys.forEach {
+ if (it.contains(acctGameInfo.acctGameInfo.gameId)) {
+ val position = positionAndPackageMap[it]
+ if (position != null && mEntityList != null && position < mEntityList.size && mEntityList[position].game != null) {
+ mEntityList[position].game!!.lastAcctGame = acctGameInfo.acctGameInfo
+ notifyItemChanged(position)
+ }
+ }
+ }
+ }
+ }
+
+ fun notifyItemAccelerationState() {
+ val lastGameId = gameIdBeingAccelerated
+ val currentGameId = AcceleratorDataHolder.instance.getAcceleratingGameId()
+ gameIdBeingAccelerated = currentGameId
+
+ fun refreshAccelerationState(gameId: String) {
+ positionAndPackageMap.keys.forEach {
+ if (it.contains(gameId)) {
+ val position = positionAndPackageMap[it]
+ if (position != null && mEntityList != null && position < mEntityList.size && mEntityList[position].game != null) {
+ notifyItemChanged(position)
+ }
+ }
+ }
+ }
+ if (lastGameId.isNotBlank()) {
+ refreshAccelerationState(lastGameId)
+ }
+ if (currentGameId.isNotBlank()) {
+ refreshAccelerationState(currentGameId)
+ }
+ }
+
fun isLoadOver(): Boolean {
return mIsOver
}
@@ -831,7 +896,7 @@ class SearchGameResultAdapter(
searchMap: ArrayMap,
exposureEvent: ExposureEvent,
position: Int,
- binding: GameItemBinding,
+ binding: SearchGameItemBinding,
item: SearchItemData,
context: Context,
adapter: SearchGameResultAdapter
@@ -907,14 +972,184 @@ class SearchGameResultAdapter(
)
}
- val viewHolder = GameViewHolder(binding)
+ val viewHolder = GameViewHolder(binding.root).apply {
+ gameDownloadBtn = binding.downloadBtn
+ gameDes = binding.gameDes
+ gameDownloadTips = binding.downloadTipsLottie
+ multiVersionDownloadTv = binding.multiVersionDownloadTv
+ gameRating = binding.gameRating
+ recommendContainer = binding.recommendContainer
+ recommendTv = binding.recommendTv
+ recommendIv = binding.recommendIv
+ recommendStarInfo = binding.recommendStarInfo
+ }
- DownloadItemUtils.updateItem(context, gameEntity, viewHolder)
+ binding.downloadBtn.visibility = View.VISIBLE
+ DownloadItemUtils.updateItem(
+ context,
+ gameEntity,
+ viewHolder,
+ listener = object : DownloadButton.OnUpdateListener {
+ override fun completion(text: String) {
+ // 如果下载按钮状态为启动,则需要判断是否需要显示加速按钮
+ showSpeedButton(
+ text,
+ gameEntity,
+ adapter.listViewModel,
+ binding
+ )
+ }
+ })
binding.downloadBtn.putWidgetBusinessName("搜索列表")
}
}
+ private fun showSpeedButton(
+ text: String,
+ gameEntity: GameEntity,
+ viewModel: SearchGameResultViewModel,
+ binding: SearchGameItemBinding
+ ) {
+ val isNewUser = AcceleratorDataHolder.instance.isNewUser
+ if (text == "启动" && !gameEntity.isVGamePreferred() && gameEntity.canSpeed) {
+ val context = binding.root.context
+ binding.tvFreeVipTag.goneIf(
+ AcceleratorDataHolder.instance.hasAcctGameInfoInLocal ||
+ (CheckLoginUtils.isLogin() && !isNewUser)
+ )
+ binding.tvSpeed.goneIf(false)
+ binding.downloadBtn.goneIf(true)
+
+ val serviceArea = gameEntity.serviceArea
+ val hasMutualityZone = serviceArea.size > 1
+
+ val lastAcctGame = gameEntity.lastAcctGame
+ val isCurrentGameAccelerating = AcceleratorDataHolder.instance.isCurrentGameAccelerating(gameEntity.id)
+
+ if (isCurrentGameAccelerating) {
+ binding.vSelectRegion.goneIf(true)
+ binding.tvSelectRegion.goneIf(true)
+ binding.tvSpeed.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue)
+ binding.tvSpeed.text = R.string.accelerating.toResString()
+ binding.tvSpeed.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
+ val drawable = ContextCompat.getDrawable(context, R.drawable.ic_basic_accelerator)
+ drawable?.setTint(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
+ binding.tvSpeed.setDrawableStart(drawable)
+
+ } else {
+ binding.vSelectRegion.goneIf(!hasMutualityZone)
+ binding.tvSelectRegion.goneIf(!hasMutualityZone)
+ binding.tvSelectRegion.text = lastAcctGame?.zoneName ?: R.string.select_the_region.toResString()
+ binding.tvSpeed.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_fill_gradient_blue)
+ binding.tvSpeed.text = R.string.network_acceleration.toResString()
+ binding.tvSpeed.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context))
+ val drawable = ContextCompat.getDrawable(context, R.drawable.ic_basic_accelerator)
+ drawable?.setTint(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context))
+ binding.tvSpeed.setDrawableStart(drawable)
+ }
+
+ binding.tvSpeed.setOnClickListener {
+ val districtServer = when {
+ hasMutualityZone && lastAcctGame != null -> lastAcctGame.zoneName
+ hasMutualityZone -> DISTRICT_SERVER_EMPTY
+ else -> DISTRICT_SERVER_HAVA
+ }
+ SensorsBridge.trackNetworkAccelerationButtonClick(
+ gameEntity.getUniquePackageName() ?: "",
+ gameEntity.id,
+ gameEntity.name ?: "",
+ AcceleratorDataHolder.instance.memberType,
+ districtServer,
+ SCENE_TYPE_NO_GUIDE_LAYER,
+ SOURCE_ENTRANCE_SEARCH
+ )
+ if (CheckLoginUtils.isLogin()) {
+ when {
+ !hasMutualityZone -> {
+ // 单区服,直接启动
+ val zoneInfo = gameEntity.serviceArea.firstOrNull() ?: AcctGameInfo.ZoneInfo(0)
+ startAccelerating(gameEntity, context, zoneInfo, false)
+ }
+
+ lastAcctGame != null -> {
+ // 多区服,有缓存的加速记录
+ startAccelerating(gameEntity, context, lastAcctGame.zoneInfo, true)
+ }
+
+ else -> {
+ // 多区服,没有缓存的加速记录
+ AcceleratorZoneDialogFragment.show(
+ context,
+ null,
+ gameEntity.serviceArea.toArrayList(),
+ gameEntity,
+ SOURCE_ENTRANCE_SEARCH
+ )
+ }
+ }
+
+ } else {
+ CheckLoginUtils.checkLogin(context, null, true, "") {
+ // 登录成功,如果vip还未刷新成功,显示刷新 vip loading
+ if (AcceleratorDataHolder.instance.vipEntity == null) {
+ viewModel.showRefreshVipLoading(true)
+ }
+
+ }
+ }
+ }
+ binding.tvSpeed.isClickable = !isCurrentGameAccelerating
+
+ binding.vSelectRegion.setOnClickListener {
+ context.ifLogin("[网络加速]") {
+ AcceleratorZoneDialogFragment.show(
+ context,
+ lastAcctGame?.zoneInfo?.id,
+ gameEntity.serviceArea.toArrayList(),
+ gameEntity,
+ SOURCE_ENTRANCE_SEARCH
+ )
+ }
+ }
+
+
+ } else {
+ binding.tvFreeVipTag.goneIf(true)
+ binding.tvSpeed.goneIf(true)
+ binding.vSelectRegion.goneIf(true)
+ binding.tvSelectRegion.goneIf(true)
+ }
+ }
+
+ private fun startAccelerating(
+ gameEntity: GameEntity,
+ context: Context,
+ zoneInfo: AcctGameInfo.ZoneInfo,
+ hasMultiZone: Boolean
+ ) {
+ val vipEntity = AcceleratorDataHolder.instance.vipEntity ?: VipEntity()
+ val request = AcceleratorValidator.Request(
+ vipEntity.vipStatus,
+ vipEntity.isNewUser,
+ gameEntity,
+ SOURCE_ENTRANCE_SEARCH
+ )
+ AcceleratorClient.newInstance()
+ .execute(context, request, object : AcceleratorValidator.ValidateListener {
+ override fun finished(context: Context) {
+ StartingAcceleratorDialogFragment.show(
+ context,
+ zoneInfo,
+ gameEntity,
+ true,
+ hasMultiZone,
+ SOURCE_ENTRANCE_SEARCH
+ )
+ }
+ })
+ }
+
private fun getContentTagView(
iconRes: Int,
iconUrl: String,
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt
index d7ec9f5265..662db77285 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt
@@ -1,5 +1,6 @@
package com.gh.gamecenter.search
+import android.app.Dialog
import android.os.Bundle
import android.text.SpannableString
import android.text.Spanned
@@ -12,6 +13,7 @@ import android.view.animation.TranslateAnimation
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.fragment.app.viewModels
+import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.*
@@ -27,19 +29,28 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.entity.SuggestType
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.viewModelProvider
+import com.gh.gamecenter.core.callback.AccelerateState
+import com.gh.gamecenter.core.callback.OnAccelerateListener
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.databinding.FragmentSearchResultBinding
import com.gh.gamecenter.db.SearchHistoryDao
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
+import com.gh.gamecenter.eventbus.EBStartupAcceleration
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.feature.entity.VipEntity
import com.gh.gamecenter.help.HelpAndFeedbackBridge
+import com.gh.gamecenter.livedata.EventObserver
+import com.gh.gamecenter.minigame.MiniGameSearchResultFragment
import com.gh.gamecenter.search.viewmodel.SearchTabViewModel
import com.halo.assistant.HaloApp
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Util_System_Keyboard
+import com.therouter.TheRouter
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@@ -48,6 +59,7 @@ open class SearchGameResultFragment : ListFragment(
ownerProducer = { parentFragment ?: this }
)
+
// 上一次搜索的关键字和时间
private var previousSearchedPair: Pair = Pair("", 0)
@@ -64,6 +76,9 @@ open class SearchGameResultFragment : ListFragment
setParams(key, type)
}
+
+
+
+ if (this !is MiniGameSearchResultFragment) {
+ // 只有普通游戏搜索支持加速
+ mListViewModel.refreshVipLoading.observe(viewLifecycleOwner, EventObserver {
+ if (it) {
+ refreshVipLoadingDialog = DialogUtils.showWaitDialog(requireContext(), "数据刷新中...")
+ } else {
+ refreshVipLoadingDialog?.dismiss()
+ }
+ })
+
+ iAcceleratorProvider?.bindAccRelatedListener("", onAccelerateListener)
+
+ AcceleratorDataHolder.instance.addListener(onDataHolderListener)
+ }
+
+
}
private fun handleCloseMenuVisibility(recyclerView: RecyclerView) {
@@ -380,7 +435,8 @@ open class SearchGameResultFragment : ListFragment
+ // 获取已加速成功过的区服
+ val gameCanSpeed = games.filter { it.canSpeed }
+ val gameIds = gameCanSpeed
+ .map { it.id }
+ val acctGameList = AccelerationDataBase.instance.accelerationDao().queryAcctGameInfoByGameId(gameIds)
+ gameCanSpeed.forEach { game ->
+ game.lastAcctGame = acctGameList.find { game.id == it.gameId }
+ }
+ games
+ }
}
override fun getSearchMiniGameCPM(key: String?): Observable> {
diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt
index acfbbdbf25..3085ba272b 100644
--- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultViewModel.kt
@@ -2,6 +2,8 @@ package com.gh.gamecenter.search
import android.annotation.SuppressLint
import android.app.Application
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.ViewModelProvider
import com.gh.ad.AdDelegateHelper
@@ -21,6 +23,7 @@ import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.AdConfig
import com.gh.gamecenter.entity.SearchSubjectEntity
import com.gh.gamecenter.feature.entity.GameEntity
+import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.minigame.MiniGameSearchResultRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import io.reactivex.Observable
@@ -149,7 +152,7 @@ class SearchGameResultViewModel(
.onErrorReturnItem(mutableListOf())
.map { subject.columnId to it }
}
- Single.zip(subjectSingleList) { it.map { item -> item as Pair> } }
+ Single.zip(subjectSingleList) { it.map { item -> item as Pair> } }
.map { dataList ->
for (index in itemDataList.indices) {
val itemData = itemDataList[index]
@@ -420,6 +423,12 @@ class SearchGameResultViewModel(
return repository.getSearchGame(mSearchKey, page)
}
+ private val _refreshVipLoading = MutableLiveData>()
+ val refreshVipLoading: LiveData> = _refreshVipLoading
+ fun showRefreshVipLoading(show: Boolean) {
+ _refreshVipLoading.value = Event(show)
+ }
+
class Factory(
private val mApplication: Application,
private val mSearchKey: String?,
diff --git a/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt
index 83ab4853ef..4b9fdf44cb 100644
--- a/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt
+++ b/app/src/main/java/com/gh/gamecenter/search/viewmodel/SearchTabActivityViewModel.kt
@@ -4,12 +4,23 @@ import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import com.gh.gamecenter.feature.entity.SettingsEntity
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.login.user.UserRepository
import com.gh.gamecenter.search.SearchTabRepository
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
class SearchTabActivityViewModel : ViewModel() {
private val repository = SearchTabRepository.newInstance()
+ init {
+ // 每次进入搜索页,都需要刷新vip状态
+ val userId = UserManager.getInstance().userId
+ if (userId.isNotBlank()) {
+ UserRepository.getInstance().refreshVipStatus(userId, true)
+ }
+ }
+
private val _searchKeyAndType = MutableLiveData>()
val searchKeyAndType: LiveData> = _searchKeyAndType
diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java
index f7dcd4adae..9a30cb04fc 100644
--- a/app/src/main/java/com/halo/assistant/HaloApp.java
+++ b/app/src/main/java/com/halo/assistant/HaloApp.java
@@ -57,6 +57,7 @@ import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.iinterface.IApplication;
+import com.gh.gamecenter.core.provider.IAcceleratorProvider;
import com.gh.gamecenter.core.provider.IFlavorProvider;
import com.gh.gamecenter.core.provider.IPushProvider;
import com.gh.gamecenter.core.provider.IQGameProvider;
@@ -76,6 +77,7 @@ import com.gh.gamecenter.wrapper.MainWrapperRepository;
import com.gh.vspace.VHelper;
import com.github.piasy.biv.BigImageViewer;
import com.github.piasy.biv.loader.fresco.FrescoImageLoader;
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder;
import com.lg.ndownload.DownloadCore;
import com.lightgame.download.DownloadEntity;
import com.lightgame.download.DownloadStatus;
@@ -420,6 +422,12 @@ public class HaloApp extends MultiDexApplication {
});
}
}, delay);
+
+ IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
+ if (acceleratorProvider != null) {
+ acceleratorProvider.init(EnvHelper.isDevEnv(), this, PackageUtils.getGhVersionName());
+ }
+ AcceleratorDataHolder.Companion.getInstance().init();
}
public void getWebviewAbiList() {
diff --git a/app/src/main/java/com/halo/assistant/accelerator/AccelerationUseCase.kt b/app/src/main/java/com/halo/assistant/accelerator/AccelerationUseCase.kt
new file mode 100644
index 0000000000..c27f19dc82
--- /dev/null
+++ b/app/src/main/java/com/halo/assistant/accelerator/AccelerationUseCase.kt
@@ -0,0 +1,183 @@
+package com.halo.assistant.accelerator
+
+import android.annotation.SuppressLint
+import android.app.Activity
+import android.os.Build
+import androidx.lifecycle.LiveData
+import androidx.lifecycle.MutableLiveData
+import com.gh.gamecenter.common.entity.ErrorEntity
+import com.gh.gamecenter.common.exposure.meta.MetaUtil
+import com.gh.gamecenter.common.exposure.meta.MetaUtil.getBase64EncodedIMEI
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.common.utils.toObject
+import com.gh.gamecenter.common.utils.toRequestBody
+import com.gh.gamecenter.common.utils.toResString
+import com.gh.gamecenter.core.provider.IAppProvider
+import com.gh.gamecenter.core.provider.IPackageUtilsProvider
+import com.gh.gamecenter.eventbus.EBStartupAcceleration
+import com.gh.gamecenter.feature.entity.*
+import com.gh.gamecenter.feedback.R
+import com.gh.gamecenter.gamedetail.accelerator.AccelerationDataBase
+import com.gh.gamecenter.livedata.Event
+import com.halo.assistant.accelerator.AccelerationUseCase.UserVerifyState.Companion.ERROR_CODE_MINORS
+import com.halo.assistant.accelerator.AccelerationUseCase.UserVerifyState.Companion.ERROR_CODE_UNVERIFIED
+import com.halo.assistant.accelerator.AccelerationUseCase.UserVerifyState.Companion.ERROR_CODE_VERIFYING
+import com.halo.assistant.accelerator.repository.AccelerationRepository
+import com.therouter.TheRouter
+import io.reactivex.Single
+import io.reactivex.disposables.CompositeDisposable
+import io.reactivex.schedulers.Schedulers
+import org.greenrobot.eventbus.EventBus
+import retrofit2.HttpException
+import java.util.concurrent.TimeUnit
+
+class AccelerationUseCase {
+
+ private val compositeDisposable = CompositeDisposable()
+
+ private val repository = AccelerationRepository.newInstance()
+
+ private val _showUserVerifyDialog = MutableLiveData>()
+ val showUserVerifyDialog: LiveData> = _showUserVerifyDialog
+
+ private val _requestAlipayAction = MutableLiveData>>()
+ val requestAlipayAction: LiveData>> = _requestAlipayAction
+
+ fun preOrderWithAlipay(order: OrderEntity) {
+ repository.preOrderWithAli(order.goodId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: AliPayEntity) {
+ _requestAlipayAction.value = Event(order to data)
+ }
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ handlePreOrderError(exception)
+
+ }
+ }).let(compositeDisposable::add)
+ }
+
+
+ fun sendAlipayRequest(activity: Activity, order: OrderEntity, payEntity: AliPayEntity) {
+ repository.payWithAli(order, payEntity, activity)
+ }
+
+ private val _requestWechatPayAction = MutableLiveData>>()
+ val requestWechatPayAction: LiveData>> = _requestWechatPayAction
+ fun preOrderWithWechat(order: OrderEntity) {
+ repository.preOrderWithWechat(order.goodId)
+ .compose(singleToMain())
+ .subscribe(object : BiResponse>() {
+ override fun onSuccess(data: BaseEntity) {
+ val payEntity = data.data
+ if (payEntity != null) {
+ _requestWechatPayAction.value = Event(order to payEntity)
+ }
+ }
+
+ override fun onFailure(exception: Exception) {
+ super.onFailure(exception)
+ handlePreOrderError(exception)
+ }
+ }).let(compositeDisposable::add)
+ }
+
+ fun sendWechatPayRequest(order: OrderEntity, payEntity: WechatPayEntity) {
+ repository.sendWechatPayRequest(order, payEntity)
+ }
+
+ private fun handlePreOrderError(exception: Exception) {
+ if (exception is HttpException) {
+ val errorEntity = exception.response().errorBody()?.string()?.toObject()
+ val code = errorEntity?.code ?: 0
+ val toast = errorEntity?.toast
+ when (code) {
+ ERROR_CODE_UNVERIFIED -> UserVerifyState.UnVerified(toast)
+ ERROR_CODE_MINORS -> UserVerifyState.Minors(toast)
+ ERROR_CODE_VERIFYING -> UserVerifyState.Verifying(toast)
+ else -> null
+ }?.let {
+ _showUserVerifyDialog.value = Event(it)
+ }
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ fun insertAcctGameInfo(acctGameInfo: AcctGameInfo) {
+ if (acctGameInfo.zoneInfo.id == -1 && acctGameInfo.zoneInfo.cnName.isNullOrBlank()) {
+ // 避免数据库存入无效数据
+ return
+ }
+ EventBus.getDefault().post(EBStartupAcceleration(acctGameInfo))
+ AccelerationDataBase.instance.accelerationDao()
+ .upsertAcctGameInfo(acctGameInfo)
+ .subscribeOn(Schedulers.io())
+ .subscribe({}, {})
+ }
+
+ fun recordAcctGameInfo(game: GameEntity, zoneInfo: AcctGameInfo.ZoneInfo, hasMultiZone: Boolean) {
+ // 将加速记录提交到服务端(我的资产页面需要显示)
+ repository.recordAcctGameInfo(game.id, zoneInfo, hasMultiZone)
+ // 将加速记录保存在本地(最近在玩需要使用)
+ repository.saveAcctRecordToLocal(game, zoneInfo, hasMultiZone)
+ }
+
+ fun rechargeTrial(userId: String): Single> = repository.rechargeTrial(userId)
+
+ /**
+ * 上传奇游错误日志
+ * 由于错误日志可能没那么快生成,这里等待3s之后,在上报
+ */
+ @SuppressLint("CheckResult")
+ fun unloadAcceleratorErrorLog(game: GameEntity) {
+ Single.just(Unit)
+ .delay(3, TimeUnit.SECONDS)
+ .flatMap {
+ repository.getAcceleratorErrorLog()
+ }.flatMap {
+ val packageUtilsConfig = TheRouter.get(IPackageUtilsProvider::class.java)
+ val appProvider = TheRouter.get(IAppProvider::class.java)
+
+ val body = hashMapOf(
+ "ghversion" to packageUtilsConfig?.getGhVersionName(),
+ "channel" to appProvider?.getChannel(),
+ "type" to Build.MODEL,
+ "sdk" to Build.VERSION.SDK_INT.toString(),
+ "version" to Build.VERSION.RELEASE,
+ "source" to R.string.app_name.toResString(),
+ "jnfj" to getBase64EncodedIMEI(),
+ "from" to "",
+ "game_id" to game.id,
+ "manufacturer" to Build.MANUFACTURER,
+ "rom" to MetaUtil.getRom().name + " " + MetaUtil.getRom().versionName,
+ "suggestion_type" to "加速器失败",
+ "message" to "加速失败, 游戏ID: ${game.id}, 游戏名: ${game.name ?: ""}",
+ "log" to it
+ ).toRequestBody()
+
+ repository.uploadAcceleratorErrorLog(body)
+ }.subscribe({}, {})
+ }
+
+ fun onClear() {
+ compositeDisposable.clear()
+ }
+
+ sealed class UserVerifyState(val toast: String?) {
+
+ class UnVerified(toast: String?) : UserVerifyState(toast)
+
+ class Minors(toast: String?) : UserVerifyState(toast)
+
+ class Verifying(toast: String?) : UserVerifyState(toast)
+
+ companion object {
+ const val ERROR_CODE_UNVERIFIED = 403129
+ const val ERROR_CODE_MINORS = 403128
+ const val ERROR_CODE_VERIFYING = 403130
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/halo/assistant/accelerator/MyAssetsActivity.kt b/app/src/main/java/com/halo/assistant/accelerator/MyAssetsActivity.kt
new file mode 100644
index 0000000000..40e30d7359
--- /dev/null
+++ b/app/src/main/java/com/halo/assistant/accelerator/MyAssetsActivity.kt
@@ -0,0 +1,48 @@
+package com.halo.assistant.accelerator
+
+import android.graphics.Color
+import android.os.Bundle
+import com.gh.gamecenter.common.base.activity.BaseActivity
+import com.gh.gamecenter.common.constant.Constants
+import com.gh.gamecenter.common.constant.EntranceConsts.KEY_URL
+import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.utils.EnvHelper
+import com.gh.gamecenter.core.utils.DisplayUtils
+import com.halo.assistant.fragment.WebFragment
+import com.therouter.router.Route
+
+@Route(
+ path = RouteConsts.activity.myAssetsActivity,
+ description = "我的资产"
+)
+class MyAssetsActivity : BaseActivity() {
+
+ override fun getLayoutId(): Int {
+ return com.gh.gamecenter.common.R.layout.activity_member
+ }
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ DisplayUtils.setLightStatusBar(this, true)
+ setStatusBarColor(Color.TRANSPARENT)
+
+ val url = if (EnvHelper.isDevEnv) {
+ Constants.MY_ASSETS_DEV
+ } else {
+ Constants.MY_ASSETS
+ }
+ val bundle = Bundle().apply {
+ putString(KEY_URL, url)
+ }
+ val containerFragment = supportFragmentManager.findFragmentByTag(WebFragment::class.java.name)
+ ?: WebFragment().with(bundle)
+ // 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
+ supportFragmentManager.beginTransaction()
+ .replace(
+ com.gh.gamecenter.selector.R.id.layout_activity_content,
+ containerFragment,
+ WebFragment::class.java.name
+ )
+ .commitAllowingStateLoss()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/halo/assistant/accelerator/repository/AccelerationRepository.kt b/app/src/main/java/com/halo/assistant/accelerator/repository/AccelerationRepository.kt
new file mode 100644
index 0000000000..629e1f0a53
--- /dev/null
+++ b/app/src/main/java/com/halo/assistant/accelerator/repository/AccelerationRepository.kt
@@ -0,0 +1,223 @@
+package com.halo.assistant.accelerator.repository
+
+import android.annotation.SuppressLint
+import android.app.Activity
+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.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.core.provider.IAliPayProvider
+import com.gh.gamecenter.core.provider.IWechatPayProvider
+import com.gh.gamecenter.feature.entity.*
+import com.gh.gamecenter.feature.eventbus.EBPayState
+import com.gh.gamecenter.gamedetail.accelerator.AccelerationDao
+import com.gh.gamecenter.gamedetail.accelerator.AccelerationDataBase
+import com.gh.gamecenter.login.user.UserManager
+import com.gh.gamecenter.retrofit.RetrofitManager
+import com.gh.gamecenter.retrofit.service.ApiService
+import com.therouter.TheRouter
+import io.reactivex.Flowable
+import io.reactivex.Observable
+import io.reactivex.Single
+import io.reactivex.schedulers.Schedulers
+import okhttp3.RequestBody
+import okhttp3.ResponseBody
+import org.greenrobot.eventbus.EventBus
+import java.util.concurrent.TimeUnit
+
+/**
+ * 加速器相关的操作,统一放在这里
+ */
+class AccelerationRepository private constructor(
+ private val newApi: ApiService,
+ private val api: ApiService,
+ private val accelerationDao: AccelerationDao,
+) {
+
+ fun preOrderWithAli(goodId: String): Single {
+ val userId = UserManager.getInstance().userId
+ return newApi.preOrderWithAli(goodId, userId, VIP_TYPE)
+ .map {
+ it.data
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ fun payWithAli(order: OrderEntity, payEntity: AliPayEntity, activity: Activity) {
+ TheRouter.get(IAliPayProvider::class.java)
+ ?.sendRequest(activity, payEntity.orderStr)
+ ?.flatMap {
+ if (it == 0) {
+ // 需要后端确认支付结果
+ getAlipayResult(payEntity.orderNo)
+ } else {
+ Single.just(it)
+ }
+ }
+ ?.compose(singleToMain())
+ ?.subscribe({
+ val (payState, result) = when (it) {
+ 0 -> EBPayState.PaySuccess to RECHARGE_RESULT_SUCCESS
+ 1 -> EBPayState.PayCancel to RECHARGE_RESULT_CANCEL
+ else -> EBPayState.PayFail to RECHARGE_RESULT_FAILURE
+ }
+ if (payState is EBPayState.PaySuccess) {
+ refreshVipStatus()
+ }
+ SensorsBridge.trackMemberRechargeResult(
+ PAYMENT_TYPE_ALIPAY,
+ order.setMenuName,
+ order.paymentAmount,
+ result
+ )
+ EventBus.getDefault().post(payState)
+ }, {
+ // 支付失败
+ EventBus.getDefault().post(EBPayState.PayFail)
+ })
+ }
+
+ private fun getAlipayResult(orderNo: String): Single {
+ val userId = UserManager.getInstance().userId
+ return RetrofitManager.getInstance().newApi
+ .getAliPayResult(orderNo, userId, VIP_TYPE)
+ .retryWhen { errors ->
+ errors.zipWith(Flowable.range(1, 3)) { error, attempt ->
+ if (attempt <= 3) {
+ attempt
+ } else {
+ throw error
+ }
+ }.flatMap {
+ Flowable.timer(1, TimeUnit.SECONDS)
+ }
+ }.map {
+ if (it.data != null) 0 else 2
+ }
+ }
+
+ fun preOrderWithWechat(goodId: String): Single> {
+ val userId = UserManager.getInstance().userId
+ return newApi.preOrderWithWechat(goodId, userId, VIP_TYPE)
+ }
+
+ fun sendWechatPayRequest(order: OrderEntity, payEntity: WechatPayEntity) {
+ TheRouter.get(IWechatPayProvider::class.java)?.payRequest(order, payEntity)
+ }
+
+ fun getWechatPayResult(orderNo: String): Single {
+ val userId = UserManager.getInstance().userId
+ return RetrofitManager.getInstance().newApi
+ .getWechatPayResult(orderNo, userId, VIP_TYPE)
+ .retryWhen { errors ->
+ errors.zipWith(Flowable.range(1, 3)) { error, attempt ->
+ if (attempt <= 3) {
+ attempt
+ } else {
+ throw error
+ }
+ }.flatMap {
+ Flowable.timer(1, TimeUnit.SECONDS)
+ }
+ }.map {
+ it.data != null
+ }
+ }
+
+ private fun refreshVipStatus() {
+ // 先刷新本地状态,支付成功,肯定是付费会员
+ AcceleratorDataHolder.instance.setVipEntity(
+ VipEntity(
+ _vipStatus = true,
+ _isNewUser = false,
+ _isTryVip = false
+ )
+ )
+ }
+
+ @SuppressLint("CheckResult")
+ fun recordAcctGameInfo(gameId: String, zoneInfo: AcctGameInfo.ZoneInfo, hasMultiZone: Boolean) {
+ val userId = UserManager.getInstance().userId
+ val body =
+ hashMapOf(
+ "game_id" to gameId,
+ "service_area_id" to zoneInfo.id,
+ "service_area" to zoneInfo.cnName,
+ "is_more_service_area" to hasMultiZone
+ ).toRequestBody()
+ RetrofitManager.getInstance().newApi
+ .recordAcctGameInfo(userId, VIP_TYPE, body)
+ .subscribeOn(Schedulers.io())
+ .subscribe({}, {})
+ }
+
+ @SuppressLint("CheckResult")
+ fun saveAcctRecordToLocal(game: GameEntity, zoneInfo: AcctGameInfo.ZoneInfo, hasMultiZone: Boolean) {
+ val record =
+ AcctRecord(game.id, game, zoneInfo, hasMultiZone, System.currentTimeMillis())
+ accelerationDao.upsertAcctRecord(record)
+ .subscribeOn(Schedulers.io())
+ .subscribe({}, {})
+
+ }
+
+ fun rechargeTrial(userId: String): Single> =
+ newApi.rechargeTrial(userId, VIP_TYPE)
+
+ fun getAcceleratorErrorLog(): Single = Single.create { emitter ->
+ val logFileStorageList = TheRouter.get(IAcceleratorProvider::class.java)?.getAccLogFileStorageList()
+ val content = buildString {
+ logFileStorageList?.forEachIndexed { index, file ->
+ if (index > 0) {
+ append("\n")
+ }
+ when {
+ !file.exists() -> {
+ appendLine("\n---------------\n")
+ }
+
+ !file.canRead() -> {
+ appendLine("文件无法读取")
+ }
+
+ else -> {
+ file.bufferedReader().use { reader ->
+ reader.lineSequence().forEach { line ->
+ appendLine(line)
+ }
+ }
+ }
+ }
+ }
+ }
+ if (content.isBlank()) {
+ emitter.onSuccess("log is empty")
+ } else {
+ emitter.onSuccess(content)
+ }
+ }
+
+ fun uploadAcceleratorErrorLog(body: RequestBody): Single =
+ api.uploadAcceleratorErrorLog(body)
+
+ fun getLastAcctGameInfoObservable(gameId: String): Observable> =
+ accelerationDao.getAcctGameInfoByGameIdObservable(gameId)
+
+
+ fun getHasAnyAcctRecordObservable() = accelerationDao.getAnyAcctRecordObservable()
+
+ companion object {
+ private const val VIP_TYPE = "gjonline_vip"
+ const val RECHARGE_RESULT_SUCCESS = "充值成功"
+ private const val RECHARGE_RESULT_CANCEL = "充值取消"
+ const val RECHARGE_RESULT_FAILURE = "充值失败"
+ const val PAYMENT_TYPE_ALIPAY = "支付宝"
+ const val PAYMENT_TYPE_WECHAT = "微信"
+
+ fun newInstance() = AccelerationRepository(
+ RetrofitManager.getInstance().newApi,
+ RetrofitManager.getInstance().api,
+ AccelerationDataBase.instance.accelerationDao()
+ )
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/halo/assistant/accelerator/repository/AcceleratorDataHolder.kt b/app/src/main/java/com/halo/assistant/accelerator/repository/AcceleratorDataHolder.kt
new file mode 100644
index 0000000000..59b68a3f9b
--- /dev/null
+++ b/app/src/main/java/com/halo/assistant/accelerator/repository/AcceleratorDataHolder.kt
@@ -0,0 +1,120 @@
+package com.halo.assistant.accelerator.repository
+
+import android.annotation.SuppressLint
+import com.gh.common.util.CheckLoginUtils
+import com.gh.gamecenter.common.retrofit.BiResponse
+import com.gh.gamecenter.common.utils.singleToMain
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.feature.entity.AcctRecord
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.gh.gamecenter.gamedetail.accelerator.AccelerationDataBase
+import com.therouter.TheRouter
+
+/**
+ * 单利:保存加速相关的一些全局信息
+ * 如:vip状态,加速状态
+ */
+class AcceleratorDataHolder {
+
+ private val iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
+
+ private val listeners = mutableSetOf()
+
+ private var _hasAcctGameInfoInLocal = false
+ val hasAcctGameInfoInLocal: Boolean
+ get() = _hasAcctGameInfoInLocal
+
+ @SuppressLint("CheckResult")
+ fun init() {
+ AccelerationDataBase.instance.accelerationDao()
+ .loadLatestRecord()
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: AcctRecord) {
+ acctGameRecord = data
+ }
+ })
+ AccelerationDataBase.instance.accelerationDao()
+ .hasAnyAcctGameInfo()
+ .compose(singleToMain())
+ .subscribe(object : BiResponse() {
+ override fun onSuccess(data: Boolean) {
+ _hasAcctGameInfoInLocal = data
+ }
+ })
+
+ }
+
+ // 最后一次加速的游戏id(失败或者成功都会记录)
+ var acctGameRecord: AcctRecord? = null
+ set(value) {
+ _hasAcctGameInfoInLocal = true
+ field = value
+ }
+
+ fun isCurrentGameAccelerating(gameId: String) =
+ iAcceleratorProvider?.isCurAccSuccess() ?: false &&
+ acctGameRecord?.gameId == gameId
+
+ fun getAcceleratingGameId() = if (iAcceleratorProvider?.isCurAccSuccess() == true) {
+ acctGameRecord?.gameId ?: ""
+ } else {
+ ""
+ }
+
+ private var _vipEntity: VipEntity? = null
+ val vipEntity: VipEntity?
+ get() = _vipEntity
+
+ val isVip: Boolean
+ get() = _vipEntity?.vipStatus ?: false
+
+ val isNewUser: Boolean
+ get() = _vipEntity?.isNewUser ?: false
+
+ val memberType: String
+ get() = when {
+ !CheckLoginUtils.isLogin() -> MEMBER_TYPE_NOT_LOGIN
+ _vipEntity?.vipStatus == true && _vipEntity?.isTryVip == true -> MEMBER_TYPE_FREE_MEMBER
+ _vipEntity?.vipStatus == true -> MEMBER_TYPE_PAID_MEMBER
+ else -> MEMBER_TYPE_NONE_MEMBER
+ }
+
+ fun setVipEntity(vip: VipEntity) {
+ if (_vipEntity != vip) {
+ _vipEntity = vip
+ listeners.forEach {
+ it.onVipStateChanged(vip)
+ }
+ }
+ }
+
+ fun addListener(listener: OnDataHolderListener) {
+ listeners.add(listener)
+ }
+
+ fun removeListener(listener: OnDataHolderListener) {
+ listeners.remove(listener)
+ }
+
+ fun clear() {
+ _vipEntity = null
+ listeners.clear()
+ }
+
+ companion object {
+ private const val MEMBER_TYPE_PAID_MEMBER = "付费会员"
+ private const val MEMBER_TYPE_FREE_MEMBER = "免费会员"
+ private const val MEMBER_TYPE_NONE_MEMBER = "非会员"
+ const val MEMBER_TYPE_NOT_LOGIN = "未登录"
+
+ val instance by lazy {
+ AcceleratorDataHolder()
+ }
+ }
+
+ interface OnDataHolderListener {
+
+ fun onVipStateChanged(vip: VipEntity)
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt b/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt
index eeb1bece73..cb0a047b92 100644
--- a/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt
+++ b/app/src/main/java/com/halo/assistant/fragment/WebFragment.kt
@@ -15,6 +15,7 @@ import android.view.MenuItem
import android.view.View
import android.webkit.*
import androidx.annotation.RequiresApi
+import androidx.fragment.app.viewModels
import com.gh.common.DefaultJsApi
import com.gh.common.DefaultUrlHandler
import com.gh.common.util.*
@@ -35,7 +36,10 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.PermissionHelper.checkStoragePermissionBeforeAction
import com.gh.gamecenter.common.view.dsbridge.DWebView
import com.gh.gamecenter.core.AppExecutor.uiExecutor
+import com.gh.gamecenter.core.callback.AccelerateState
+import com.gh.gamecenter.core.callback.OnAccelerateListener
import com.gh.gamecenter.core.iinterface.IScrollable
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
import com.gh.gamecenter.core.utils.ElapsedTimeCallback
import com.gh.gamecenter.core.utils.GsonUtils.fromJson
import com.gh.gamecenter.core.utils.MtaHelper.onEventWithTime
@@ -44,11 +48,29 @@ import com.gh.gamecenter.databinding.FragmentWebBinding
import com.gh.gamecenter.databinding.FragmentWebWarningBinding
import com.gh.gamecenter.entity.WebShareEntity
import com.gh.gamecenter.eventbus.EBTypeChange
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.AcctRecordEntity
import com.gh.gamecenter.feature.entity.CommentnumEntity
+import com.gh.gamecenter.feature.entity.OrderEntity
+import com.gh.gamecenter.feature.eventbus.EBPayState
import com.gh.gamecenter.gamedetail.GameDetailWrapperFragment
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.BUTTON_NAME_STOP_ACCELERATOR
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_HAVA
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SCENE_TYPE_NO_GUIDE_LAYER
+import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_MY_ASSETS
+import com.gh.gamecenter.gamedetail.accelerator.UserVerifyDialogUtils
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorClient
+import com.gh.gamecenter.gamedetail.accelerator.chain.AcceleratorValidator
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment
+import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorDialogFragment.Companion.SPEED_STOP
+import com.gh.gamecenter.gamedetail.accelerator.dialog.StartingAcceleratorDialogFragment
+import com.gh.gamecenter.livedata.EventObserver
import com.gh.gamecenter.message.MessageDetailFragment
import com.gh.gamecenter.retrofit.RetrofitManager
+import com.halo.assistant.accelerator.AccelerationUseCase
+import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
import com.lightgame.utils.Utils
+import com.therouter.TheRouter
import com.zhihu.matisse.Matisse
import com.zhihu.matisse.MimeType
import com.zhihu.matisse.engine.impl.PicassoEngine
@@ -61,6 +83,8 @@ import kotlin.math.abs
class WebFragment : LazyFragment(), IScrollable {
+ private val viewModel by viewModels()
+
private var mBinding: FragmentWebBinding? = null
var mMenuShare: MenuItem? = null
var mMenuCollect: MenuItem? = null
@@ -114,6 +138,19 @@ class WebFragment : LazyFragment(), IScrollable {
}
}
+ private val acceleratorDataHolder = AcceleratorDataHolder.instance
+
+ private val acctListener = object : OnAccelerateListener {
+ override fun onStateChanged(state: AccelerateState) {
+ if (state is AccelerateState.Success || state is AccelerateState.Normal) {
+ onAccelerateCallback()
+ }
+ }
+
+ override fun onProgress(progress: Int, curGamePkgName: String?, curGameZoneFlag: String?) = Unit
+
+ }
+
override fun onCreate(savedInstanceState: Bundle?) {
updateIsWebViewInstalled()
super.onCreate(savedInstanceState)
@@ -311,7 +348,125 @@ class WebFragment : LazyFragment(), IScrollable {
mLeaveWebpageToHandleTitle = args.getBoolean(KEY_LEAVE_WEB_PAGE_TO_HANDLE_TITLE, false)
mWebUrl = dealWithUrl(args.getString(EntranceConsts.KEY_URL, ""))
}
- mJsApi = DefaultJsApi(requireContext(), mEntrance, this, mBbsId, mWebUrl, mForumName)
+ mJsApi = DefaultJsApi(
+ requireContext(),
+ mEntrance,
+ this,
+ mBbsId,
+ mWebUrl,
+ mForumName,
+ object : DefaultJsApi.OnWebClickListener {
+ override fun onPreOrderWithAli(order: OrderEntity) {
+ viewModel.memberUseCase.preOrderWithAlipay(order)
+ }
+
+ override fun onPreOrderWithWechat(order: OrderEntity) {
+ viewModel.memberUseCase.preOrderWithWechat(order)
+ }
+
+ override fun onStartGameAccelerate(accInfo: AcctRecordEntity.AccInfo) {
+ context?.let {
+ val game = accInfo.game
+ val hasMultiZone = accInfo.isMoreServiceArea
+ val zoneInfo = AcctGameInfo.ZoneInfo(accInfo.serviceAreaId, accInfo.serviceArea)
+
+ val memberType = acceleratorDataHolder.memberType
+
+ SensorsBridge.trackNetworkAccelerationButtonClick(
+ game.getUniquePackageName() ?: "",
+ game.id,
+ game.name ?: "",
+ memberType,
+ if (hasMultiZone) accInfo.serviceArea else DISTRICT_SERVER_HAVA,
+ SCENE_TYPE_NO_GUIDE_LAYER,
+ SOURCE_ENTRANCE_MY_ASSETS
+ )
+
+ // h5 页面点击加速
+ val isVip = acceleratorDataHolder.isVip
+ val request = AcceleratorValidator.Request(isVip, false, game, SOURCE_ENTRANCE_MY_ASSETS)
+ AcceleratorClient.newInstance()
+ .execute(it, request, object : AcceleratorValidator.ValidateListener {
+ override fun finished(context: Context) {
+ StartingAcceleratorDialogFragment.show(
+ context,
+ zoneInfo,
+ game,
+ false,
+ hasMultiZone,
+ SOURCE_ENTRANCE_MY_ASSETS
+ )
+ }
+ })
+ }
+
+ }
+
+ override fun onStopGameAccelerate() {
+ context?.let {
+ val acctGameRecord = AcceleratorDataHolder.instance.acctGameRecord
+
+ SensorsBridge.trackNetworkAccelerationOtherButtonClick(
+ acctGameRecord?.game?.getUniquePackageName() ?: "",
+ acctGameRecord?.gameId ?: "",
+ acctGameRecord?.game?.name ?: "",
+ acceleratorDataHolder.memberType,
+ BUTTON_NAME_STOP_ACCELERATOR,
+ SOURCE_ENTRANCE_MY_ASSETS
+ )
+ AcceleratorDialogFragment.show(
+ SPEED_STOP,
+ acctGameRecord?.game?.getUniquePackageName() ?: "",
+ acctGameRecord?.gameId ?: "",
+ acctGameRecord?.game?.name ?: "",
+ SOURCE_ENTRANCE_MY_ASSETS,
+ it
+ )
+ }
+
+ }
+
+ })
+
+ with(viewModel.memberUseCase) {
+ requestAlipayAction.observe(viewLifecycleOwner, EventObserver { (order, payEntity) ->
+ viewModel.memberUseCase.sendAlipayRequest(requireActivity(), order, payEntity)
+ })
+
+ requestWechatPayAction.observe(viewLifecycleOwner, EventObserver { (order, payEntity) ->
+ viewModel.memberUseCase.sendWechatPayRequest(order, payEntity)
+ })
+
+ showUserVerifyDialog.observe(viewLifecycleOwner, EventObserver {
+ when (it) {
+ is AccelerationUseCase.UserVerifyState.UnVerified -> { // 未实名
+ UserVerifyDialogUtils.showUnVerifiedDialog(
+ requireContext(),
+ R.string.archive_dialog_title.toResString(),
+ it.toast ?: R.string.recharge_without_real_name_description.toResString()
+ )
+ }
+
+ is AccelerationUseCase.UserVerifyState.Minors -> { // 已实名:未成年
+ UserVerifyDialogUtils.showMinorsOrVerifyingDialog(
+ requireContext(),
+ R.string.archive_dialog_title.toResString(),
+ it.toast ?: R.string.recharge_with_minors_description.toResString()
+ )
+ }
+
+ is AccelerationUseCase.UserVerifyState.Verifying -> { // 实名审核中
+ UserVerifyDialogUtils.showMinorsOrVerifyingDialog(
+ requireContext(),
+ R.string.archive_dialog_title.toResString(),
+ it.toast ?: R.string.recharge_with_verifying_description.toResString()
+ )
+ }
+ }
+ })
+ }
+
+ TheRouter.get(IAcceleratorProvider::class.java)?.bindAccRelatedListener("", acctListener)
}
@SuppressLint("SetJavaScriptEnabled")
@@ -362,6 +517,20 @@ class WebFragment : LazyFragment(), IScrollable {
// 用webview打开url
webview.webViewClient = object : WebViewClient() {
+
+ override fun shouldInterceptRequest(
+ view: WebView?,
+ request: WebResourceRequest
+ ): WebResourceResponse? {
+ val url = request.url.toString()
+ // 检查是否是字体请求
+ if (isFontRequest(url)) {
+ // 获取字体文件名
+ return loadFontFromAssets()
+ }
+ return super.shouldInterceptRequest(view, request)
+ }
+
override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
return if (isAdded) {
val originalUrl = requireArguments().getString(EntranceConsts.KEY_URL, "")
@@ -599,6 +768,42 @@ class WebFragment : LazyFragment(), IScrollable {
}
}
+ private fun onAccelerateCallback() {
+ val gameId = AcceleratorDataHolder.instance.acctGameRecord?.gameId ?: ""
+ mBinding?.webview?.callHandler("onAccelerateCallback", arrayOf(gameId)) { _: Any -> }
+ }
+
+ private fun isFontRequest(url: String): Boolean {
+ return url.lowercase().endsWith(Constants.DIN_FONT_PATH)
+ }
+
+ /**
+ * 从assets加载字体文件
+ */
+ private fun loadFontFromAssets(): WebResourceResponse? {
+ return try {
+ val inputStream = requireContext().assets.open(Constants.DIN_FONT_PATH)
+ val mineType = "font/ttf"
+ WebResourceResponse(
+ mineType,
+ "UTF-8",
+ inputStream
+ ).apply {
+ // 设置响应头
+ setStatusCodeAndReasonPhrase(200, "OK")
+ setResponseHeaders(
+ mapOf(
+ "Access-Control-Allow-Origin" to "*",
+ "Content-Type" to mineType
+ )
+ )
+ }
+
+ } catch (e: Exception) {
+ null
+ }
+ }
+
override fun onDestroy() {
super.onDestroy()
if (!TextUtils.isEmpty(mGameName)) {
@@ -609,6 +814,7 @@ class WebFragment : LazyFragment(), IScrollable {
removeJavascriptObject("share")
removeJavascriptObject("internal")
}
+ TheRouter.get(IAcceleratorProvider::class.java)?.unBindAccRelatedListener(acctListener)
}
private fun getNewsCommentNum() {
@@ -681,6 +887,16 @@ class WebFragment : LazyFragment(), IScrollable {
}
}
+ @Subscribe(threadMode = ThreadMode.MAIN)
+ fun onEventMainThread(data: EBPayState) {
+ val payStatus = when (data) {
+ EBPayState.PaySuccess -> "success"
+ EBPayState.PayFail -> "fail"
+ EBPayState.PayCancel -> "cancel"
+ }
+ mBinding?.webview?.callHandler("onPayCallback", arrayOf(payStatus)) { _: Any -> }
+ }
+
override fun onFragmentResume() {
super.onFragmentResume()
mBinding?.webview?.callHandler("videoPlay") { _: Any? -> }
diff --git a/app/src/main/java/com/halo/assistant/fragment/WebViewModel.kt b/app/src/main/java/com/halo/assistant/fragment/WebViewModel.kt
new file mode 100644
index 0000000000..c83cf6076a
--- /dev/null
+++ b/app/src/main/java/com/halo/assistant/fragment/WebViewModel.kt
@@ -0,0 +1,14 @@
+package com.halo.assistant.fragment
+
+import androidx.lifecycle.ViewModel
+import com.halo.assistant.accelerator.AccelerationUseCase
+
+class WebViewModel:ViewModel() {
+
+ val memberUseCase = AccelerationUseCase()
+
+ override fun onCleared() {
+ super.onCleared()
+ memberUseCase.onClear()
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-night/bg_accelerator_guide.xml b/app/src/main/res/drawable-night/bg_accelerator_guide.xml
new file mode 100644
index 0000000000..a0a828b0b4
--- /dev/null
+++ b/app/src/main/res/drawable-night/bg_accelerator_guide.xml
@@ -0,0 +1,144 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_accelerator_guide.xml b/app/src/main/res/drawable/bg_accelerator_guide.xml
new file mode 100644
index 0000000000..c147062275
--- /dev/null
+++ b/app/src/main/res/drawable/bg_accelerator_guide.xml
@@ -0,0 +1,125 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/bg_accelerator_guide_download.xml b/app/src/main/res/drawable/bg_accelerator_guide_download.xml
new file mode 100644
index 0000000000..154bc07aad
--- /dev/null
+++ b/app/src/main/res/drawable/bg_accelerator_guide_download.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/ic_accelerator_recently_played.xml b/app/src/main/res/drawable/ic_accelerator_recently_played.xml
new file mode 100644
index 0000000000..90d5c80367
--- /dev/null
+++ b/app/src/main/res/drawable/ic_accelerator_recently_played.xml
@@ -0,0 +1,42 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_basic_accelerator.xml b/app/src/main/res/drawable/ic_basic_accelerator.xml
new file mode 100644
index 0000000000..75494a77bd
--- /dev/null
+++ b/app/src/main/res/drawable/ic_basic_accelerator.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/app/src/main/res/drawable/ic_basic_x.xml b/app/src/main/res/drawable/ic_basic_x.xml
new file mode 100644
index 0000000000..81f7d48c13
--- /dev/null
+++ b/app/src/main/res/drawable/ic_basic_x.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_basic_x_18.xml b/app/src/main/res/drawable/ic_basic_x_18.xml
new file mode 100644
index 0000000000..9728a56940
--- /dev/null
+++ b/app/src/main/res/drawable/ic_basic_x_18.xml
@@ -0,0 +1,14 @@
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_free_vip_tag.xml b/app/src/main/res/drawable/ic_free_vip_tag.xml
new file mode 100644
index 0000000000..578b1fd9b7
--- /dev/null
+++ b/app/src/main/res/drawable/ic_free_vip_tag.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_free_vip_tag_detail.xml b/app/src/main/res/drawable/ic_free_vip_tag_detail.xml
new file mode 100644
index 0000000000..5180ba053c
--- /dev/null
+++ b/app/src/main/res/drawable/ic_free_vip_tag_detail.xml
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_new_function_text.xml b/app/src/main/res/drawable/ic_new_function_text.xml
new file mode 100644
index 0000000000..97084ff2fb
--- /dev/null
+++ b/app/src/main/res/drawable/ic_new_function_text.xml
@@ -0,0 +1,21 @@
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/drawable/ic_triangle_down.xml b/app/src/main/res/drawable/ic_triangle_down.xml
new file mode 100644
index 0000000000..a63546fbaa
--- /dev/null
+++ b/app/src/main/res/drawable/ic_triangle_down.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/app/src/main/res/layout/detail_download_item.xml b/app/src/main/res/layout/detail_download_item.xml
index 5a0b10de1d..3e1606c867 100644
--- a/app/src/main/res/layout/detail_download_item.xml
+++ b/app/src/main/res/layout/detail_download_item.xml
@@ -279,6 +279,7 @@
android:visibility="gone"
app:layout_constraintBottom_toBottomOf="parent"
app:layout_constraintEnd_toStartOf="@id/detail_progressbar"
+ app:layout_constraintHorizontal_weight="76"
app:layout_constraintStart_toEndOf="@+id/iv_switch"
app:layout_constraintTop_toTopOf="parent"
tools:visibility="visible">
@@ -313,7 +314,8 @@
app:download_button_show_progress="true"
app:download_button_text_size="@dimen/primary_text_size"
app:layout_constraintBottom_toBottomOf="parent"
- app:layout_constraintEnd_toEndOf="parent"
+ app:layout_constraintEnd_toStartOf="@id/cl_speed_container"
+ app:layout_constraintHorizontal_weight="76"
app:layout_constraintStart_toEndOf="@+id/localDownloadContainer"
app:layout_constraintTop_toTopOf="parent"
app:layout_goneMarginStart="24dp" />
@@ -387,6 +389,167 @@
app:layout_constraintStart_toStartOf="@id/detail_progressbar"
app:layout_constraintTop_toTopOf="@id/detail_progressbar"
tools:text="选择下载你的版本" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/layout/dialog_fragment_accelerator_zone.xml b/app/src/main/res/layout/dialog_fragment_accelerator_zone.xml
new file mode 100644
index 0000000000..bd837b84f3
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_accelerator_zone.xml
@@ -0,0 +1,34 @@
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_fragment_speed_failure.xml b/app/src/main/res/layout/dialog_fragment_speed_failure.xml
new file mode 100644
index 0000000000..a24b768259
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_speed_failure.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_fragment_speed_replace_game.xml b/app/src/main/res/layout/dialog_fragment_speed_replace_game.xml
new file mode 100644
index 0000000000..c88528246f
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_speed_replace_game.xml
@@ -0,0 +1,65 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_fragment_speed_without_login.xml b/app/src/main/res/layout/dialog_fragment_speed_without_login.xml
new file mode 100644
index 0000000000..e45e55fe61
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_speed_without_login.xml
@@ -0,0 +1,49 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/dialog_fragment_starting_accelerator.xml b/app/src/main/res/layout/dialog_fragment_starting_accelerator.xml
new file mode 100644
index 0000000000..6a1fe6c0c6
--- /dev/null
+++ b/app/src/main/res/layout/dialog_fragment_starting_accelerator.xml
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/fragment_halo_personal.xml b/app/src/main/res/layout/fragment_halo_personal.xml
index b0e1b8873a..460af079c8 100644
--- a/app/src/main/res/layout/fragment_halo_personal.xml
+++ b/app/src/main/res/layout/fragment_halo_personal.xml
@@ -132,11 +132,18 @@
tools:visibility="visible" />
-
+
diff --git a/app/src/main/res/layout/item_home_recent_vgame_custom.xml b/app/src/main/res/layout/item_home_recent_vgame_custom.xml
index e16748aaa7..2429247b88 100644
--- a/app/src/main/res/layout/item_home_recent_vgame_custom.xml
+++ b/app/src/main/res/layout/item_home_recent_vgame_custom.xml
@@ -88,12 +88,11 @@
diff --git a/app/src/main/res/layout/item_home_vgame_refactor.xml b/app/src/main/res/layout/item_home_vgame_refactor.xml
index 6b3cf93781..2ed01d520a 100644
--- a/app/src/main/res/layout/item_home_vgame_refactor.xml
+++ b/app/src/main/res/layout/item_home_vgame_refactor.xml
@@ -2,8 +2,10 @@
+ android:layout_width="48dp"
+ android:layout_height="48dp"
+ android:clipToPadding="false"
+ android:clipChildren="false">
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/recycler_accelerator_zone.xml b/app/src/main/res/layout/recycler_accelerator_zone.xml
new file mode 100644
index 0000000000..1c39eba3db
--- /dev/null
+++ b/app/src/main/res/layout/recycler_accelerator_zone.xml
@@ -0,0 +1,14 @@
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/search_game_first_item.xml b/app/src/main/res/layout/search_game_first_item.xml
index 2c5c661ac8..2b9f80f761 100644
--- a/app/src/main/res/layout/search_game_first_item.xml
+++ b/app/src/main/res/layout/search_game_first_item.xml
@@ -16,7 +16,7 @@
+ layout="@layout/search_game_item" />
+ layout="@layout/search_game_item" />
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 9ff818721d..24a2263751 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -650,5 +650,35 @@
我的遊戲
玩過
預約
+ 提示
+ 請您完成登入後,在使用加速啟動遊戲
+ 去登入
+ 開通會員使用加速功能!
+ 去開通
+ 加速失敗,請聯絡客服
+ 聯絡客服
+ 偵測到您目前正在加速遊戲,繼續啟動將停止原遊戲的加速,並加速啟動【%1$s】,是否繼續?
+ 偵測到您目前遊戲正在加速,停止加速可能會導致遊戲斷線,是否繼續?
+ 暫不啟動
+ 繼續啟動
+ 停止加速可能會導致遊戲斷線,是否繼續?
+ 停止加速
+ 取消
+ 選擇加速區服
+ 網路加速
+ 加速中...%1$s%%
+ 加速中
+ 進入遊戲
+ 偵測到您尚未安裝對應遊戲,請先安裝遊戲後再啟動加速器!
+ 已複製客服QQ號
+ 實名提示
+ 根據相關政策要求,該遊戲需通過認證後才能加速
+ 前往實名認證
+ 響應國家號召,本平台僅對成年人提供儲值服務,請先完成實名認證
+ 響應國家號召,保護未成年人健康參與遊戲,本平台暫不對未成年人提供儲值服務
+ 您實名認證處於【認證中】,暫不能進行充值,請等待完成認證
+ 選擇加速區服
+ 新用戶免費3小時
+ 最近在玩
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 2a23789e85..2ba003266f 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -650,4 +650,34 @@
我的游戏
玩过
预约
+ 提示
+ 请您完成登录后,在使用加速启动游戏
+ 去登录
+ 开通会员使用加速功能!
+ 去开通
+ 加速失败,请联系客服
+ 联系客服
+ 检测到您当前正在加速游戏,继续启动将停止原游戏的加速,并加速启动【%1$s】,是否继续?
+ 检测到您当前游戏正在加速,停止加速可能会导致游戏断线,是否继续?
+ 暂不启动
+ 继续启动
+ 停止加速可能会导致游戏断线,是否继续?
+ 停止加速
+ 取消
+ 选择加速区服
+ 网络加速
+ 加速中...%1$s%%
+ 加速中
+ 进入游戏
+ 检测到您尚未安装对应游戏,请先安装游戏后再启动加速器!
+ 已复制客服QQ号
+ 实名提示
+ 根据相关政策要求,该游戏需通过认证后才能进行加速
+ 前往实名认证
+ 响应国家号召,本平台仅对成年人提供充值服务,请先完成实名认证
+ 响应国家号召,保护未成年人健康参与游戏,本平台暂不对未成年人提供充值服务
+ 您实名认证处于【认证中】,暂不能进行充值,请等待完成认证
+ 选择加速区服
+ 新用户免费3小时
+ 最近在玩
diff --git a/app/src/main/res/xml/personal_scene.xml b/app/src/main/res/xml/personal_scene.xml
index 86328eec5a..5d588c1417 100644
--- a/app/src/main/res/xml/personal_scene.xml
+++ b/app/src/main/res/xml/personal_scene.xml
@@ -20,6 +20,10 @@
android:alpha="0"
motion:framePosition="20"
motion:motionTarget="@+id/iv_arrow" />
+
@@ -97,6 +101,14 @@
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent" />
+
@@ -188,5 +200,17 @@
android:alpha="0"
motion:layout_constraintBottom_toBottomOf="parent"
motion:layout_constraintEnd_toEndOf="parent" />
+
+
\ No newline at end of file
diff --git a/dependencies.gradle b/dependencies.gradle
index 94f5a85946..34ca3e03c1 100644
--- a/dependencies.gradle
+++ b/dependencies.gradle
@@ -7,8 +7,8 @@ ext {
targetSdkVersion = 30 // 升级targetSdkVersion到 34 时需要根据官方文档补全前台服务的权限类型。比如 NDownloadService,KeepAliveService
// application info (每个大版本之间的 versionCode 增加 20)
- versionCode = 1130
- versionName = "5.39.0"
+ versionCode = 1131
+ versionName = "5.39.1"
applicationId = "com.gh.gamecenter"
applicationIdGat = "com.gh.gamecenter.intl"
@@ -149,6 +149,8 @@ ext {
volcTlsVersion = "1.1.4"
xcrashVersion = "3.1.0"
+ aliPayVersion = "15.8.17"
+ acceleratorVersion = "1.0.1"
}
apply from: 'dependencies_vasdk.gradle'
diff --git a/feature/accelerator/build.gradle b/feature/accelerator/build.gradle
new file mode 100644
index 0000000000..58a01f5716
--- /dev/null
+++ b/feature/accelerator/build.gradle
@@ -0,0 +1,48 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+ id 'kotlin-kapt'
+ id 'kotlin-parcelize'
+ id 'com.google.devtools.ksp'
+}
+
+android {
+ namespace 'com.gh.gamecenter.accelerator'
+ compileSdk 34
+
+ defaultConfig {
+ minSdk 22
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+
+ repositories {
+ flatDir {
+ dirs 'libs'
+ }
+ }
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ consumerProguardFiles 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation fileTree(include: ['*.jar', '*.aar'], dir: 'libs')
+ implementation(project(path: ":module_common")) {
+ exclude group: 'androidx.swiperefreshlayout'
+ }
+ implementation(project(path: ":module_core_feature"))
+ ksp "cn.therouter:apt:${routerVersion}"
+ implementation "com.lg:accelerator:${acceleratorVersion}"
+}
\ No newline at end of file
diff --git a/feature/accelerator/proguard-rules.pro b/feature/accelerator/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/feature/accelerator/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/accelerator/src/main/AndroidManifest.xml b/feature/accelerator/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..e3a95d18ed
--- /dev/null
+++ b/feature/accelerator/src/main/AndroidManifest.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/feature/accelerator/src/main/java/com/gh/gamecenter/accelerator/provider/AcceleratorProviderImpl.kt b/feature/accelerator/src/main/java/com/gh/gamecenter/accelerator/provider/AcceleratorProviderImpl.kt
new file mode 100644
index 0000000000..8b0201c1bd
--- /dev/null
+++ b/feature/accelerator/src/main/java/com/gh/gamecenter/accelerator/provider/AcceleratorProviderImpl.kt
@@ -0,0 +1,173 @@
+package com.gh.gamecenter.accelerator.provider
+
+import android.app.Application
+import com.gh.gamecenter.core.callback.AccelerateState
+import com.gh.gamecenter.core.callback.OnAccelerateListener
+import com.gh.gamecenter.core.provider.IAcceleratorProvider
+import com.gh.gamecenter.feature.entity.AcctGameInfo
+import com.gh.gamecenter.feature.entity.VipEntity
+import com.gh.gamecenter.feature.utils.SentryHelper
+import com.lightgame.utils.Utils
+import com.qeeyou.qyvpn.QyAccelerator
+import com.qeeyou.qyvpn.bean.QyAcctGameInfo
+import com.qeeyou.qyvpn.utils.OnQyAccelerateListener
+import com.qeeyou.qyvpn.utils.QyAccConfig
+import java.io.File
+
+/**
+ * 单例模式,可以用户保存一些加速器全局状态信息
+ */
+@com.therouter.inject.ServiceProvider
+class AcceleratorProviderImpl : IAcceleratorProvider {
+ private var vipEntity: VipEntity? = null
+ private var _token = ""
+
+ // 根据包名回调
+ private val listeners = hashMapOf()
+
+ // 所有包名都回调(pkgName.isBlank())
+ private val allListener = mutableSetOf()
+ private var curAcctGameInfo: AcctGameInfo? = null
+
+ private val qyListener = object : OnQyAccelerateListener {
+ override fun onAccCurrentProgress(progress: Int, curGamePkgName: String?, curGameZoneFlag: String?) {
+ listeners[curGamePkgName]?.onProgress(progress, curGamePkgName, curGameZoneFlag)
+ allListener.forEach {
+ it.onProgress(progress, curGamePkgName, curGameZoneFlag)
+ }
+ }
+
+ override fun onAccCurrentStatus(
+ status: QyAccelerator.QyStatus,
+ curGamePkgName: String?,
+ curGameZoneFlag: String?,
+ eventCode: Int,
+ eventMsg: String?,
+ extraParam: Any?
+ ) {
+ Utils.log(LOG_TAG, "onAccCurrentStatus:$status -- code:$eventCode -- msg:$eventMsg")
+ val state = when (status) {
+ QyAccelerator.QyStatus.AccNormal -> AccelerateState.Normal(eventCode)
+ QyAccelerator.QyStatus.AccStarting -> AccelerateState.Starting(eventCode)
+ QyAccelerator.QyStatus.AccSuccess -> AccelerateState.Success(curAcctGameInfo, eventCode)
+ QyAccelerator.QyStatus.AccFailure -> {
+ SentryHelper.onEvent(SENTRY_EVENT_ID, KEY_ACC_FAILURE_ERROR, "$eventCode($eventMsg)")
+ AccelerateState.Failure(eventCode)
+ }
+
+ QyAccelerator.QyStatus.AccOkStopping -> AccelerateState.OkStopping(eventCode)
+ QyAccelerator.QyStatus.AccErrStopping -> AccelerateState.ErrStopping(eventCode)
+ }
+ listeners[curGamePkgName]?.onStateChanged(state)
+ allListener.forEach {
+ it.onStateChanged(state)
+ }
+ }
+
+ override fun onAccEventCallBack(
+ eventCode: Int,
+ eventMsg: String?,
+ curGamePkgName: String?,
+ curGameZoneFlag: String?
+ ) = Unit
+
+ override fun onAccExtraInfoEvent(
+ eventFlag: QyAccelerator.QyExtra,
+ extraInfo: Any?,
+ curGamePkgName: String?,
+ curGameZoneFlag: String?
+ ) = ""
+ }
+
+ override fun init(isDev: Boolean, application: Application, version: String) {
+ Utils.log(LOG_TAG, "init:$version")
+ val qyAccConfigBuilder = QyAccConfig.Builder().setAppId("QyAccSdk")
+ .setDebug(isDev)
+ .setAppVersion(version)
+ .setServerAddressEnv(if (isDev) QyAccConfig.ServerAddressEnv.DebugProImg else QyAccConfig.ServerAddressEnv.Release)
+ .build()
+ QyAccelerator.getInstance().init(application, qyAccConfigBuilder, QyAccelerator.QyAccStrategy.HaloRing)
+ QyAccelerator.getInstance().bindQyAccRelatedListener(qyListener)
+ QyAccelerator.getInstance().switchAccLogCanUpload(true)
+ }
+
+ override fun setQyUserToken(token: String, callback: ((Boolean) -> Unit)?) {
+ Utils.log(LOG_TAG, "setQyUserToken")
+ if (_token == token) {
+ // 避免外部多次设置相同的token
+ return
+ }
+ QyAccelerator.getInstance().setQyUserToken(token, setResultCallback = { isSuccess, errMsg ->
+ Utils.log(LOG_TAG, "setQyUserToken:$token --isSuccess:$isSuccess --errMsg:$errMsg")
+ if (isSuccess) {
+ _token = token
+ } else {
+ // 将setToken错误事件上报的sentry,便于后期分析原因
+ SentryHelper.onEventInAllChannel(SENTRY_EVENT_ID, KEY_SET_TOKEN_ERROR_MESSAGE, errMsg)
+ }
+ callback?.invoke(isSuccess)
+ })
+ }
+
+ override fun deleteQyUserToken(): Boolean {
+ Utils.log(LOG_TAG, "deleteQyUserToken")
+ if (isCurAccSuccess()) {
+ stopQyGameAccelerate("退出登录!")
+ }
+ // 退出登录的时候才会调用
+ val isDeleted = QyAccelerator.getInstance().delQyUserToken()
+ _token = ""
+ listeners.clear()
+ allListener.clear()
+ vipEntity = null
+ return isDeleted
+ }
+
+ override fun startQyGameAccelerate(gameId: String, pkgName: String, zoneInfo: Any) {
+ QyAccelerator.getInstance().clearAllAccLogFile()
+ Utils.log(LOG_TAG, "startQyGameAccelerate:$zoneInfo")
+ if (zoneInfo is AcctGameInfo.ZoneInfo) {
+ val qyAcctGameInfo = QyAcctGameInfo(
+ pkgName,
+ zoneInfo.id.toString()
+ )
+ curAcctGameInfo = AcctGameInfo(gameId, pkgName, zoneInfo)
+ QyAccelerator.getInstance().startQyGameAccelerate(qyAcctGameInfo)
+ }
+ }
+
+ override fun stopQyGameAccelerate(appLayerCallStopMsg: String?): Boolean {
+ Utils.log(LOG_TAG, "stopQyGameAccelerate:$appLayerCallStopMsg")
+ return QyAccelerator.getInstance().stopQyGameAccelerate(appLayerCallStopMsg)
+ }
+
+ override fun bindAccRelatedListener(pkgName: String, listener: OnAccelerateListener) {
+ if (pkgName.isBlank()) {
+ allListener.add(listener)
+ } else {
+ listeners[pkgName] = listener
+ }
+ }
+
+ override fun unBindAccRelatedListener(listener: OnAccelerateListener) {
+ listeners.values.remove(listener)
+ allListener.remove(listener)
+ }
+
+ override fun isCurAccSuccess(): Boolean {
+ return QyAccelerator.getInstance().isCurAccSuccess()
+ }
+
+ override fun getAccLogFileStorageList(): List? {
+ return QyAccelerator.getInstance().getAccLogFileStorageList()
+ }
+
+ companion object {
+ private const val LOG_TAG = "AcceleratorProviderImpl"
+ private const val SENTRY_EVENT_ID = "ACCELERATOR_SET_TOKEN_ERROR"
+ private const val KEY_SET_TOKEN_ERROR_MESSAGE = "set_token_error"
+ private const val KEY_ACC_FAILURE_ERROR = "key_acc_failure_error"
+
+ }
+
+}
\ No newline at end of file
diff --git a/feature/accelerator/src/main/res/values-zh-rTW/themes.xml b/feature/accelerator/src/main/res/values-zh-rTW/themes.xml
new file mode 100644
index 0000000000..708cb75e4a
--- /dev/null
+++ b/feature/accelerator/src/main/res/values-zh-rTW/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/accelerator/src/main/res/values/strings.xml b/feature/accelerator/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..972aca8425
--- /dev/null
+++ b/feature/accelerator/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ accelerator
+
\ No newline at end of file
diff --git a/feature/ali_pay/build.gradle b/feature/ali_pay/build.gradle
new file mode 100644
index 0000000000..b783d5f69b
--- /dev/null
+++ b/feature/ali_pay/build.gradle
@@ -0,0 +1,42 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+ id 'kotlin-kapt'
+ id 'kotlin-parcelize'
+ id 'com.google.devtools.ksp'
+}
+
+android {
+ namespace 'com.game.gamecenter.alipay'
+ compileSdk 34
+
+ defaultConfig {
+
+ minSdk 22
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation "com.alipay.sdk:alipaysdk-android:$aliPayVersion"
+ implementation(project(path: ":module_common")) {
+ exclude group: 'androidx.swiperefreshlayout'
+ }
+ implementation(project(path: ":module_core_feature"))
+ ksp "cn.therouter:apt:${routerVersion}"
+}
\ No newline at end of file
diff --git a/feature/ali_pay/proguard-rules.pro b/feature/ali_pay/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/feature/ali_pay/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/AndroidManifest.xml b/feature/ali_pay/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..0971662520
--- /dev/null
+++ b/feature/ali_pay/src/main/AndroidManifest.xml
@@ -0,0 +1,13 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/AliPayProviderImpl.kt b/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/AliPayProviderImpl.kt
new file mode 100644
index 0000000000..e95a2e063c
--- /dev/null
+++ b/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/AliPayProviderImpl.kt
@@ -0,0 +1,44 @@
+package com.game.gamecenter.alipay
+
+import android.app.Activity
+import com.alipay.sdk.app.PayTask
+import com.gh.gamecenter.core.provider.IAliPayProvider
+import com.gh.gamecenter.feature.eventbus.EBPayState
+import io.reactivex.Single
+
+@com.therouter.inject.ServiceProvider
+class AliPayProviderImpl : IAliPayProvider {
+ override fun sendRequest(activity: Activity, orderNo: String): Single {
+ return Single.create {
+ val pay = PayTask(activity)
+ val result = pay.payV2(orderNo, true)
+ val statusCode = result[KEY_RESULT_STATUS]
+ val resultCode = when (statusCode) {
+ PAY_RESULT_CODE_SUCCESS, PAY_RESULT_CODE_HANDLING, PAY_RESULT_CODE_UNKNOWN -> 0
+
+ PAY_RESULT_CODE_CANCEL -> 1
+
+ else -> 2
+
+ }
+ it.onSuccess(resultCode)
+ }
+ }
+
+ companion object {
+ // 订单支付成功。
+ private const val PAY_RESULT_CODE_SUCCESS = "9000"
+
+ // 正在处理中,支付结果未知(有可能已经支付成功),请查询商家订单列表中订单的支付状态
+ private const val PAY_RESULT_CODE_HANDLING = "8000"
+
+ // 支付结果未知(有可能已经支付成功),请查询商家订单列表中订单的支付状态
+ private const val PAY_RESULT_CODE_UNKNOWN = "6004"
+
+ private const val PAY_RESULT_CODE_CANCEL = "6001"
+
+ private const val KEY_RESULT_STATUS = "resultStatus"
+
+ private const val DELAY_DURATION = 1000L
+ }
+}
diff --git a/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/H5PayActivity.kt b/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/H5PayActivity.kt
new file mode 100644
index 0000000000..1d17bff91e
--- /dev/null
+++ b/feature/ali_pay/src/main/java/com/game/gamecenter/alipay/H5PayActivity.kt
@@ -0,0 +1,78 @@
+package com.game.gamecenter.alipay
+
+import android.app.Activity
+import android.os.Bundle
+import android.text.TextUtils
+import android.view.View
+import android.webkit.CookieManager
+import android.webkit.WebView
+import android.webkit.WebViewClient
+import android.widget.LinearLayout
+import com.alipay.sdk.app.PayTask
+
+class H5PayActivity : Activity() {
+
+ private var _webView: WebView? = null
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+
+ val layout = LinearLayout(applicationContext)
+ val params =
+ LinearLayout.LayoutParams(LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
+ layout.orientation = LinearLayout.VERTICAL
+ setContentView(layout)
+
+ _webView = WebView(applicationContext)
+ params.weight = 1F
+ _webView?.let {
+ it.visibility = View.VISIBLE
+ layout.addView(it, params)
+
+ initWebView(it)
+ }
+ }
+
+ private fun initWebView(webView: WebView) {
+ with(webView.settings) {
+ javaScriptEnabled = true
+ javaScriptCanOpenWindowsAutomatically = true
+ CookieManager.getInstance().setAcceptThirdPartyCookies(webView, true)
+ domStorageEnabled = true
+ }
+ with(webView) {
+ webViewClient = MyWebViewClient()
+ loadUrl("url")
+ }
+ }
+
+ private inner class MyWebViewClient : WebViewClient() {
+ override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean {
+ if (!(url.startsWith("http") || url.startsWith("https"))) {
+ return true
+ }
+
+ /**
+ * 推荐采用的新的二合一接口(payInterceptorWithUrl),只需调用一次
+ */
+ val task = PayTask(this@H5PayActivity)
+ val isIntercepted = task.payInterceptorWithUrl(url, true) { result ->
+ val url = result.getReturnUrl()
+ if (!TextUtils.isEmpty(url)) {
+ this@H5PayActivity.runOnUiThread {
+ view.loadUrl(url)
+ }
+ }
+ }
+
+ /**
+ * 判断是否成功拦截
+ * 若成功拦截,则无需继续加载该URL;否则继续加载
+ */
+ if (!isIntercepted) {
+ view.loadUrl(url)
+ }
+ return true
+ }
+ }
+}
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/res/values-night/themes.xml b/feature/ali_pay/src/main/res/values-night/themes.xml
new file mode 100644
index 0000000000..708cb75e4a
--- /dev/null
+++ b/feature/ali_pay/src/main/res/values-night/themes.xml
@@ -0,0 +1,7 @@
+
+
+
+
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/res/values/colors.xml b/feature/ali_pay/src/main/res/values/colors.xml
new file mode 100644
index 0000000000..c8524cd961
--- /dev/null
+++ b/feature/ali_pay/src/main/res/values/colors.xml
@@ -0,0 +1,5 @@
+
+
+ #FF000000
+ #FFFFFFFF
+
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/res/values/strings.xml b/feature/ali_pay/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..2065f32d80
--- /dev/null
+++ b/feature/ali_pay/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ ali_pay
+
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/res/values/themes.xml b/feature/ali_pay/src/main/res/values/themes.xml
new file mode 100644
index 0000000000..e291fb4fb9
--- /dev/null
+++ b/feature/ali_pay/src/main/res/values/themes.xml
@@ -0,0 +1,9 @@
+
+
+
+
+
+
\ No newline at end of file
diff --git a/feature/ali_pay/src/main/res/xml/network_security_config.xml b/feature/ali_pay/src/main/res/xml/network_security_config.xml
new file mode 100644
index 0000000000..2439f15c2c
--- /dev/null
+++ b/feature/ali_pay/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/feature/sentry/src/main/java/com/lg/SentryProviderImpl.kt b/feature/sentry/src/main/java/com/lg/SentryProviderImpl.kt
index 493aa4a487..7e323cdc8a 100644
--- a/feature/sentry/src/main/java/com/lg/SentryProviderImpl.kt
+++ b/feature/sentry/src/main/java/com/lg/SentryProviderImpl.kt
@@ -2,26 +2,19 @@ package com.lg
import android.content.Context
import android.text.TextUtils
-import com.therouter.router.Route
-import com.therouter.TheRouter
import com.gh.gamecenter.common.base.activity.BaseActivity
-import com.gh.gamecenter.common.constant.RouteConsts
+import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.core.HaloApp
import com.gh.gamecenter.core.provider.IAppProvider
import com.gh.gamecenter.core.provider.ISentryProvider
-import com.lightgame.utils.Utils
-import io.sentry.Sentry
-import io.sentry.SentryEvent
-import io.sentry.SentryLevel
-import io.sentry.protocol.Message
import com.gh.sentry.BuildConfig
import com.lightgame.utils.AppManager
-import io.sentry.Hint
-import io.sentry.Scope
-import io.sentry.ScopeCallback
-import io.sentry.SentryOptions
+import com.lightgame.utils.Utils
+import com.therouter.TheRouter
+import io.sentry.*
import io.sentry.android.core.SentryAndroid
import io.sentry.android.fragment.FragmentLifecycleIntegration
+import io.sentry.protocol.Message
@com.therouter.inject.ServiceProvider
class SentryProviderImpl : ISentryProvider {
@@ -63,7 +56,9 @@ class SentryProviderImpl : ISentryProvider {
}
options.setBeforeBreadcrumb { breadcrumb, _ ->
- if ("ui.lifecycle" == breadcrumb.getCategory() && "started" == breadcrumb.getData("state") && AppManager.getInstance().currentActivity() is BaseActivity) {
+ if ("ui.lifecycle" == breadcrumb.getCategory() && "started" == breadcrumb.getData("state") && AppManager.getInstance()
+ .currentActivity() is BaseActivity
+ ) {
val businessId = (AppManager.getInstance().currentActivity() as? BaseActivity)?.getBusinessId()
if (businessId != null) {
breadcrumb.setData("businessId1", businessId.component1())
@@ -114,6 +109,30 @@ class SentryProviderImpl : ISentryProvider {
}
}
+ override fun onEventInAllChannel(eventId: String, vararg kv: String?) {
+ val sentryEvent = SentryEvent()
+ val message = Message()
+ message.message = eventId
+ sentryEvent.message = message
+ sentryEvent.level = SentryLevel.INFO
+
+ for (i in kv.indices) {
+ if (i % 2 != 0) {
+ val key = kv[i - 1]
+ val value = kv[i]
+ if (!TextUtils.isEmpty(key) && !TextUtils.isEmpty(value)) {
+ sentryEvent.setTag(key!!, value!!)
+ if (BuildConfig.DEBUG && isContainChinese(key)) {
+ throw RuntimeException("tag-key 不支持中文")
+ }
+ }
+ }
+ }
+
+ Utils.log("Sentry", "$eventId + [${kv.joinToString(" , ")}]")
+ Sentry.captureEvent(sentryEvent)
+ }
+
override fun captureException(e: Throwable) {
Sentry.captureException(e)
}
diff --git a/feature/wechat_pay/build.gradle b/feature/wechat_pay/build.gradle
new file mode 100644
index 0000000000..c4521bbd39
--- /dev/null
+++ b/feature/wechat_pay/build.gradle
@@ -0,0 +1,40 @@
+plugins {
+ id 'com.android.library'
+ id 'org.jetbrains.kotlin.android'
+ id 'kotlin-kapt'
+ id 'kotlin-parcelize'
+ id 'com.google.devtools.ksp'
+}
+
+android {
+ namespace 'com.gh.gamecenter.wechat.pay'
+ compileSdk 34
+
+ defaultConfig {
+ minSdk 22
+
+ testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
+ }
+
+ buildTypes {
+ release {
+ minifyEnabled false
+ proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
+ }
+ }
+ compileOptions {
+ sourceCompatibility JavaVersion.VERSION_1_8
+ targetCompatibility JavaVersion.VERSION_1_8
+ }
+ kotlinOptions {
+ jvmTarget = '1.8'
+ }
+}
+
+dependencies {
+ implementation(project(path: ":module_common")) {
+ exclude group: 'androidx.swiperefreshlayout'
+ }
+ implementation(project(path: ":module_core_feature"))
+ ksp "cn.therouter:apt:${routerVersion}"
+}
\ No newline at end of file
diff --git a/feature/wechat_pay/proguard-rules.pro b/feature/wechat_pay/proguard-rules.pro
new file mode 100644
index 0000000000..481bb43481
--- /dev/null
+++ b/feature/wechat_pay/proguard-rules.pro
@@ -0,0 +1,21 @@
+# Add project specific ProGuard rules here.
+# You can control the set of applied configuration files using the
+# proguardFiles setting in build.gradle.
+#
+# For more details, see
+# http://developer.android.com/guide/developing/tools/proguard.html
+
+# If your project uses WebView with JS, uncomment the following
+# and specify the fully qualified class name to the JavaScript interface
+# class:
+#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
+# public *;
+#}
+
+# Uncomment this to preserve the line number information for
+# debugging stack traces.
+#-keepattributes SourceFile,LineNumberTable
+
+# If you keep the line number information, uncomment this to
+# hide the original source file name.
+#-renamesourcefileattribute SourceFile
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/AndroidManifest.xml b/feature/wechat_pay/src/main/AndroidManifest.xml
new file mode 100644
index 0000000000..7fcf2fcb9f
--- /dev/null
+++ b/feature/wechat_pay/src/main/AndroidManifest.xml
@@ -0,0 +1,17 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/java/com/gh/gamecenter/WechatPayProviderImpl.kt b/feature/wechat_pay/src/main/java/com/gh/gamecenter/WechatPayProviderImpl.kt
new file mode 100644
index 0000000000..7f787e5da8
--- /dev/null
+++ b/feature/wechat_pay/src/main/java/com/gh/gamecenter/WechatPayProviderImpl.kt
@@ -0,0 +1,56 @@
+package com.gh.gamecenter
+
+import com.gh.gamecenter.core.HaloApp
+import com.gh.gamecenter.core.provider.IConfigProvider
+import com.gh.gamecenter.core.provider.IWechatPayProvider
+import com.gh.gamecenter.feature.entity.OrderEntity
+import com.gh.gamecenter.feature.entity.WechatPayEntity
+import com.tencent.mm.opensdk.modelpay.PayReq
+import com.tencent.mm.opensdk.openapi.IWXAPI
+import com.tencent.mm.opensdk.openapi.WXAPIFactory
+import com.therouter.TheRouter
+
+/**
+ * 这里必须使用单例模式,需要保存 orderNo,以便后续查询订单使用
+ */
+@com.therouter.inject.ServiceProvider
+class WechatPayProviderImpl : IWechatPayProvider {
+ private var api: IWXAPI
+
+ private var _orderNo: String = ""
+ override val orderNo: String
+ get() = _orderNo
+
+ private var _order: OrderEntity? = null
+ override val order: Any?
+ get() = _order
+
+ init {
+ val config = TheRouter.get(IConfigProvider::class.java)
+ api = WXAPIFactory.createWXAPI(HaloApp.getInstance().applicationContext, config?.getWechatAppId(), false)
+ }
+
+ override fun payRequest(order: Any, payRequest: Any) {
+ if (order is OrderEntity) {
+ _order = order
+ }
+ if (payRequest is WechatPayEntity) {
+ _orderNo = payRequest.orderNo
+ val request = PayReq().apply {
+ appId = payRequest.appId
+ partnerId = payRequest.partnerId
+ prepayId = payRequest.prepayId
+ packageValue = payRequest.packageValue
+ nonceStr = payRequest.nonceStr
+ timeStamp = payRequest.timestamp
+ sign = payRequest.sign
+ }
+ api.sendReq(request)
+ }
+ }
+
+ override fun clear() {
+ _orderNo = ""
+ _order = null
+ }
+}
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/java/com/gh/gamecenter/wxapi/WXPayEntryActivity.kt b/feature/wechat_pay/src/main/java/com/gh/gamecenter/wxapi/WXPayEntryActivity.kt
new file mode 100644
index 0000000000..42e0349c39
--- /dev/null
+++ b/feature/wechat_pay/src/main/java/com/gh/gamecenter/wxapi/WXPayEntryActivity.kt
@@ -0,0 +1,88 @@
+package com.gh.gamecenter.wxapi
+
+import android.app.Activity
+import android.content.Intent
+import android.os.Bundle
+import com.gh.gamecenter.WechatPayProviderImpl
+import com.gh.gamecenter.common.utils.SensorsBridge
+import com.gh.gamecenter.core.provider.IConfigProvider
+import com.gh.gamecenter.core.provider.IWechatPayProvider
+import com.gh.gamecenter.core.provider.IWechatPayResultProvider
+import com.gh.gamecenter.feature.entity.OrderEntity
+import com.gh.gamecenter.feature.eventbus.EBPayState
+import com.tencent.mm.opensdk.constants.ConstantsAPI
+import com.tencent.mm.opensdk.modelbase.BaseReq
+import com.tencent.mm.opensdk.modelbase.BaseResp
+import com.tencent.mm.opensdk.openapi.IWXAPI
+import com.tencent.mm.opensdk.openapi.IWXAPIEventHandler
+import com.tencent.mm.opensdk.openapi.WXAPIFactory
+import com.therouter.TheRouter
+
+class WXPayEntryActivity : Activity(), IWXAPIEventHandler {
+
+ private lateinit var api: IWXAPI
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ val config = TheRouter.get(IConfigProvider::class.java)
+ api = WXAPIFactory.createWXAPI(this, config!!.getWechatAppId(), false)
+ api.handleIntent(intent, this)
+ }
+
+ override fun onNewIntent(intent: Intent?) {
+ super.onNewIntent(intent)
+ setIntent(intent)
+ api.handleIntent(intent, this)
+ }
+
+ override fun onReq(reqeust: BaseReq?) {
+ }
+
+ override fun onResp(response: BaseResp) {
+ if (response.type == ConstantsAPI.COMMAND_PAY_BY_WX) {
+ val payProvider = TheRouter.get(IWechatPayProvider::class.java)
+ val order = payProvider?.order as? OrderEntity
+ when (response.errCode) {
+ PAY_RESULT_SUCCESSFULLY -> {
+
+ if (payProvider is WechatPayProviderImpl) {
+ val nonceStr = payProvider.orderNo
+ TheRouter.get(IWechatPayResultProvider::class.java)?.onPayComplete(nonceStr, order)
+ }
+ }
+
+ PAY_RESULT_FAILURE -> {
+ SensorsBridge.trackMemberRechargeResult(
+ PAYMENT_TYPE_WECHAT,
+ order?.setMenuName ?: "",
+ order?.paymentAmount ?: "",
+ RECHARGE_RESULT_FAILURE
+ )
+
+ org.greenrobot.eventbus.EventBus.getDefault().post(EBPayState.PayFail)
+ }
+
+ PAY_RESULT_CANCEL -> {
+ SensorsBridge.trackMemberRechargeResult(
+ PAYMENT_TYPE_WECHAT,
+ order?.setMenuName ?: "",
+ order?.paymentAmount ?: "",
+ RECHARGE_RESULT_CANCEL
+ )
+ org.greenrobot.eventbus.EventBus.getDefault().post(EBPayState.PayCancel)
+ }
+ }
+ payProvider?.clear()
+ finish()
+ }
+ }
+
+ companion object {
+ private const val PAY_RESULT_SUCCESSFULLY = 0
+ private const val PAY_RESULT_FAILURE = -1
+ private const val PAY_RESULT_CANCEL = -2
+ private const val PAYMENT_TYPE_WECHAT = "微信"
+ private const val RECHARGE_RESULT_CANCEL = "充值取消"
+ private const val RECHARGE_RESULT_FAILURE = "充值失败"
+ }
+}
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/res/values-zh-rTW/strings.xml b/feature/wechat_pay/src/main/res/values-zh-rTW/strings.xml
new file mode 100644
index 0000000000..1fde989696
--- /dev/null
+++ b/feature/wechat_pay/src/main/res/values-zh-rTW/strings.xml
@@ -0,0 +1,3 @@
+
+ 光環助手
+
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/res/values/strings.xml b/feature/wechat_pay/src/main/res/values/strings.xml
new file mode 100644
index 0000000000..f78fdebdc3
--- /dev/null
+++ b/feature/wechat_pay/src/main/res/values/strings.xml
@@ -0,0 +1,3 @@
+
+ 光环助手
+
\ No newline at end of file
diff --git a/feature/wechat_pay/src/main/res/xml/network_security_config.xml b/feature/wechat_pay/src/main/res/xml/network_security_config.xml
new file mode 100644
index 0000000000..2439f15c2c
--- /dev/null
+++ b/feature/wechat_pay/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,4 @@
+
+
+
+
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java
index 9bd870d471..bc65662841 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java
+++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/Constants.java
@@ -299,6 +299,10 @@ public class Constants {
// 腾讯企点地址
public static final String TENCENT_QIDIAN_ADDRESS = "https://admin.qidian.qq.com/template/blue/mp/menu/qr-code-jump.html?linkType=0&env=ol&kfuin=2355094296&fid=457&key=c76dcb2e3d582b6ffbfb5bb22cde85ff&cate=1&source=&isLBS=&isCustomEntry=&type=16&ftype=1&_type=wpa&qidian=true";
+ // 企点qq连接
+ public static final String QQ_QIDIAN_ADDRESS = "https://wpa1.qq.com/XqJqVq2d?_type=wpa&qidian=true";
+
+
//版规声明
public static final String FORUM_REGULATIONS_NEWS_ID = "5f4db9cc34d44d01b92fd670";
@@ -345,6 +349,10 @@ public class Constants {
//游戏单管理规范
public static final String GAME_COLLECTION_RULE = "https://and-static.ghzs66.com/page/privacy_policies/game_collection.html";
+ // 我的资产页面
+ public static final String MY_ASSETS_DEV = "https://dev-and-static.ghzs.com/web/qiyou-acc/index.html";
+ public static final String MY_ASSETS = "https://and-static.ghzs.com/web/qiyou-acc/index.html";
+
public static final String SP_IS_DEV_ENV = "is_dev_env";
public static final String[] REPORT_LIST = new String[]{"垃圾广告营销", "恶意攻击谩骂", "淫秽色情信息", "违法有害信息", "其他原因"};
@@ -485,4 +493,9 @@ public class Constants {
public static final String SP_SHOW_GAME_DETAIL_FUNCTION_TAG_GUIDE = "show_game_detail_function_tag_guide";
public static final String LIBAO_CHANGED_TAG = "libao_changed";
+ public static final String SP_HAS_SHOW_ACCELERATION_GUIDE_LAYER = "sp_has_show_acceleration_guide_layer";
+
+ public static final String SP_MEMBER_RECHARGE_BUTTON_CLICK = "sp_member_recharge_button_click";
+ public static final String SP_MEMBER_PAYMENT_BUTTON_CLICK = "sp_member_payment_button_click";
+
}
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java
index 4f1e2ffc7d..04a6021274 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java
+++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/EntranceConsts.java
@@ -365,4 +365,10 @@ public class EntranceConsts {
public static final String KEY_DISPLAY_TYPE = "display_type";
public static final String KEY_SHOW_FLOATING_WINDOW = "key_show_floating_window";
public static final String KEY_HIDE_TOOLBAR = "hide_toolbar";
+
+ public static final String KEY_ACCT_ZONE_INFO = "acct_zone_info";
+ public static final String KEY_IS_NEED_RECORD = "is_need_record";
+ public static final String KEY_HAS_MULTI_ZONE = "has_multi_zone";
+ public static final String KEY_SPEED_TYPE = "speed_type";
+ public static final String KEY_ACCELERATOR_REQUEST = "accelerator_request";
}
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt
index e2280034c0..7a263d2c93 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt
+++ b/module_common/src/main/java/com/gh/gamecenter/common/constant/RouteConsts.kt
@@ -27,6 +27,7 @@ object RouteConsts {
const val quickLoginActivity = "/activity/quick_login_activity"
const val securityActivity = "/activity/account_security"
const val serversCalendarManagementActivity = "/activity/server_manager"
+ const val myAssetsActivity = "/activity/my_assets"
const val aboutActivity = "/settings/AboutActivity"
const val webActivity = "/setting/WebActivity"
diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt
index 1893fc8b9b..90d296473e 100644
--- a/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt
+++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/SensorsBridge.kt
@@ -76,7 +76,7 @@ object SensorsBridge {
private const val KEY_GAME_FORUM_TYPE = "game_forum_type"
private const val KEY_STAY_LENGTH = "stay_length"
private const val KEY_SHARE_TYPE = "share_type"
- private const val KEY_IS_FIRST_TIME = "\$is_first_time"
+ const val KEY_IS_FIRST_TIME = "\$is_first_time"
private const val KEY_TAB_TYPE = "tab_type"
private const val KEY_BANNER_NAME = "banner_name"
private const val KEY_TOPPING_TYPE = "topping_type"
@@ -124,6 +124,14 @@ object SensorsBridge {
private const val KEY_WECHAT_REMIND = "wechat_remind"
private const val KEY_MESSAGE_REMIND = "message_remind"
private const val KEY_AUTOMATIC_DOWNLOAD = "automatic_download"
+ private const val KEY_PACKAGE_NAME = "package_name"
+ private const val KEY_MEMBER_TYPE = "member_type"
+ private const val KEY_DISTRICT_SERVER = "district_server"
+ private const val KEY_SCENE_TYPE = "scene_type"
+ private const val KEY_SET_MENU_NAME = "set_menu_name"
+ private const val KEY_PAYMENT_AMOUNT = "payment_amount"
+ private const val KEY_PAYMENT_TYPE = "payment_type"
+ const val EVENT_NAME = "event_name"
private const val EVENT_GAME_DETAIL_PAGE_TAB_SELECT = "GameDetailPageTabSelect"
private const val EVENT_GAME_DETAIL_PAGE_TAG_CLICK = "GameDetailPageGameTagClick"
@@ -308,6 +316,24 @@ object SensorsBridge {
private const val EVENT_PK_CLICK = "PkClick"
private const val EVENT_GAME_DETAIL_MODULE_CLICK = "GameDetailModuleClick"
+ private const val EVENT_NETWORK_ACCELERATION_GUIDANCE_DIAGRAM_SHOW = "NetworkAccelerationGuidanceDiagramShow"
+ private const val EVENT_NETWORK_ACCELERATION_BUTTON_CLICK = "NetworkAccelerationButtonClick"
+ private const val EVENT_NETWORK_ACCELERATION_START_UP = "NetworkAccelerationStartup"
+ private const val EVENT_NETWORK_ACCELERATION_START_UP_RESULT = "NetworkAccelerationStartupResult"
+ private const val EVENT_NETWORK_ACCELERATION_OTHER_BUTTON_CLICK = "NetworkAccelerationOtherButtonClick"
+ private const val EVENT_MEMBERSHIP_ACTIVATION_DIALOG_SHOW = "MembershipActivationDialogShow"
+ private const val EVENT_MEMBERSHIP_ACTIVATION_DIALOG_CLICK = "MembershipActivationDialogClick"
+ private const val EVENT_NETWORK_ACCELERATION_FAILURE_DIALOG_SHOW = "NetworkAccelerationFailureDialogShow"
+ private const val EVENT_NETWORK_ACCELERATION_FAILURE_DIALOG_CLICK = "NetworkAccelerationFailureDialogClick"
+ private const val EVENT_NETWORK_ACCELERATION_CONFLICT_DIALOG_SHOW = "NetworkAccelerationConflictDialogShow"
+ private const val EVENT_NETWORK_ACCELERATION_CONFLICT_DIALOG_CLICK = "NetworkAccelerationConflictDialogClick"
+ private const val EVENT_MY_ASSETS_PAGE_SHOW = "MyAssetsPageShow"
+ const val EVENT_MEMBER_RECHARGE_BUTTON_CLICK = "MemberRechargeButtonClick"
+ private const val EVENT_MEMBER_PAYMENT_BUTTON_CLICK = "MemberPaymentButtonClick"
+ private const val EVENT_MEMBER_RECHARGE_RESULT = "MemberRechargeResult"
+ private const val EVENT_MY_ASSETS_PAGE_CONTACT_CUSTOMER_SERVICE_CLICK = "MyAssetsPageContactCustomerServiceClick"
+ private const val EVENT_STOP_ACCELERATING_DIALOG_SHOW = "StopAcceleratingDialogShow"
+
private var mIsSensorsEnabled = false
private val mSensor by lazy {
@@ -4781,6 +4807,7 @@ object SensorsBridge {
}
trackEvent(EVENT_APPOINTMENT_GAME_ONLINE_DIALOG_CLICK, json)
}
+
/**
* 事件ID:SearchDiscoveryClick
* 事件名称:搜索发现点击事件
@@ -4899,4 +4926,359 @@ object SensorsBridge {
}
trackEvent(EVENT_GAME_DETAIL_MODULE_CLICK, json)
}
+
+ /**
+ * 事件ID:NetworkAccelerationGuidanceDiagramShow
+ * 事件名称:网络加速引导图展示事件
+ * 触发时机:网络加速引导图展示触发
+ */
+ fun trackNetworkAccelerationGuidanceDiagramShow(
+ packageName: String,
+ gameId: String,
+ gameName: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_GUIDANCE_DIAGRAM_SHOW, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationButtonClick
+ * 事件名称:网络加速按钮点击事件
+ * 触发时机:用户点击网络加速按钮时触发上报以及点击时的具体场景以及用户的相关属性
+ */
+ fun trackNetworkAccelerationButtonClick(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ memberType: String,
+ districtServer: String,
+ sceneType: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_MEMBER_TYPE to memberType
+ KEY_DISTRICT_SERVER to districtServer
+ KEY_SCENE_TYPE to sceneType
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_BUTTON_CLICK, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationStartup
+ * 事件名称:网络加速启动事件
+ * 触发时机:调用奇游SDK进行加速时进行上报
+ */
+ fun trackNetworkAccelerationStartup(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ memberType: String,
+ districtServer: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_MEMBER_TYPE to memberType
+ KEY_DISTRICT_SERVER to districtServer
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_START_UP, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationStartupResult
+ * 事件名称:网络加速启动结果事件
+ * 触发时机:调用奇游SDK进行加速时成功\失败时触发上报,失败是需上报具体的code码
+ */
+ fun trackNetworkAccelerationStartupResult(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ memberType: String,
+ districtServer: String,
+ result: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_MEMBER_TYPE to memberType
+ KEY_DISTRICT_SERVER to districtServer
+ KEY_RESULT to result
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_START_UP_RESULT, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationOtherButtonClick
+ * 事件名称:网络加速其他按钮点击事件
+ * 触发时机:点击【进入游戏\停止加速】时,触发上报
+ */
+ fun trackNetworkAccelerationOtherButtonClick(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ memberType: String,
+ buttonName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_MEMBER_TYPE to memberType
+ KEY_BUTTON_NAME to buttonName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_OTHER_BUTTON_CLICK, json)
+ }
+
+ /**
+ * 事件ID:MembershipActivationDialogShow
+ * 事件名称:会员开通提示弹窗展示事件
+ * 触发时机:会员提示弹窗展示时触发上报
+ */
+ fun trackMembershipActivationDialogShow(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_MEMBERSHIP_ACTIVATION_DIALOG_SHOW, json)
+ }
+
+ /**
+ * 事件ID:MembershipActivationDialogClick
+ * 事件名称:会员开通提示弹窗点击事件
+ * 触发时机:用户点击会员提示弹窗的按钮【去开通】时触发上报
+ */
+ fun trackMembershipActivationDialogClick(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_MEMBERSHIP_ACTIVATION_DIALOG_CLICK, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationFailureDialogShow
+ * 事件名称:加速失败提示弹窗展示事件
+ * 触发时机:加速失败提示弹窗展示时触发上报
+ */
+ fun trackNetworkAccelerationFailureDialogShow(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_FAILURE_DIALOG_SHOW, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationFailureDialogClick
+ * 事件名称:加速失败提示弹窗点击事件
+ * 触发时机:用户点击加速失败提示弹窗的按钮【联系客服】时触发上报
+ */
+ fun trackNetworkAccelerationFailureDialogClick(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_FAILURE_DIALOG_CLICK, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationConflictDialogShow
+ * 事件名称:加速冲突提示弹窗展示事件
+ * 触发时机:加速冲突提示弹窗展示时触发上报
+ */
+ fun trackNetworkAccelerationConflictDialogShow(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_CONFLICT_DIALOG_SHOW, json)
+ }
+
+ /**
+ * 事件ID:NetworkAccelerationConflictDialogClick
+ * 事件名称:加速冲突提示弹窗点击事件
+ * 触发时机:用户点击加速冲突提示弹窗的按钮【暂不启动】\【继续启动】时触发上报
+ */
+ fun trackNetworkAccelerationConflictDialogClick(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ buttonName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_BUTTON_NAME to buttonName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_NETWORK_ACCELERATION_CONFLICT_DIALOG_CLICK, json)
+ }
+
+ /**
+ * 事件ID:MyAssetsPageShow
+ * 事件名称:我的资产页面展示事件
+ * 触发时机:进入我的资产页面时触发上报
+ */
+ fun trackMyAssetsPageShow(
+ packageName: String,
+ gameId: String,
+ gameName: String,
+ sourceEntrance: String
+ ) {
+ val json = json {
+ KEY_PACKAGE_NAME to packageName
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_SOURCE_ENTRANCE to sourceEntrance
+ }
+ trackEvent(EVENT_MY_ASSETS_PAGE_SHOW, json)
+ }
+
+ /**
+ * 事件ID:MemberRechargeButtonClick
+ * 事件名称:会员充值按钮点击事件
+ * 触发时机:用户点击立即支付按钮时触发上报
+ * note:这个事件目前有 web 调用调用原生方法上报
+ */
+ fun trackMemberRechargeButtonClick(
+ isFirstTime: Boolean,
+ setMenuName: String,
+ paymentAmount: String
+ ) {
+ val json = json {
+ KEY_IS_FIRST_TIME to isFirstTime
+ KEY_SET_MENU_NAME to setMenuName
+ KEY_PAYMENT_AMOUNT to paymentAmount
+ }
+ trackEvent(EVENT_MEMBER_RECHARGE_BUTTON_CLICK, json)
+ }
+
+ /**
+ * 事件ID:MemberPaymentButtonClick
+ * 事件名称:会员充值按钮点击事件
+ * 触发时机:用户点击立即支付按钮时触发上报
+ */
+ fun trackMemberPaymentButtonClick(
+ isFirstTime: Boolean,
+ paymentType: String,
+ setMenuName: String,
+ paymentAmount: String
+ ) {
+ val json = json {
+ KEY_IS_FIRST_TIME to isFirstTime
+ KEY_SET_MENU_NAME to setMenuName
+ KEY_PAYMENT_AMOUNT to paymentAmount
+ KEY_PAYMENT_TYPE to paymentType
+ }
+ trackEvent(EVENT_MEMBER_PAYMENT_BUTTON_CLICK, json)
+ }
+
+ /**
+ * 事件ID:MemberRechargeResult
+ * 事件名称:会员充值结果返回事件
+ * 触发时机:支付的回调结果返回时触发上报
+ */
+ fun trackMemberRechargeResult(
+ paymentType: String,
+ setMenuName: String,
+ paymentAmount: String,
+ result: String
+ ) {
+ val json = json {
+ KEY_PAYMENT_TYPE to paymentType
+ KEY_SET_MENU_NAME to setMenuName
+ KEY_PAYMENT_AMOUNT to paymentAmount
+ KEY_RESULT to result
+ }
+ trackEvent(EVENT_MEMBER_RECHARGE_RESULT, json)
+ }
+
+ /**
+ * 事件ID:MyAssetsPageContactCustomerServiceClick
+ * 事件名称:我的资产联系客服点击事件
+ * 触发时机:用户在【我的资产】点击联系客服时触发上报
+ */
+ fun trackMyAssetsPageContactCustomerServiceClick() {
+ trackEvent(EVENT_MY_ASSETS_PAGE_CONTACT_CUSTOMER_SERVICE_CLICK)
+ }
+
+ fun trackSensorsAnalyticsFromWeb(eventName: String, hashmap: HashMap) {
+ val json = json {
+ hashmap.iterator().forEach { (key, value) ->
+ key to value
+ }
+ }
+ trackEvent(eventName, json)
+ }
+
+ /**
+ * 事件ID:MyAssetsPageContactCustomerServiceClick
+ * 事件名称:我的资产联系客服点击事件
+ * 触发时机:用户在【我的资产】点击联系客服时触发上报
+ */
+ fun trackStopAcceleratingDialogShow(
+ gameId: String,
+ gameName: String,
+ packageName: String
+ ) {
+ val json = json {
+ KEY_GAME_ID to gameId
+ KEY_GAME_NAME to gameName
+ KEY_PACKAGE_NAME to packageName
+ }
+ trackEvent(EVENT_STOP_ACCELERATING_DIALOG_SHOW, json)
+ }
}
\ No newline at end of file
diff --git a/module_common/src/main/res/layout/activity_member.xml b/module_common/src/main/res/layout/activity_member.xml
new file mode 100644
index 0000000000..ad74856f97
--- /dev/null
+++ b/module_common/src/main/res/layout/activity_member.xml
@@ -0,0 +1,16 @@
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/module_common/src/main/res/values-night/colors.xml b/module_common/src/main/res/values-night/colors.xml
index fdce12932e..b1bf77e819 100644
--- a/module_common/src/main/res/values-night/colors.xml
+++ b/module_common/src/main/res/values-night/colors.xml
@@ -5,6 +5,8 @@
#2888E0
#1A2888E0
+
+ #332888E0
#4D2888E0
diff --git a/module_common/src/main/res/values/colors.xml b/module_common/src/main/res/values/colors.xml
index d4dcfa0905..0b9addfec8 100644
--- a/module_common/src/main/res/values/colors.xml
+++ b/module_common/src/main/res/values/colors.xml
@@ -5,6 +5,8 @@
#2496FF
#1A2496FF
+
+ #332496FF
#4D2496FF
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/callback/OnAccelerateListener.kt b/module_core/src/main/java/com/gh/gamecenter/core/callback/OnAccelerateListener.kt
new file mode 100644
index 0000000000..8cb5bacdcf
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/callback/OnAccelerateListener.kt
@@ -0,0 +1,43 @@
+package com.gh.gamecenter.core.callback
+
+interface OnAccelerateListener {
+
+ fun onStateChanged(state: AccelerateState)
+
+ fun onProgress(progress: Int, curGamePkgName: String?, curGameZoneFlag: String?)
+}
+
+sealed class AccelerateState(val code: Int) {
+ // token 过期
+ val isTokenExpired: Boolean
+ get() = code == ACCT_CODE_TOKEN_EXPIRED
+
+ val isTokenEmpty: Boolean
+ get() = code == ACCT_CODE_TOKEN_EMPTY
+
+ val isSkipError: Boolean
+ get() = skipErrorCodes.any { it == code }
+
+ val isPermissionExpired: Boolean
+ get() = code == ACCT_CODE_PERMISSION_EXPIRE
+
+ data class Normal(private val _code: Int) : AccelerateState(_code)
+
+ data class Starting(private val _code: Int) : AccelerateState(_code)
+
+ data class Success(val acctGameInfo: Any?, private val _code: Int) : AccelerateState(_code)
+
+ data class Failure(private val _code: Int) : AccelerateState(_code)
+
+ data class OkStopping(private val _code: Int) : AccelerateState(_code)
+
+ data class ErrStopping(private val _code: Int) : AccelerateState(_code)
+
+ companion object {
+ private const val ACCT_CODE_TOKEN_EMPTY = 102
+ private const val ACCT_CODE_TOKEN_EXPIRED = 200 // token过期
+ private const val ACCT_CODE_PERMISSION_DENIED = 290 // 用户拒绝权限
+ private const val ACCT_CODE_PERMISSION_EXPIRE = 220 // 加速时长过期
+ private val skipErrorCodes = arrayOf(ACCT_CODE_PERMISSION_DENIED)
+ }
+}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorDataHolderProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorDataHolderProvider.kt
new file mode 100644
index 0000000000..d4444fbc93
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorDataHolderProvider.kt
@@ -0,0 +1,8 @@
+package com.gh.gamecenter.core.provider
+
+interface IAcceleratorDataHolderProvider {
+
+ fun setVipEntity(vip: Any)
+
+ fun clear()
+}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorProvider.kt
new file mode 100644
index 0000000000..4f38cac615
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAcceleratorProvider.kt
@@ -0,0 +1,28 @@
+package com.gh.gamecenter.core.provider
+
+import android.app.Application
+import com.gh.gamecenter.core.callback.OnAccelerateListener
+import com.therouter.inject.Singleton
+import java.io.File
+
+@Singleton
+interface IAcceleratorProvider {
+
+ fun init(isDev: Boolean, application: Application, version: String)
+
+ fun setQyUserToken(token: String, callback: ((Boolean) -> Unit)? = null)
+
+ fun deleteQyUserToken(): Boolean
+
+ fun startQyGameAccelerate(gameId: String, pkgName: String, zoneInfo: Any)
+
+ fun stopQyGameAccelerate(appLayerCallStopMsg: String? = null): Boolean
+
+ fun bindAccRelatedListener(pkgName: String, listener: OnAccelerateListener)
+
+ fun unBindAccRelatedListener(listener: OnAccelerateListener)
+
+ fun isCurAccSuccess(): Boolean
+
+ fun getAccLogFileStorageList(): List?
+}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IAliPayProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAliPayProvider.kt
new file mode 100644
index 0000000000..c92451dec1
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAliPayProvider.kt
@@ -0,0 +1,9 @@
+package com.gh.gamecenter.core.provider
+
+import android.app.Activity
+import io.reactivex.Single
+
+interface IAliPayProvider {
+
+ fun sendRequest(activity: Activity, orderNo: String): Single
+}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/ISentryProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/ISentryProvider.kt
index 784474fade..5b455b625f 100644
--- a/module_core/src/main/java/com/gh/gamecenter/core/provider/ISentryProvider.kt
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/ISentryProvider.kt
@@ -3,7 +3,6 @@ package com.gh.gamecenter.core.provider
import android.content.Context
-
interface ISentryProvider {
fun init(context: Context, channel: String, flavor: String, versionName: String)
@@ -12,4 +11,6 @@ interface ISentryProvider {
fun captureException(e: Throwable)
+ fun onEventInAllChannel(eventId: String, vararg kv: String?)
+
}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayProvider.kt
new file mode 100644
index 0000000000..56eb77d395
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayProvider.kt
@@ -0,0 +1,12 @@
+package com.gh.gamecenter.core.provider
+
+interface IWechatPayProvider {
+
+ val orderNo: String
+
+ val order: Any?
+
+ fun payRequest(order: Any, payRequest: Any)
+
+ fun clear()
+}
\ No newline at end of file
diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayResultProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayResultProvider.kt
new file mode 100644
index 0000000000..c552dd1a75
--- /dev/null
+++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IWechatPayResultProvider.kt
@@ -0,0 +1,6 @@
+package com.gh.gamecenter.core.provider
+
+interface IWechatPayResultProvider {
+
+ fun onPayComplete(nonceStr: String, order: Any?)
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctGameInfo.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctGameInfo.kt
new file mode 100644
index 0000000000..cb326e81d5
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctGameInfo.kt
@@ -0,0 +1,35 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+@Entity(tableName = "AcctGameInfo")
+data class AcctGameInfo(
+ @PrimaryKey(autoGenerate = false)
+ val gameId: String,
+ val accGamePkgName: String,
+ val zoneInfo: ZoneInfo,
+ val notifyGameName: String? = null,
+ val notifyGameTxtTitle: String? = null,
+ val notifyGameSmallLogo: Int? = null,
+ val notifyGameLargeLogo: Int? = null,
+ val notifyClickParam: String? = null,
+) : Parcelable {
+
+ val zoneName: String
+ get() = zoneInfo.cnName ?: ""
+
+ @Parcelize
+ data class ZoneInfo(
+ @SerializedName(value = "_id", alternate = ["id"])
+ val id: Int,
+ @SerializedName(value = "name", alternate = ["cnName"])
+ val cnName: String? = null,
+ @SerializedName(value = "en_name", alternate = ["enName"])
+ val enName: String? = null
+ ) : Parcelable
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecord.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecord.kt
new file mode 100644
index 0000000000..7849de6767
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecord.kt
@@ -0,0 +1,17 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import androidx.room.Entity
+import androidx.room.PrimaryKey
+import kotlinx.parcelize.Parcelize
+
+@Entity
+@Parcelize
+data class AcctRecord(
+ @PrimaryKey(autoGenerate = false)
+ val gameId: String,
+ val game: GameEntity,
+ val zoneInfo: AcctGameInfo.ZoneInfo,
+ val hasMultiZone: Boolean,
+ val createTime: Long
+) : Parcelable
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecordEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecordEntity.kt
new file mode 100644
index 0000000000..8494b779c2
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AcctRecordEntity.kt
@@ -0,0 +1,40 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class AcctRecordEntity(
+ @SerializedName("accInfo")
+ private val _accInfo: AccInfo? = null
+) : Parcelable {
+
+ val accInfo: AccInfo
+ get() = _accInfo ?: AccInfo()
+
+ @Parcelize
+ data class AccInfo(
+ @SerializedName("game")
+ private val _game: GameEntity? = null,
+ @SerializedName("service_area_id")
+ private val _serviceAreaId: Int? = null,
+ @SerializedName("service_area")
+ private val _serviceArea: String? = null,
+ @SerializedName("is_more_service_area")
+ private val _isMoreServiceArea: Boolean? = null
+ ) : Parcelable {
+
+ val game: GameEntity
+ get() = _game ?: GameEntity()
+
+ val serviceAreaId: Int
+ get() = _serviceAreaId ?: 0
+
+ val serviceArea: String
+ get() = _serviceArea ?: ""
+
+ val isMoreServiceArea: Boolean
+ get() = _isMoreServiceArea ?: false
+ }
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AliPayEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AliPayEntity.kt
new file mode 100644
index 0000000000..7239196152
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/AliPayEntity.kt
@@ -0,0 +1,20 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class AliPayEntity(
+ @SerializedName("order_no")
+ private val _orderNo: String? = null,
+ @SerializedName("order_str")
+ private val _orderStr: String? = null
+) : Parcelable {
+
+ val orderNo: String
+ get() = _orderNo ?: ""
+
+ val orderStr: String
+ get() = _orderStr ?: ""
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/BaseEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/BaseEntity.kt
new file mode 100644
index 0000000000..273269f81a
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/BaseEntity.kt
@@ -0,0 +1,8 @@
+package com.gh.gamecenter.feature.entity
+
+
+data class BaseEntity(
+ val code: Int? = null,
+ val message: String? = null,
+ val data: T? = null
+)
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt
index 931d2037e3..a865d00a25 100644
--- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/GameEntity.kt
@@ -19,6 +19,7 @@ import com.therouter.TheRouter
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
import java.util.*
+import kotlin.collections.ArrayList
/**
* 关联的后端数据库结构说明 ...
@@ -335,6 +336,10 @@ data class GameEntity(
var articleData: CommunityTopEntity? = null,
@SerializedName("wifi_auto_download")
private val _wifiAutoDownload: Boolean? = null, // 只有预约上线弹窗/我的预约接口接口会返回这个字段
+ @SerializedName("accelerator_status")
+ private var _acceleratorStatus: Boolean? = null,
+ @SerializedName("service_area")
+ private var _serviceArea: List? = null,
// 本地字段
// 用来标记在专题中的序号,仅用于曝光记录
@@ -422,6 +427,9 @@ data class GameEntity(
@IgnoredOnParcel
var customPageTrackData: CustomPageTrackData? = null
+ @IgnoredOnParcel
+ var lastAcctGame: AcctGameInfo? = null
+
// 临时变量
@IgnoredOnParcel
var hasBubbleShowed = false
@@ -430,7 +438,9 @@ data class GameEntity(
get() = _assignRemark ?: AssignRemark()
var id: String
- set(value) { _id = value }
+ set(value) {
+ _id = value
+ }
get() = if (isWechatMiniGameCPM()) miniGameAppId else _id
@IgnoredOnParcel
@@ -697,6 +707,22 @@ data class GameEntity(
val wifiAutoDownloadEnable: Boolean
get() = _wifiAutoDownload != null
+ var acceleratorStatus: Boolean
+ get() = _acceleratorStatus ?: false
+ set(value) {
+ _acceleratorStatus = value
+ }
+
+ var serviceArea: List
+ get() = _serviceArea ?: emptyList()
+ set(value) {
+ _serviceArea = value
+ }
+
+ // 配置了加速并且只有一个apk包
+ val canSpeed: Boolean
+ get() = acceleratorStatus && getApk().size == 1 && serviceArea.isNotEmpty()
+
//是否需要弹出试玩弹窗
fun isShowVersionNumber(): Boolean {
return versionNumber == "无版号无内购有弹窗"
@@ -821,10 +847,11 @@ data class GameEntity(
return category.equals("gjonline")
}
- val miniGameType: String get() = when(profit) {
- Constants.WECHAT_MINI_GAME_PROFIT_CPM -> Constants.WECHAT_MINI_GAME_CPM
- else -> miniGameCategory
- }
+ val miniGameType: String
+ get() = when (profit) {
+ Constants.WECHAT_MINI_GAME_PROFIT_CPM -> Constants.WECHAT_MINI_GAME_CPM
+ else -> miniGameCategory
+ }
/**
* 当前游戏是否为小游戏
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/OrderEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/OrderEntity.kt
new file mode 100644
index 0000000000..34690cf252
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/OrderEntity.kt
@@ -0,0 +1,22 @@
+package com.gh.gamecenter.feature.entity
+
+import com.google.gson.annotations.SerializedName
+
+data class OrderEntity(
+ @SerializedName("good_id")
+ private val _goodId: String? = null,
+ @SerializedName("set_menu_name")
+ private val _setMenuName: String? = null,
+ @SerializedName("payment_amount")
+ private val _paymentAmount: String? = null
+) {
+
+ val goodId: String
+ get() = _goodId ?: ""
+
+ val setMenuName: String
+ get() = _setMenuName ?: ""
+
+ val paymentAmount: String
+ get() = _paymentAmount ?: ""
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/PackageDialogEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/PackageDialogEntity.kt
index 42e4963600..d03cf79da1 100644
--- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/PackageDialogEntity.kt
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/PackageDialogEntity.kt
@@ -15,6 +15,7 @@ data class PackageDialogEntity(
val detectionObjects: ArrayList = arrayListOf(),
@SerializedName("link_hint_text")
val linkHintText: String = "",
+ @SerializedName("links")
val links: ArrayList = arrayListOf(),
val level: String = "HINT_SKIP"// 弹窗级别,HINT_SKIP(提示且跳转)、ALWAYS_HINT(仅提示(每次提示))、OPTIONAL_HINT(仅提示(可不再提示))、OPTIONAL_CURRENT_HINT(仅提示(可关闭当前游戏的弹窗)
) : Parcelable
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/QyToken.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/QyToken.kt
new file mode 100644
index 0000000000..5a22059298
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/QyToken.kt
@@ -0,0 +1,12 @@
+package com.gh.gamecenter.feature.entity
+
+import com.google.gson.annotations.SerializedName
+
+data class QyToken(
+ @SerializedName("token")
+ private val _token: String? = null
+) {
+
+ val token: String
+ get() = _token ?: ""
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/TrialEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/TrialEntity.kt
new file mode 100644
index 0000000000..ee197d13ba
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/TrialEntity.kt
@@ -0,0 +1,15 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class TrialEntity(
+ @SerializedName("result")
+ private val _result: Boolean? = null
+) : Parcelable {
+
+ val result: Boolean
+ get() = _result ?: false
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/VipEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/VipEntity.kt
new file mode 100644
index 0000000000..9440e9bfb6
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/VipEntity.kt
@@ -0,0 +1,22 @@
+package com.gh.gamecenter.feature.entity
+
+import com.google.gson.annotations.SerializedName
+
+data class VipEntity(
+ @SerializedName("vip_status")
+ private val _vipStatus: Boolean? = null,
+ @SerializedName("is_new_user")
+ private val _isNewUser: Boolean? = null,
+ @SerializedName("is_try_vip")
+ private val _isTryVip: Boolean? = null
+) {
+
+ val vipStatus: Boolean
+ get() = _vipStatus ?: false
+
+ val isNewUser: Boolean
+ get() = _isNewUser ?: false
+
+ val isTryVip: Boolean
+ get() = _isTryVip ?: false
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WechatPayEntity.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WechatPayEntity.kt
new file mode 100644
index 0000000000..d9f4f3a716
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/entity/WechatPayEntity.kt
@@ -0,0 +1,50 @@
+package com.gh.gamecenter.feature.entity
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.parcelize.Parcelize
+
+@Parcelize
+data class WechatPayEntity(
+ @SerializedName("appid")
+ private val _appId: String? = null,
+ @SerializedName("partnerid")
+ private val _partnerId: String? = null,
+ @SerializedName("prepayid")
+ private val _prepayId: String? = null,
+ @SerializedName("package")
+ private val _package: String? = null,
+ @SerializedName("noncestr")
+ private val _nonceStr: String? = null,
+ @SerializedName("timestamp")
+ private val _timestamp: String? = null,
+ @SerializedName("sign")
+ private val _sign: String? = null,
+ @SerializedName("order_no")
+ private val _orderNo: String? = null
+) : Parcelable {
+
+ val appId: String
+ get() = _appId ?: ""
+
+ val partnerId: String
+ get() = _partnerId ?: ""
+
+ val prepayId: String
+ get() = _prepayId ?: ""
+
+ val packageValue: String
+ get() = _package ?: ""
+
+ val nonceStr: String
+ get() = _nonceStr ?: ""
+
+ val timestamp: String
+ get() = _timestamp ?: ""
+
+ val sign: String
+ get() = _sign ?: ""
+
+ val orderNo: String
+ get() = _orderNo ?: ""
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/eventbus/EBPayState.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/eventbus/EBPayState.kt
new file mode 100644
index 0000000000..79eb45268b
--- /dev/null
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/eventbus/EBPayState.kt
@@ -0,0 +1,7 @@
+package com.gh.gamecenter.feature.eventbus
+
+sealed class EBPayState {
+ data object PaySuccess : EBPayState()
+ data object PayFail : EBPayState()
+ data object PayCancel : EBPayState()
+}
\ No newline at end of file
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/utils/SentryHelper.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/utils/SentryHelper.kt
index f2bdd8c1ac..09ccf16c73 100644
--- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/utils/SentryHelper.kt
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/utils/SentryHelper.kt
@@ -14,4 +14,13 @@ object SentryHelper {
sentryProvider?.onEvent(eventId, *kv)
}
+ /**
+ * 上面的日志只会上传到 gh-206 渠道
+ * 这个方法会在全渠道上传
+ */
+ fun onEventInAllChannel(eventId: String, vararg kv: String?) {
+ val sentryProvider = TheRouter.get(ISentryProvider::class.java)
+ sentryProvider?.onEventInAllChannel(eventId, *kv)
+ }
+
}
diff --git a/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/DownloadButton.kt b/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/DownloadButton.kt
index 89caf69b96..ee08ea6215 100644
--- a/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/DownloadButton.kt
+++ b/module_core_feature/src/main/java/com/gh/gamecenter/feature/view/DownloadButton.kt
@@ -10,13 +10,12 @@ import android.view.MotionEvent
import android.widget.ProgressBar
import androidx.annotation.ColorInt
import androidx.annotation.StringRes
-import com.therouter.TheRouter
-import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.common.utils.toDrawable
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.feature.R
import com.gh.gamecenter.feature.provider.IDownloadButtonClickedProvider
+import com.therouter.TheRouter
class DownloadButton @JvmOverloads constructor(
context: Context,
@@ -65,10 +64,15 @@ class DownloadButton @JvmOverloads constructor(
init {
if (attrs != null) {
val ta = context.obtainStyledAttributes(attrs, com.gh.gamecenter.common.R.styleable.DownloadButton)
- showProgress = ta.getBoolean(com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_show_progress, false)
- mShowPercent = ta.getBoolean(com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_show_percent, false)
+ showProgress =
+ ta.getBoolean(com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_show_progress, false)
+ mShowPercent =
+ ta.getBoolean(com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_show_percent, false)
mDownloadStyle =
- ta.getInteger(com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_download_style, DOWNLOAD_NORMAL_STYLE)
+ ta.getInteger(
+ com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_download_style,
+ DOWNLOAD_NORMAL_STYLE
+ )
mTextSize = ta.getDimensionPixelSize(
com.gh.gamecenter.common.R.styleable.DownloadButton_download_button_text_size,
DisplayUtils.sp2px(context, 12F)
@@ -90,7 +94,8 @@ class DownloadButton @JvmOverloads constructor(
override fun onDraw(canvas: Canvas) {
super.onDraw(canvas)
if (mText.isEmpty()) return
- mPaint.color = if (mPaintColor == 0) com.gh.gamecenter.common.R.color.text_theme.toColor(context) else mPaintColor // 初始化颜色
+ mPaint.color =
+ if (mPaintColor == 0) com.gh.gamecenter.common.R.color.text_theme.toColor(context) else mPaintColor // 初始化颜色
mPaint.textSize = mTextSize.toFloat()
mFakeTextPaint.textSize = mTextSize.toFloat()
mFakeTextPaint.style = Paint.Style.FILL_AND_STROKE
@@ -160,7 +165,10 @@ class DownloadButton @JvmOverloads constructor(
progressDrawable = null
background = when (mDownloadStyle) {
DOWNLOAD_IMAGE_STYLE -> R.drawable.text_white_background.toDrawable(context)
- DOWNLOAD_HOME_PUSH_STYLE -> com.gh.gamecenter.common.R.drawable.bg_shape_theme_radius_999.toDrawable(context)
+ DOWNLOAD_HOME_PUSH_STYLE -> com.gh.gamecenter.common.R.drawable.bg_shape_theme_radius_999.toDrawable(
+ context
+ )
+
else -> com.gh.gamecenter.common.R.drawable.download_button_normal_style.toDrawable(context)
}
progress = 0
@@ -169,16 +177,21 @@ class DownloadButton @JvmOverloads constructor(
context
)
}
+
ButtonStyle.LAUNCH_OR_OPEN -> {
progressDrawable = null
background = when (mDownloadStyle) {
DOWNLOAD_IMAGE_STYLE -> R.drawable.detail_download_open_image_style.toDrawable(context)
- DOWNLOAD_HOME_PUSH_STYLE -> com.gh.gamecenter.common.R.drawable.bg_shape_theme_radius_999.toDrawable(context)
+ DOWNLOAD_HOME_PUSH_STYLE -> com.gh.gamecenter.common.R.drawable.bg_shape_theme_radius_999.toDrawable(
+ context
+ )
+
else -> com.gh.gamecenter.common.R.drawable.download_button_normal_style.toDrawable(context)
}
progress = 0
mDefaultColor = com.gh.gamecenter.common.R.color.white.toColor(context)
}
+
ButtonStyle.RESERVABLE,
ButtonStyle.UPDATING -> {
progressDrawable = null
@@ -186,6 +199,7 @@ class DownloadButton @JvmOverloads constructor(
progress = 0
mDefaultColor = com.gh.gamecenter.common.R.color.white.toColor(context)
}
+
ButtonStyle.FAILURE,
ButtonStyle.NONE,
ButtonStyle.RESERVED -> {
@@ -194,6 +208,7 @@ class DownloadButton @JvmOverloads constructor(
progress = 0
mDefaultColor = com.gh.gamecenter.common.R.color.text_tertiary.toColor(context)
}
+
ButtonStyle.PLUGIN,
ButtonStyle.INSTALL_PLUGIN -> {
progressDrawable = null
@@ -201,6 +216,7 @@ class DownloadButton @JvmOverloads constructor(
progress = 0
mDefaultColor = com.gh.gamecenter.common.R.color.white.toColor(context)
}
+
ButtonStyle.XAPK_SUCCESS,
ButtonStyle.XAPK_UNZIPPING,
ButtonStyle.DOWNLOADING_NORMAL -> {
@@ -210,7 +226,9 @@ class DownloadButton @JvmOverloads constructor(
context
) else R.drawable.detail_downloading_normal_style.toDrawable(context)
mDefaultColor =
- if (mDownloadStyle == DOWNLOAD_IMAGE_STYLE) com.gh.gamecenter.common.R.color.white.toColor(context) else com.gh.gamecenter.common.R.color.text_theme.toColor(
+ if (mDownloadStyle == DOWNLOAD_IMAGE_STYLE) com.gh.gamecenter.common.R.color.white.toColor(
+ context
+ ) else com.gh.gamecenter.common.R.color.text_theme.toColor(
context
)
} else {
@@ -219,6 +237,7 @@ class DownloadButton @JvmOverloads constructor(
mPressedColor = com.gh.gamecenter.common.R.color.white.toColor(context)
}
}
+
ButtonStyle.DOWNLOADING_PLUGIN -> {
if (showProgress) {
progressDrawable = R.drawable.detail_downloading_normal_style.toDrawable(context)
@@ -229,12 +248,14 @@ class DownloadButton @JvmOverloads constructor(
mPressedColor = com.gh.gamecenter.common.R.color.white.toColor(context)
}
}
+
ButtonStyle.WAITING -> {
progressDrawable = null
background = R.drawable.button_round_border_eeeeee.toDrawable(context)
progress = 0
mDefaultColor = com.gh.gamecenter.common.R.color.text_tertiary.toColor(context)
}
+
ButtonStyle.PAUSE -> {
progressDrawable = null
background = R.drawable.button_normal_round_border.toDrawable(context)
@@ -303,4 +324,9 @@ class DownloadButton @JvmOverloads constructor(
const val DOWNLOAD_HOME_PUSH_STYLE = 1
const val DOWNLOAD_IMAGE_STYLE = 2
}
+
+ interface OnUpdateListener {
+
+ fun completion(text: String)
+ }
}
\ No newline at end of file
diff --git a/module_login/src/main/java/com/gh/gamecenter/login/retrofit/ApiService.java b/module_login/src/main/java/com/gh/gamecenter/login/retrofit/ApiService.java
index 80e754bdae..e419a909c2 100644
--- a/module_login/src/main/java/com/gh/gamecenter/login/retrofit/ApiService.java
+++ b/module_login/src/main/java/com/gh/gamecenter/login/retrofit/ApiService.java
@@ -1,5 +1,8 @@
package com.gh.gamecenter.login.retrofit;
+import com.gh.gamecenter.feature.entity.BaseEntity;
+import com.gh.gamecenter.feature.entity.QyToken;
+import com.gh.gamecenter.feature.entity.VipEntity;
import com.gh.gamecenter.login.entity.LoginTokenEntity;
import com.gh.gamecenter.login.entity.UserInfoEntity;
@@ -14,6 +17,7 @@ import retrofit2.http.Headers;
import retrofit2.http.POST;
import retrofit2.http.PUT;
import retrofit2.http.Path;
+import retrofit2.http.Query;
import retrofit2.http.Url;
public interface ApiService {
@@ -123,5 +127,9 @@ public interface ApiService {
@POST("./certification:sync")
Observable postSyncCertification(@Body RequestBody body);
+ @GET("vip/user/{userId}/qiyou_token")
+ Single> getQyToken(@Path("userId") String userId, @Query("type") String type);
+ @GET("vip/user/{userId}/effective_duration")
+ Single> getVipStatus(@Path("userId") String userId, @Query("type") String type, @Query("refresh") boolean refresh);
}
diff --git a/module_login/src/main/java/com/gh/gamecenter/login/user/UserManager.java b/module_login/src/main/java/com/gh/gamecenter/login/user/UserManager.java
index a97fc65b22..6e34cbda51 100644
--- a/module_login/src/main/java/com/gh/gamecenter/login/user/UserManager.java
+++ b/module_login/src/main/java/com/gh/gamecenter/login/user/UserManager.java
@@ -7,20 +7,20 @@ import android.text.TextUtils;
import androidx.annotation.Nullable;
-import com.therouter.TheRouter;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.constant.Constants;
-import com.gh.gamecenter.common.constant.RouteConsts;
import com.gh.gamecenter.common.eventbus.EBShowDialog;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.EnvHelper;
+import com.gh.gamecenter.core.provider.IAcceleratorProvider;
import com.gh.gamecenter.core.provider.IErrorHelperProvider;
import com.gh.gamecenter.core.provider.IReservationRepositoryProvider;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
+import com.gh.gamecenter.feature.entity.QyToken;
import com.gh.gamecenter.login.HaloApp;
import com.gh.gamecenter.login.entity.LoginTokenEntity;
import com.gh.gamecenter.login.entity.TokenEntity;
@@ -28,12 +28,16 @@ import com.gh.gamecenter.login.entity.UserInfoEntity;
import com.gh.gamecenter.login.retrofit.ApiService;
import com.gh.gamecenter.login.retrofit.RetrofitManager;
import com.lightgame.utils.Utils;
+import com.therouter.TheRouter;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
import org.json.JSONObject;
+import io.reactivex.ObservableSource;
+import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
+import kotlin.Pair;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
@@ -150,12 +154,31 @@ public class UserManager {
}
getApiService()
.refreshToken(EnvHelper.getHost() + "tokens:refresh", body)
- .subscribe(new Response() {
+ .flatMap(new Function>>() {
@Override
- public void onResponse(LoginTokenEntity response) {
+ public ObservableSource> apply(LoginTokenEntity loginTokenEntity) throws Exception {
+ return UserRepository.getInstance().loadQyToken(getUserId(), loginTokenEntity);
+ }
+ })
+ .subscribe(new Response>() {
+ @Override
+ public void onResponse(Pair response) {
super.onResponse(response);
- saveLoginToken(response);
+ QyToken qyToken = response.getFirst();
+ IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
+ if (acceleratorProvider != null && qyToken != null) {
+ acceleratorProvider.setQyUserToken(qyToken.getToken(), null);
+ }
+
+ LoginTokenEntity loginTokenEntity = response.getSecond();
+ if (loginTokenEntity == null) {
+ // 理论上不会出现 loginTokenEntity == null
+ UserRepository.getInstance().logout();
+ callBack.onLoginFailure("refresh token failed");
+ return;
+ }
+ saveLoginToken(loginTokenEntity);
refreshUserInfo(callBack);
@@ -166,6 +189,7 @@ public class UserManager {
}
refreshUserRegulationTestStatus();
+
}
@Override
diff --git a/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java b/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java
index 248d550549..e2cbc86c6a 100644
--- a/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java
+++ b/module_login/src/main/java/com/gh/gamecenter/login/user/UserRepository.java
@@ -14,7 +14,6 @@ import androidx.annotation.Nullable;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
-import com.therouter.TheRouter;
import com.gh.gamecenter.common.callback.BiCallback;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
@@ -23,8 +22,11 @@ import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.EnvHelper;
+import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.PackageFlavorHelper;
import com.gh.gamecenter.common.utils.SensorsBridge;
+import com.gh.gamecenter.core.provider.IAcceleratorDataHolderProvider;
+import com.gh.gamecenter.core.provider.IAcceleratorProvider;
import com.gh.gamecenter.core.provider.IAppProvider;
import com.gh.gamecenter.core.provider.IDataUtilsProvider;
import com.gh.gamecenter.core.provider.IDownloadManagerProvider;
@@ -37,6 +39,9 @@ import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.feature.entity.AvatarBorderEntity;
import com.gh.gamecenter.feature.entity.BackgroundImageEntity;
+import com.gh.gamecenter.feature.entity.BaseEntity;
+import com.gh.gamecenter.feature.entity.QyToken;
+import com.gh.gamecenter.feature.entity.VipEntity;
import com.gh.gamecenter.feature.provider.IMessageUnreadRepositoryProvider;
import com.gh.gamecenter.login.HaloApp;
import com.gh.gamecenter.login.R;
@@ -51,6 +56,7 @@ import com.gh.gamecenter.login.utils.LoginUtils;
import com.gh.gamecenter.login.utils.NewLogUtils;
import com.gh.gamecenter.login.utils.QuickLoginHelper;
import com.lightgame.utils.Utils;
+import com.therouter.TheRouter;
import org.greenrobot.eventbus.EventBus;
import org.json.JSONException;
@@ -60,8 +66,15 @@ import java.util.HashMap;
import java.util.Map;
import io.reactivex.Observable;
+import io.reactivex.ObservableSource;
+import io.reactivex.Single;
import io.reactivex.android.schedulers.AndroidSchedulers;
+import io.reactivex.functions.Function;
import io.reactivex.schedulers.Schedulers;
+import kotlin.Pair;
+import kotlin.Triple;
+import kotlin.Unit;
+import kotlin.jvm.functions.Function1;
import okhttp3.MediaType;
import okhttp3.RequestBody;
import okhttp3.ResponseBody;
@@ -174,11 +187,21 @@ public class UserRepository {
IDataUtilsProvider dataUtils = TheRouter.get(IDataUtilsProvider.class);
if (dataUtils != null) {
- dataUtils.getDeviceCertification();
+ dataUtils.getDeviceCertification();
}
// 退出登录后进行预取号
QuickLoginHelper.preLogin(mContext);
+
+ // 退出加速器
+ IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
+ if (acceleratorProvider != null) {
+ acceleratorProvider.deleteQyUserToken();
+ }
+ IAcceleratorDataHolderProvider iAcceleratorDataHolderProvider = TheRouter.get(IAcceleratorDataHolderProvider.class);
+ if (iAcceleratorDataHolderProvider != null) {
+ iAcceleratorDataHolderProvider.clear();
+ }
}
public LiveData> getLoginUserInfo() {
@@ -304,8 +327,8 @@ public class UserRepository {
} else {
Utils.toast(mContext, mContext.getString(R.string.login_failure_hint));
}
- }else {
- Utils.toast(mContext,mContext.getString(R.string.auth_login_failure_hint));
+ } else {
+ Utils.toast(mContext, mContext.getString(R.string.auth_login_failure_hint));
}
} catch (Exception e1) {
e1.printStackTrace();
@@ -426,6 +449,7 @@ public class UserRepository {
public void refreshUserInfo(LoginTag loginTag) {
mApiService
.getUserInfo(EnvHelper.getHost() + "tokens:validate")
+ .flatMap((Function>>) this::setQyTokenAndVipState)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(userInfoResponse(loginTag));
@@ -520,14 +544,34 @@ public class UserRepository {
});
}
- private Response userInfoResponse(final LoginTag loginTag) {
- return new Response() {
+ private Response> userInfoResponse(final LoginTag loginTag) {
+ return new Response>() {
@SuppressLint("CheckResult")
@Override
- public void onResponse(UserInfoEntity response) {
+ public void onResponse(Triple response) {
super.onResponse(response);
- userInfoHandle(response, false);
+ // 设置加速器token
+ QyToken qyToken = response.getFirst();
+ IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
+ if (acceleratorProvider != null && qyToken != null) {
+ acceleratorProvider.setQyUserToken(qyToken.getToken(), null);
+ }
+
+ // 保存 vip 状态
+ VipEntity vipEntity = response.getSecond();
+ IAcceleratorDataHolderProvider acceleratorDataHolderProvider = TheRouter.get(IAcceleratorDataHolderProvider.class);
+ if (acceleratorDataHolderProvider != null && vipEntity != null) {
+ acceleratorDataHolderProvider.setVipEntity(vipEntity);
+ }
+
+ UserInfoEntity userInfo = response.getThird();
+ if (userInfo == null) {
+ // 理论上这里不会调用,做这个判断只是为了消除空指针提示
+ loginFailed(loginTag, new ApiResponse<>());
+ return;
+ }
+ userInfoHandle(userInfo, false);
IReservationRepositoryProvider reservationRepository = TheRouter.get(IReservationRepositoryProvider.class);
if (reservationRepository != null) {
reservationRepository.refreshReservations();
@@ -548,7 +592,7 @@ public class UserRepository {
IPushProvider pushProvider = TheRouter.get(IPushProvider.class);
if (pushProvider != null) {
pushProvider
- .bindAlias(response.getUserId())
+ .bindAlias(userInfo.getUserId())
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe((s) -> {
@@ -557,8 +601,8 @@ public class UserRepository {
}
// 登录成功后,刷新qq小游戏最近在玩
- IMiniGameRecentlyPlayedProvider miniGameRecentlyPlayedProvider= TheRouter.get(IMiniGameRecentlyPlayedProvider.class);
- if(miniGameRecentlyPlayedProvider != null){
+ IMiniGameRecentlyPlayedProvider miniGameRecentlyPlayedProvider = TheRouter.get(IMiniGameRecentlyPlayedProvider.class);
+ if (miniGameRecentlyPlayedProvider != null) {
miniGameRecentlyPlayedProvider.refreshQQMiniRecentPlayed();
}
}
@@ -577,31 +621,133 @@ public class UserRepository {
}
@Override
- public void onApiFailure(ApiResponse e) {
+ public void onApiFailure(ApiResponse> e) {
super.onApiFailure(e);
- if (loginTag != null) {
- mLoginObsResponseUserInfo.postValue(e);
-
- IErrorHelperProvider errorHelper = TheRouter.get(IErrorHelperProvider.class);
- if (errorHelper != null) {
- errorHelper.handleLoginError(mContext, e.getHttpException(), loginTag.toChinese(), false);
- }
- logout();
- }
-
- if (PackageFlavorHelper.IS_TEST_FLAVOR) {
- try {
- HttpException httpException = e.getHttpException();
- ResponseBody responseBody = httpException.response().errorBody();
- Utils.log("UserInfo ->onApiFailure Code:" + httpException.code() + " ErrorBody:" + responseBody.string());
- } catch (Exception e1) {
- e1.printStackTrace();
- }
- }
+ loginFailed(loginTag, e);
}
};
}
+ private void loginFailed(LoginTag loginTag, ApiResponse> e) {
+ if (loginTag != null) {
+ ApiResponse apiResponse = new ApiResponse<>();
+ apiResponse.setThrowable(e.getThrowable());
+ apiResponse.setHttpException(e.getHttpException());
+ mLoginObsResponseUserInfo.postValue(apiResponse);
+
+ IErrorHelperProvider errorHelper = TheRouter.get(IErrorHelperProvider.class);
+ if (errorHelper != null) {
+ errorHelper.handleLoginError(mContext, e.getHttpException(), loginTag.toChinese(), false);
+ }
+ logout();
+ }
+
+ if (PackageFlavorHelper.IS_TEST_FLAVOR) {
+ try {
+ HttpException httpException = e.getHttpException();
+ ResponseBody responseBody = httpException.response().errorBody();
+ Utils.log("UserInfo ->onApiFailure Code:" + httpException.code() + " ErrorBody:" + responseBody.string());
+ } catch (Exception e1) {
+ e1.printStackTrace();
+ }
+ }
+ }
+
+ @SuppressLint("CheckResult")
+ public void setAcceleratorToken(String userId, Function1 function) {
+ RetrofitManager.getInstance().getNewApi()
+ .getQyToken(userId, "gjonline_vip")
+ .compose(ExtensionsKt.singleToMain())
+ .subscribe(new BiResponse>() {
+
+ @Override
+ public void onSuccess(BaseEntity data) {
+ QyToken qyToken = data.getData();
+ if (qyToken != null) {
+ String token = qyToken.getToken();
+ if (!token.isEmpty()) {
+ IAcceleratorProvider acceleratorProvider = TheRouter.get(IAcceleratorProvider.class);
+ if (acceleratorProvider != null) {
+ acceleratorProvider.setQyUserToken(token, function);
+ }
+ } else {
+ if (function != null) {
+ function.invoke(false);
+ }
+
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Exception exception) {
+ super.onFailure(exception);
+ if (function != null) {
+ function.invoke(false);
+ }
+ }
+ });
+ // 刷新奇游token的同时,还需要重新获取vip状态
+ refreshVipStatus(userId, false);
+ }
+
+ @SuppressLint("CheckResult")
+ public void refreshVipStatus(String userId, boolean refresh) {
+ // 每次更新token成功,都需要重新获取vip状态
+ RetrofitManager.getInstance().getNewApi().getVipStatus(userId, "gjonline_vip", refresh)
+ .map(vipEntityBaseEntity -> vipEntityBaseEntity)
+ .compose(ExtensionsKt.singleToMain())
+ .subscribe(new BiResponse>() {
+ @Override
+ public void onSuccess(BaseEntity data) {
+ IAcceleratorDataHolderProvider acceleratorProvider = TheRouter.get(IAcceleratorDataHolderProvider.class);
+ if (acceleratorProvider != null) {
+ VipEntity vip = data.getData();
+ if (vip != null) {
+ acceleratorProvider.setVipEntity(vip);
+ }
+ }
+ }
+
+ @Override
+ public void onFailure(@NonNull Exception exception) {
+ super.onFailure(exception);
+ }
+ });
+ }
+
+ /**
+ * 用户登录之后调用(包括自动登录和主动登录)
+ * 1.刷新 奇游token
+ * 2.获取vip状态
+ */
+ public Observable> setQyTokenAndVipState(UserInfoEntity userInfoEntity) {
+ String userId = userInfoEntity.getUserId();
+ String vipType = "gjonline_vip";
+ Single qyTokenSingle = RetrofitManager.getInstance().getNewApi()
+ .getQyToken(userId, vipType)
+ .map(BaseEntity::getData)
+ .onErrorReturnItem(new QyToken());
+ Single vipSingle = RetrofitManager.getInstance().getNewApi()
+ .getVipStatus(userId, vipType, true)
+ .map(BaseEntity::getData)
+ .onErrorReturnItem(new VipEntity());
+ return Single.zip(
+ qyTokenSingle,
+ vipSingle,
+ (qyToken, vipEntity) -> new Triple<>(qyToken, vipEntity, userInfoEntity)).toObservable();
+ }
+
+ /**
+ * 光环刷新token之后调用:刷新奇游token
+ */
+ public Observable> loadQyToken(String userId, LoginTokenEntity loginTokenEntity) {
+ return RetrofitManager.getInstance().getNewApi()
+ .getQyToken(userId, "gjonline_vip")
+ .map(qyTokenBaseEntity -> new Pair<>(qyTokenBaseEntity.getData(), loginTokenEntity))
+ .onErrorReturnItem(new Pair<>(null, loginTokenEntity))
+ .toObservable();
+ }
private void userInfoHandle(final UserInfoEntity userInfo, boolean isEditUserInfo) {
SPUtils.setString(mPreferences, Constants.USER_INFO_KEY, GsonUtils.toJson(userInfo));
@@ -652,7 +798,6 @@ public class UserRepository {
if (userInfoEntity != null) {
UserManager.getInstance().setUserInfoEntity(userInfoEntity);
}
-
}
public MediatorLiveData> getEditObsUserInfo() {
diff --git a/scripts/build_with_simple_backup.sh b/scripts/build_with_simple_backup.sh
index 555ae3e016..d301862d62 100755
--- a/scripts/build_with_simple_backup.sh
+++ b/scripts/build_with_simple_backup.sh
@@ -16,7 +16,7 @@ git checkout module_sensors_data/build.gradle
# 开启 mapping 上传
if [[ "$OSTYPE" == "darwin"* ]]; then
- sed -i '' '1 a plugins { id "io.sentry.android.gradle" version "3.7.0" } ' app/build.gradle
+ sed -i '' "1s|^|plugins { id \"io.sentry.android.gradle\" version \"3.7.0\" }\n|" app/build.gradle
else
sed -i '1 a plugins { id "io.sentry.android.gradle" version "3.7.0" }' app/build.gradle
fi
@@ -77,7 +77,7 @@ done
if [ $BUILD_WITH_INIT_GRADLE == false ]; then
./gradlew assemble${BUILD_VARIANT_ENV}${BUILD_VARIANT_REGION^}Release -PBUILD_PUSH_TYPE=${BUILD_PUSH_TYPE}
else
- ./gradlew assemble${BUILD_VARIANT_ENV}${BUILD_VARIANT_REGION^}Release -I init.gradle -PBUILD_PUSH_TYPE=${BUILD_PUSH_TYPE}
+ ./gradlew assemble${BUILD_VARIANT_ENV}${BUILD_VARIANT_REGION^}Release -I init.gradle -PBUILD_PUSH_TYPE=${BUILD_PUSH_TYPE} --no-daemon
fi
mkdir -p release-app/${versionName}_${versionCode}
diff --git a/settings.gradle b/settings.gradle
index 303a3f1d0a..786d337940 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -15,6 +15,9 @@ gradle.ext.enablePush = gradle.startParameter.projectProperties.get('ENABLE_PUSH
gradle.ext.enableSentry = gradle.startParameter.projectProperties.get('ENABLE_SENTRY')?.toBoolean() ?: false
gradle.ext.enableVa = gradle.startParameter.projectProperties.get('ENABLE_VA')?.toBoolean() ?: false
gradle.ext.enableQuickLogin = gradle.startParameter.projectProperties.get('ENABLE_QUICK_LOGIN')?.toBoolean() ?: false
+gradle.ext.enableAccelerator = gradle.startParameter.projectProperties.get('ENABLE_ACCELERATOR')?.toBoolean() ?: false
+gradle.ext.enableWechatPay = gradle.startParameter.projectProperties.get('ENABLE_WECHAT_PAY')?.toBoolean() ?: false
+gradle.ext.enableAliPay = gradle.startParameter.projectProperties.get('ENABLE_ALI_PAY')?.toBoolean() ?: false
// 是否启用路由文档输出
gradle.ext.enableRouteDoc = gradle.startParameter.projectProperties.get('ENABLE_ROUTE_DOC')?.toBoolean() ?: false
@@ -60,9 +63,12 @@ def optionalModules = [
':feature:acloud_push',
':feature:sentry',
':feature:quick_login',
+ ':feature:accelerator',
+ ':feature:wechat_pay',
+ ':feature:ali_pay',
':module_message',
':module_sensors_data',
- ':module_va_impl',
+ ':module_va_impl'
]
// 定义 va 可选模块
@@ -146,7 +152,16 @@ if (gradle.ext.excludeOptionalModules == false) { // 默认全部模块都包含
// 包含 va 可选模块
includeVaModuleFun(optionalVaModules)
}
- if(gradle.ext.enableQuickLogin){
+ if (gradle.ext.enableQuickLogin) {
include(':feature:quick_login')
}
+ if (gradle.ext.enableAccelerator) {
+ include ':feature:accelerator'
+ }
+ if (gradle.ext.enableWechatPay) {
+ include ':feature:wechat_pay'
+ }
+ if (gradle.ext.enableAliPay) {
+ include ':feature:ali_pay'
+ }
}