Compare commits
328 Commits
fix/build_
...
feat/GHZSC
| Author | SHA1 | Date | |
|---|---|---|---|
| e855f59ae4 | |||
| 9ea4b1367e | |||
| e44c11349b | |||
| a0ea9f9f44 | |||
| 082d3297f7 | |||
| f81055ae7f | |||
| 8e3de4e441 | |||
| 7cf66c4362 | |||
| d514e4ce1b | |||
| 9d0f75a882 | |||
| 6c8c2f2340 | |||
| 8d7afea0f6 | |||
| aff6cbccdc | |||
| b19b6960ac | |||
| c60b317125 | |||
| 4fbb9d0c88 | |||
| f039d71562 | |||
| 1d74bfc7a8 | |||
| 3d118544bb | |||
| 730611362f | |||
| 9825bb7d3f | |||
| 7dfb69fd34 | |||
| 380939b8c4 | |||
| 83d43f32bf | |||
| 184e0731fb | |||
| 085356d85e | |||
| 44138225fc | |||
| 9c86504c2f | |||
| e4cf36d1b8 | |||
| 676e7a4d94 | |||
| 09e439f143 | |||
| c23a78a546 | |||
| b3ee742faf | |||
| ead61c1916 | |||
| 9bb20ca41f | |||
| 74942be890 | |||
| 391eb64df1 | |||
| 03ba6f5614 | |||
| 1552a3e95d | |||
| 9b7526773c | |||
| 764402b701 | |||
| dc7ebbc308 | |||
| 065ebe32ed | |||
| c284fcd531 | |||
| 101aa11855 | |||
| 9158d951f4 | |||
| 1c7f5c2d26 | |||
| bc630f63d4 | |||
| 1a2cfb79c2 | |||
| 7b708ebd8d | |||
| 21a4ff5560 | |||
| 034b7f07ee | |||
| d3c9cb7776 | |||
| 8593f5cd0d | |||
| 26e272edd0 | |||
| cc0683c3c7 | |||
| 7d9f36d587 | |||
| eac6bc2c7f | |||
| 1fd0d7f215 | |||
| cd5748fa1c | |||
| 3cc4a2b9bc | |||
| 55056b772e | |||
| a11acbb8c4 | |||
| 24d59aaa85 | |||
| f57a95b82c | |||
| 3a78b307d3 | |||
| c4dccb7aa7 | |||
| 7612c6afd7 | |||
| 2c781bcbf1 | |||
| 7ab09e42ca | |||
| 292c96586a | |||
| 7681b63e27 | |||
| 95966be1a1 | |||
| 9eab3361a0 | |||
| d0d5ccdc45 | |||
| c61d0bc9cc | |||
| 2da07354e7 | |||
| d874b91b7c | |||
| 62e62d3a93 | |||
| 2e806d77e1 | |||
| 30da988ebc | |||
| fd61e83f8e | |||
| 0712960a30 | |||
| 5dfdaa0353 | |||
| fa4dce66a6 | |||
| 8a017df220 | |||
| 3a0be0a6ee | |||
| d50eeddde2 | |||
| 89aa6e7abc | |||
| ab795ab538 | |||
| ba0854ad9a | |||
| fc2051387a | |||
| ab2973c7be | |||
| 2a08ea681d | |||
| e23d510fb0 | |||
| 3c6ee0c782 | |||
| 07840822a8 | |||
| 12e547d333 | |||
| 8bb3736ad1 | |||
| aa9ba5163f | |||
| 23033edc42 | |||
| 308e134aff | |||
| ac78ea0498 | |||
| 38bab9cf4f | |||
| d3351ec0b6 | |||
| e3f883b784 | |||
| fbb81d7e45 | |||
| 750ec04c9d | |||
| e23a3d3938 | |||
| e769c0e5f5 | |||
| d75020cdb5 | |||
| c56c71d1f1 | |||
| ccfa50d748 | |||
| 7176a5a4c4 | |||
| d007092193 | |||
| 2552743ff2 | |||
| 4faf15ffe7 | |||
| 33d6ed75ad | |||
| 2eb4878b12 | |||
| 2bea3c72d7 | |||
| fa3db9d521 | |||
| 8084cda37d | |||
| e624f34de1 | |||
| b832c0c14f | |||
| 6610c43937 | |||
| ec29d94fb7 | |||
| efee9409bf | |||
| 38a7eaf780 | |||
| a1b4233fdb | |||
| 60c24e7457 | |||
| b6ec74d789 | |||
| 2d7224cf16 | |||
| 3f45344b54 | |||
| 1c3dbce08d | |||
| 7844800b0e | |||
| ebcfd9c85d | |||
| 3825315f65 | |||
| 3ffe4f9bc6 | |||
| bb3a849671 | |||
| 4ed1e1e64a | |||
| 9f4eaa67fa | |||
| 729685e764 | |||
| b2fc01ae48 | |||
| 06f932af14 | |||
| c2fe3bb64a | |||
| b1b231a309 | |||
| e5161ae350 | |||
| 655173482a | |||
| 4dfa7733ad | |||
| 3906d9b84b | |||
| 775e6cc689 | |||
| 019b9f3f82 | |||
| 49f752dab5 | |||
| 0cbda119d4 | |||
| aed8e50db3 | |||
| da5bb9eaf3 | |||
| d7593de98b | |||
| 69d2d699fe | |||
| a438ef16c0 | |||
| d5a2cecd29 | |||
| 65a4e675fa | |||
| ec3e6d5f7b | |||
| 07b0afce03 | |||
| 852a6d32d6 | |||
| 55022d8b1c | |||
| eb7d407e92 | |||
| 733cd1e198 | |||
| 1d767018f6 | |||
| 0a3616a1c0 | |||
| 05108ab478 | |||
| 508c90cc63 | |||
| 6242f79f80 | |||
| 71dd964440 | |||
| 63fc720c40 | |||
| 5da02c4b64 | |||
| f94b28a085 | |||
| e8a199b5c0 | |||
| 2ba39188f7 | |||
| 963074d5d8 | |||
| cc629819d9 | |||
| d82505ab5d | |||
| a917214b36 | |||
| bdf9094799 | |||
| df0d0604b9 | |||
| f100d906ab | |||
| cc05dabb93 | |||
| bd39ac2eaf | |||
| 31903d21c0 | |||
| 266fcc4c38 | |||
| 075ed04191 | |||
| c804c2f7fd | |||
| 882f0a7b3e | |||
| 3cf86a500b | |||
| 3cd70b5b8f | |||
| 60f35a89b4 | |||
| 1503c23246 | |||
| ea9e91618f | |||
| d52fcc2185 | |||
| db047c36d7 | |||
| 1e58bec4a0 | |||
| 87ff1a64b3 | |||
| b3678d7a54 | |||
| c7fa04792b | |||
| 76ea531419 | |||
| d821da0b2c | |||
| d9b137504a | |||
| 93369f5676 | |||
| b109c3bf6f | |||
| bb80560a49 | |||
| c43a9e3b6e | |||
| 34d255d258 | |||
| 3ca17cea07 | |||
| d3e23bfbb5 | |||
| 273a9f010d | |||
| e8cbaf89ff | |||
| 0e3586a556 | |||
| d546651c06 | |||
| 815f567f44 | |||
| ba3e9357a1 | |||
| 312448daae | |||
| a00a1ced7e | |||
| 1587ca8e6a | |||
| 86b4761a1c | |||
| 0446d87b24 | |||
| 6338fb4dd0 | |||
| 6056b7ce1f | |||
| d51d3c16fe | |||
| d3858ba38f | |||
| 3cbee34b8a | |||
| ec88f52603 | |||
| b669e216c4 | |||
| 8a6d476636 | |||
| 17b34c9e6f | |||
| 795b640870 | |||
| 099d026e73 | |||
| 162381527c | |||
| dedbe1d18c | |||
| b775c2f0a9 | |||
| 7e079bc8fa | |||
| b719763f2d | |||
| fdc04bf8cb | |||
| 27811b027f | |||
| 4e8b8435b8 | |||
| a7efd9528e | |||
| 982e3a9739 | |||
| a942cdbe51 | |||
| f6f0a54cb4 | |||
| e26856220a | |||
| 0f36d7ccfa | |||
| d0c6a499f8 | |||
| c56963fab3 | |||
| 0faa0b490a | |||
| 51cc1e3104 | |||
| ae0ef717d5 | |||
| 02ed9c247a | |||
| a747917370 | |||
| 69241c489b | |||
| 66ffb5cedc | |||
| 97b3efc968 | |||
| 288f7370aa | |||
| f9db7068f2 | |||
| b4390f2811 | |||
| dbc2be5bc6 | |||
| dec2c35ff4 | |||
| 77514aa2a9 | |||
| 40cdda7bae | |||
| 4917b1d4ff | |||
| d646e971f7 | |||
| af9ba8a4d0 | |||
| 738dfd3b4d | |||
| 0f0962b261 | |||
| ffa61e8b96 | |||
| b460750f5a | |||
| 528d7511f9 | |||
| 02523b9e33 | |||
| bfc4083544 | |||
| 18e14d3811 | |||
| 89901028ea | |||
| 20c3f5cf46 | |||
| 9bc155816a | |||
| 92366c5629 | |||
| a1fd894dde | |||
| 03757b22c7 | |||
| ce10d82a8f | |||
| 74a4feee9e | |||
| 9cb0736e30 | |||
| b954df3267 | |||
| 0607a37256 | |||
| 00e4c7f1bd | |||
| 7d94250914 | |||
| 7b8634aa40 | |||
| 210e18eb34 | |||
| 957eff3c1e | |||
| 72b953b7c8 | |||
| 53fb77d2dc | |||
| f78e7a10a6 | |||
| 6aebea14bc | |||
| fd0fc31fc3 | |||
| 889558eb4b | |||
| 37ec9ae66c | |||
| f4d34a635a | |||
| 024895838c | |||
| 740bdbf79a | |||
| 73f7bee3cc | |||
| b37f247b14 | |||
| 811378f411 | |||
| f910cb67da | |||
| 4f2874877c | |||
| 15b35fd6b7 | |||
| cd64bb79a2 | |||
| d57c03b1f8 | |||
| 1895977d4c | |||
| 08571998b6 | |||
| 21cdadca13 | |||
| 54dfa46204 | |||
| c9f14641c9 | |||
| 0e95c0cbf6 | |||
| 2a93af56ee | |||
| 28e785745b | |||
| 317620daf4 | |||
| bf95a00bc6 | |||
| cb05c8a020 | |||
| d458898eb1 | |||
| f4f422b089 | |||
| e48b11f19d | |||
| b6fe834406 | |||
| 8b1f35516d | |||
| 5d4648b3d2 |
@ -157,3 +157,4 @@ oss-upload&send-email:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
|
||||
|
||||
@ -113,6 +113,7 @@ android {
|
||||
buildConfigField "String", "WECHAT_SECRET", "\"${WECHAT_SECRET}\""
|
||||
buildConfigField "String", "TENCENT_APPID", "\"${TENCENT_APPID}\""
|
||||
buildConfigField "String", "WEIBO_APPKEY", "\"${WEIBO_APPKEY}\""
|
||||
buildConfigField "String", "DSP_API_HOST","\"${DSP_API_HOST}\""
|
||||
// 一体包的32位畅玩游戏助手包名
|
||||
buildConfigField "String", "EXT_PACKAGE_NAME", "\"${rootProject.ext.EXT_PACKAGE_NAME}\""
|
||||
}
|
||||
@ -346,7 +347,7 @@ repositories {
|
||||
android.applicationVariants.configureEach { variant ->
|
||||
variant.mergeAssets.doLast {
|
||||
def assetDir = variant.mergeAssetsProvider.get().outputDir.get()
|
||||
def unwantedAssets = ['2011394667', 'gdt_plugin/gdtadv2.jar']
|
||||
def unwantedAssets = ['1832823466', 'gdt_plugin/gdtadv2.jar']
|
||||
|
||||
unwantedAssets.each { assetPath ->
|
||||
def file = new File([assetDir, assetPath].join(File.separator))
|
||||
@ -402,7 +403,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 +527,20 @@ 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"))
|
||||
}
|
||||
|
||||
implementation "me.xdrop:fuzzywuzzy:${fuzzyVersion}"
|
||||
}
|
||||
|
||||
File propFile = file('sign.properties')
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
-keep class com.gh.gamecenter.db.info.* {*;}
|
||||
-keep class com.gh.gamecenter.entity.** {<fields>;}
|
||||
-keep class com.gh.gamecenter.qa.entity.** {<fields>;}
|
||||
-keep class com.gh.gamecenter.gamedetail.entity.** {<fields>;}
|
||||
-keep class com.gh.download.DownloadDataSimpleEntity {<fields>;}
|
||||
-keep class com.gh.gamecenter.floatingwindow.FloatingWindowEntity {<fields>;}
|
||||
-keep class com.gh.gamecenter.BR
|
||||
@ -76,6 +77,7 @@
|
||||
|
||||
### TEA
|
||||
-keep class com.gh.gamecenter.TeaHelper { *; }
|
||||
-keep class com.bytedance.ads.convert.broadcast.common.EncryptionTools {*;}
|
||||
|
||||
### EasyFloat
|
||||
-keep class com.lzf.easyfloat.* {*;}
|
||||
|
||||
Binary file not shown.
BIN
app/src/gdt/libs/GDTActionSDK.min.1.9.1.aar
Normal file
BIN
app/src/gdt/libs/GDTActionSDK.min.1.9.1.aar
Normal file
Binary file not shown.
@ -3,9 +3,14 @@ package com.gh.vspace.installexternalgames
|
||||
import android.Manifest
|
||||
import android.app.Dialog
|
||||
import android.content.ComponentName
|
||||
import android.content.Intent
|
||||
import android.content.pm.PackageManager
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.os.Environment
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.gh.common.util.DialogUtils
|
||||
@ -37,6 +42,9 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
|
||||
|
||||
private var uninstallDisposable: Disposable? = null
|
||||
|
||||
|
||||
private var shouldScan = false
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
@ -74,7 +82,24 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
|
||||
adapter.notifyDataSetChanged()
|
||||
}
|
||||
|
||||
requestStoragePermission()
|
||||
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
|
||||
if (!Environment.isExternalStorageManager()) {
|
||||
shouldScan = true
|
||||
AlertDialog.Builder(requireContext()).setMessage("请在设置页面允许光环助手")
|
||||
.setNegativeButton("去") { dialog, which ->
|
||||
val intent = Intent(Settings.ACTION_MANAGE_ALL_FILES_ACCESS_PERMISSION)
|
||||
intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
startActivity(intent)
|
||||
}.show()
|
||||
} else {
|
||||
mViewModel.scanPaths()
|
||||
}
|
||||
} else {
|
||||
requestStoragePermission()
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
private fun requestStoragePermission() {
|
||||
@ -96,6 +121,12 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (shouldScan) {
|
||||
mViewModel.scanPaths()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
dialog = DialogUtils.showWaitDialog(requireContext(), "")
|
||||
@ -152,7 +183,7 @@ class InstallExternalGameFragment : ToolbarFragment(), OnItemClickListener {
|
||||
}, true)
|
||||
|
||||
VHelper.newCwValidateVspaceBeforeAction(
|
||||
requireContext(),null,
|
||||
requireContext(), null,
|
||||
) {
|
||||
dialog.show()
|
||||
}
|
||||
|
||||
@ -10,9 +10,9 @@
|
||||
<queries>
|
||||
<package android:name="com.gh.toolmap" />
|
||||
<intent>
|
||||
<action android:name="android.intent.action.VIEW"/>
|
||||
<category android:name="android.intent.category.BROWSABLE"/>
|
||||
<data android:scheme="ghtoolmap"/>
|
||||
<action android:name="android.intent.action.VIEW" />
|
||||
<category android:name="android.intent.category.BROWSABLE" />
|
||||
<data android:scheme="ghtoolmap" />
|
||||
</intent>
|
||||
</queries>
|
||||
|
||||
@ -71,6 +71,10 @@
|
||||
<!-- 适配 双开/分身 游戏授权登录 -->
|
||||
<uses-permission android:name="android.permission.INTERACT_ACROSS_USERS" />
|
||||
|
||||
<!-- 日历 -->
|
||||
<uses-permission android:name="android.permission.READ_CALENDAR" />
|
||||
<uses-permission android:name="android.permission.WRITE_CALENDAR" />
|
||||
|
||||
<uses-sdk tools:overrideLibrary="
|
||||
com.shuyu.gsyvideoplayer,
|
||||
com.shuyu.gsyvideoplayer.lib,
|
||||
@ -197,7 +201,9 @@
|
||||
android:name="io.sentry.breadcrumbs.system-events"
|
||||
android:value="false" />
|
||||
|
||||
<meta-data android:name="module_version" android:value="${VA_VERSION_NAME}" />
|
||||
<meta-data
|
||||
android:name="module_version"
|
||||
android:value="${VA_VERSION_NAME}" />
|
||||
|
||||
<service android:name="com.gh.ndownload.NDownloadService" />
|
||||
|
||||
@ -814,6 +820,10 @@
|
||||
android:name=".video.poster.PosterEditActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.halo.assistant.accelerator.MyAssetsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- <activity-->
|
||||
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
|
||||
<!-- android:launchMode="singleTask"-->
|
||||
|
||||
File diff suppressed because one or more lines are too long
@ -27,8 +27,8 @@ import com.gh.gamecenter.MainActivity
|
||||
import com.gh.gamecenter.R
|
||||
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.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
@ -178,6 +178,15 @@ object AdDelegateHelper {
|
||||
when (config.location) {
|
||||
"halo_launch" -> {
|
||||
config.ownerAd?.startAd?.let { it.id = config.ownerAd.id }
|
||||
|
||||
// HarmonyOS 2.2.0 版本不展示第三方开屏广告 (因为会引起奇怪的闪退)
|
||||
if (MetaUtil.getRom().name == "HarmonyOS"
|
||||
&& MetaUtil.getRom().versionName == "2.2.0"
|
||||
&& config.displayRule.adSource == "third_party_ads") {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
mSplashAd = config
|
||||
}
|
||||
|
||||
@ -600,32 +609,32 @@ object AdDelegateHelper {
|
||||
), null, null
|
||||
)
|
||||
|
||||
adImage.visibleIf(true)
|
||||
ImageUtils.displayWithCallback(adImage, ad.img, true, object : BaseControllerListener<ImageInfo>() {
|
||||
override fun onSubmit(id: String?, callerContext: Any?) {
|
||||
super.onSubmit(id, callerContext)
|
||||
adImage.post {
|
||||
ownerSplashAdLoadTime = System.currentTimeMillis()
|
||||
NewFlatLogUtils.logSplashAdLoad(ad.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
|
||||
isOwnerSplashAdShown = true
|
||||
adImage.post {
|
||||
NewFlatLogUtils.logSplashAdShow(ad.id, System.currentTimeMillis() - ownerSplashAdLoadTime)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(id: String?, throwable: Throwable?) {
|
||||
super.onFailure(id, throwable)
|
||||
NewFlatLogUtils.logSplashAdFail(ad.id, "启动广告图加载失败")
|
||||
}
|
||||
})
|
||||
|
||||
if (ad.isImageType) {
|
||||
adVideo.visibleIf(false)
|
||||
adImage.visibleIf(true)
|
||||
ImageUtils.displayWithCallback(adImage, ad.img, true, object : BaseControllerListener<ImageInfo>() {
|
||||
override fun onSubmit(id: String?, callerContext: Any?) {
|
||||
super.onSubmit(id, callerContext)
|
||||
adImage.post {
|
||||
ownerSplashAdLoadTime = System.currentTimeMillis()
|
||||
NewFlatLogUtils.logSplashAdLoad(ad.id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFinalImageSet(id: String?, imageInfo: ImageInfo?, animatable: Animatable?) {
|
||||
isOwnerSplashAdShown = true
|
||||
adImage.post {
|
||||
NewFlatLogUtils.logSplashAdShow(ad.id, System.currentTimeMillis() - ownerSplashAdLoadTime)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(id: String?, throwable: Throwable?) {
|
||||
super.onFailure(id, throwable)
|
||||
NewFlatLogUtils.logSplashAdFail(ad.id, "启动广告图加载失败")
|
||||
}
|
||||
})
|
||||
} else {
|
||||
adImage.visibleIf(false)
|
||||
adVideo.startPlay(ad.video.url)
|
||||
}
|
||||
startAdContainer.setOnClickListener {
|
||||
|
||||
@ -19,10 +19,10 @@ import java.net.URLConnection
|
||||
|
||||
object AdPluginDownloadHelper : InnerDownloadListener {
|
||||
|
||||
private const val CSJ_FILE_NAME = "2011394667"
|
||||
private const val CSJ_FILE_NAME = "1832823466"
|
||||
private const val GDT_FILE_NAME = "gdt_plugin/gdtadv2.jar"
|
||||
|
||||
private const val CSJ_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/2011394667"
|
||||
private const val CSJ_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/1832823466"
|
||||
private const val GDT_PLUGIN_URL = "https://and-static.ghzs66.com/android/static/gdtadv2.jar"
|
||||
|
||||
private var csjDownloadedCallback: (() -> Unit)? = null
|
||||
|
||||
@ -2,9 +2,9 @@ package com.gh.base
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.therouter.TheRouter
|
||||
import com.gh.ad.AdDelegateHelper
|
||||
import com.gh.common.util.FloatingBackViewManager
|
||||
import com.gh.common.xapk.XapkInstaller
|
||||
@ -15,27 +15,45 @@ import com.gh.gamecenter.SplashAdActivity
|
||||
import com.gh.gamecenter.SplashScreenActivity
|
||||
import com.gh.gamecenter.authorization.AuthorizationActivity
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
|
||||
import com.gh.gamecenter.common.utils.PackageFlavorHelper
|
||||
import com.gh.gamecenter.core.provider.IPushProvider
|
||||
import com.gh.gamecenter.login.utils.QuickLoginHelper
|
||||
import com.gh.gamecenter.login.view.LoginActivity
|
||||
import com.gh.gamecenter.va.VCore
|
||||
import com.gh.gamecenter.login.utils.QuickLoginHelper
|
||||
import com.gh.vspace.VHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.therouter.TheRouter
|
||||
|
||||
// TODO:移动到对应的模块
|
||||
class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
|
||||
private var isFromBackgroundToForeground = false // 是否后台回到前台
|
||||
|
||||
override fun onActivityPreCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
if (QuickLoginHelper.isLoginAuthPage(activity)) {
|
||||
try {
|
||||
val resources = activity.resources
|
||||
val config = Configuration(resources.configuration)
|
||||
config.fontScale = 1.0f
|
||||
|
||||
// 更新Resources配置
|
||||
val metrics = resources.displayMetrics
|
||||
metrics.scaledDensity = metrics.density
|
||||
resources.updateConfiguration(config, metrics)
|
||||
|
||||
} catch (e: Exception) {
|
||||
// 设置字体失败
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
override fun onActivityStarted(activity: Activity) {
|
||||
GlobalActivityManager.currentActivity = activity
|
||||
GlobalActivityManager.activityCount ++
|
||||
GlobalActivityManager.activityCount++
|
||||
if (GlobalActivityManager.activityCount == 1 && isFromBackgroundToForeground) {
|
||||
if (AdDelegateHelper.shouldShowStartUpAd(true)
|
||||
&& !HaloApp.getInstance().isDisableSplashAdTemporarily
|
||||
@ -111,7 +129,7 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
|
||||
}
|
||||
|
||||
override fun onActivityStopped(activity: Activity) {
|
||||
GlobalActivityManager.activityCount --
|
||||
GlobalActivityManager.activityCount--
|
||||
isFromBackgroundToForeground = GlobalActivityManager.activityCount <= 0
|
||||
}
|
||||
|
||||
|
||||
@ -3,6 +3,8 @@ package com.gh.common
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.util.Base64
|
||||
import android.view.View
|
||||
import android.webkit.JavascriptInterface
|
||||
@ -11,7 +13,7 @@ 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 androidx.lifecycle.Lifecycle
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.LogUtils
|
||||
@ -19,10 +21,12 @@ import com.gh.download.DownloadManager
|
||||
import com.gh.download.PackageObserver
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.ImageViewerActivity
|
||||
import com.gh.gamecenter.R
|
||||
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 +34,23 @@ 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.IAcceleratorProvider
|
||||
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 +62,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
|
||||
@ -71,10 +82,11 @@ import java.util.*
|
||||
class DefaultJsApi(
|
||||
var context: Context,
|
||||
val entrance: String = "",
|
||||
private var mFragment: Fragment? = null,
|
||||
private val mFragment: Fragment? = null,
|
||||
private var mBbsId: String? = "",
|
||||
private var mOriginUrl: String? = "",
|
||||
private val mForumName: String? = "",
|
||||
private val listener: OnWebClickListener? = null
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@ -87,6 +99,8 @@ class DefaultJsApi(
|
||||
private var mDownloadHandler: CompletionHandler<Any>? = null // 下载信息回调
|
||||
private var mExposureEvent: ExposureEvent? = null // 活动曝光实体
|
||||
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
|
||||
init {
|
||||
if (mFragment != null) {
|
||||
EventBus.getDefault().register(this)
|
||||
@ -233,6 +247,12 @@ class DefaultJsApi(
|
||||
VHelper.launch(context, packageName)
|
||||
}
|
||||
} else {
|
||||
val wechatPkgName = "com.tencent.mm"
|
||||
if (packageName == wechatPkgName && !PackageUtils.isInstalled(context, wechatPkgName)) {
|
||||
// 如果是微信客户端,需要检查是否安装微信
|
||||
ToastUtils.showToast(R.string.wechat_app_not_install_tips.toResString())
|
||||
return@runOnUiThread
|
||||
}
|
||||
PackageLauncher.launchApp(context, packageName = packageName)
|
||||
}
|
||||
}
|
||||
@ -495,6 +515,44 @@ class DefaultJsApi(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun saveWechatQRCode(msg: Any) {
|
||||
val base64StringData = msg.toString()
|
||||
runOnUiThread {
|
||||
(context as? FragmentActivity)?.checkStoragePermissionBeforeAction {
|
||||
runOnIoThread {
|
||||
val base64String = base64StringData.replace("data:image/png;base64", "")
|
||||
tryWithDefaultCatch {
|
||||
val imageFile =
|
||||
File(HaloApp.getInstance().cacheDir.absolutePath + File.separator + System.currentTimeMillis() + ".png")
|
||||
val decodedString = Base64.decode(base64String, Base64.DEFAULT)
|
||||
val bos = BufferedOutputStream(FileOutputStream(imageFile))
|
||||
bos.write(decodedString)
|
||||
bos.flush()
|
||||
bos.close()
|
||||
|
||||
ImageUtils.saveImageToFile(imageFile, "", true) {
|
||||
// 这里是 ui 线程
|
||||
// 保存微信二维码成功,1s 以后跳转微信
|
||||
if (mFragment != null && mFragment.lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
handler.postDelayed({
|
||||
val wechatPkgName = "com.tencent.mm"
|
||||
if (!PackageUtils.isInstalled(context, wechatPkgName)) {
|
||||
ToastUtils.showToast(R.string.wechat_app_not_install_tips.toResString())
|
||||
return@postDelayed
|
||||
}
|
||||
PackageLauncher.launchApp(context, packageName = wechatPkgName)
|
||||
}, 1000)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun loginWithCallback(msg: Any, handler: CompletionHandler<Any>) {
|
||||
mLoginHandler = handler
|
||||
@ -699,6 +757,86 @@ class DefaultJsApi(
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun preOrderWithAli(json: Any) {
|
||||
val order = json.toString().toObject<OrderEntity>() ?: return
|
||||
trackMemberPaymentButtonClick(order, PAYMENT_TYPE_ALIPAY)
|
||||
listener?.onPreOrderWithAli(order)
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun preOrderWithWechat(json: Any) {
|
||||
val order = json.toString().toObject<OrderEntity>() ?: 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 getDurationRemainingTime(msg: Any, handler: CompletionHandler<Any>) {
|
||||
TheRouter.get(IAcceleratorProvider::class.java)?.loadQyUserPermissionData {
|
||||
val durationExpiredMinute = AcceleratorDataHolder.instance.vipEntity?.durationExpiredTime ?: 0L
|
||||
handler.complete(durationExpiredMinute)
|
||||
}
|
||||
}
|
||||
|
||||
@JavascriptInterface
|
||||
fun refreshToken(token: Any, handler: CompletionHandler<Any>) {
|
||||
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<HashMap<String, Any>>() ?: 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,可能为空
|
||||
*/
|
||||
@ -719,6 +857,8 @@ class DefaultJsApi(
|
||||
}
|
||||
|
||||
EventBus.getDefault().unregister(this@DefaultJsApi)
|
||||
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
}
|
||||
}
|
||||
|
||||
@ -815,4 +955,15 @@ class DefaultJsApi(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnWebClickListener {
|
||||
|
||||
fun onPreOrderWithAli(order: OrderEntity)
|
||||
|
||||
fun onPreOrderWithWechat(order: OrderEntity)
|
||||
|
||||
fun onStartGameAccelerate(accInfo: AcctRecordEntity.AccInfo)
|
||||
|
||||
fun onStopGameAccelerate()
|
||||
}
|
||||
}
|
||||
|
||||
@ -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()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -66,6 +66,8 @@ public class Config {
|
||||
public static final String WGAME_CPM_BUSIAPPID = BuildConfig.WGAME_CPM_BUSIAPPID;
|
||||
public static final String WGAME_CPM_API_HOST = EnvHelper.getWGameCPMHost();
|
||||
|
||||
public static final String DSP_API_HOST = EnvHelper.getDspHost();
|
||||
|
||||
// Third-Party confs
|
||||
public static final String WECHAT_APPID = BuildConfig.WECHAT_APPID;
|
||||
public static final String WECHAT_SECRET = BuildConfig.WECHAT_SECRET;
|
||||
|
||||
@ -1,5 +1,10 @@
|
||||
package com.gh.common.databind;
|
||||
|
||||
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_SELLING_POINT;
|
||||
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_TEST;
|
||||
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_TYPE;
|
||||
import static com.gh.gamecenter.entity.SubjectEntity.SUBJECT_TAG_UPDATE;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.graphics.Color;
|
||||
@ -8,11 +13,14 @@ import android.text.SpannableString;
|
||||
import android.text.TextUtils;
|
||||
import android.text.style.ForegroundColorSpan;
|
||||
import android.view.View;
|
||||
import android.view.ViewParent;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.appcompat.app.AppCompatActivity;
|
||||
import androidx.constraintlayout.widget.ConstraintLayout;
|
||||
import androidx.constraintlayout.widget.ConstraintSet;
|
||||
|
||||
import com.gh.common.chain.BrowserInstallHandler;
|
||||
import com.gh.common.chain.CheckDownloadHandler;
|
||||
@ -48,6 +56,7 @@ import com.gh.download.server.BrowserInstallHelper;
|
||||
import com.gh.gamecenter.DownloadManagerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.WebActivity;
|
||||
import com.gh.gamecenter.common.databinding.LayoutGameItemSellingPointBinding;
|
||||
import com.gh.gamecenter.common.entity.LinkEntity;
|
||||
import com.gh.gamecenter.common.utils.DarkModeUtils;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
@ -55,6 +64,7 @@ import com.gh.gamecenter.common.utils.FileUtils;
|
||||
import com.gh.gamecenter.common.utils.NewFlatLogUtils;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.utils.MtaHelper;
|
||||
import com.gh.gamecenter.core.utils.TimeUtils;
|
||||
import com.gh.gamecenter.core.utils.ToastUtils;
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity;
|
||||
import com.gh.gamecenter.feature.entity.GameEntity;
|
||||
@ -201,7 +211,8 @@ public class BindingAdapters {
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
location + ":" + gameEntity.getName(),
|
||||
null);
|
||||
return null;
|
||||
});
|
||||
final DownloadChainHandler chainHandler = builder.buildHandlerChain();
|
||||
@ -246,7 +257,8 @@ public class BindingAdapters {
|
||||
gameEntity,
|
||||
traceEvent,
|
||||
entrance,
|
||||
location + ":" + gameEntity.getName());
|
||||
location + ":" + gameEntity.getName(),
|
||||
null);
|
||||
}
|
||||
break;
|
||||
case INSTALL_PLUGIN:
|
||||
@ -262,7 +274,9 @@ public class BindingAdapters {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameEntity.getId(),
|
||||
gameEntity.getName() != null ? gameEntity.getName() : "",
|
||||
"主动安装"
|
||||
"主动安装",
|
||||
gameEntity.isDspGame(),
|
||||
gameEntity.getDspAdId()
|
||||
);
|
||||
|
||||
DownloadEntity downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity);
|
||||
@ -287,7 +301,7 @@ public class BindingAdapters {
|
||||
});
|
||||
break;
|
||||
case RESERVED:
|
||||
ReservationHelper.showCancelReservationDialog(progressBar.getContext(),gameEntity, () -> {
|
||||
ReservationHelper.showCancelReservationDialog(progressBar.getContext(), gameEntity, () -> {
|
||||
ReservationHelper.cancelReservation(gameEntity, () -> {
|
||||
updateReservation(progressBar, gameEntity);
|
||||
});
|
||||
@ -470,39 +484,37 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
// 包含测试开服标签
|
||||
public static void setGameTags(LinearLayout layout, GameEntity gameEntity) {
|
||||
/**
|
||||
* 包含测试开服标签
|
||||
*
|
||||
* @param layout
|
||||
* @param gameEntity
|
||||
* @param subjectTag 默认为 “”,只有游戏专题可以配置subjectTag
|
||||
*/
|
||||
public static void setGameTags(LinearLayout layout, GameEntity gameEntity, String subjectTag) {
|
||||
try {
|
||||
if (layout.getVisibility() == View.GONE) return;
|
||||
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
|
||||
TestEntity test = gameEntity.getTest();
|
||||
if (test != null
|
||||
// 这个判断用于开测表列表
|
||||
&& !"type_tag".equals(test.getGameTag())) {
|
||||
if ("custom".equals(test.getGameTag())) {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
if (!TextUtils.isEmpty(test.getText())) {
|
||||
typeTag.setName(test.getText() != null ? test.getText() : "");
|
||||
} else {
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
}
|
||||
typeTag.setBackground("E8F3FF");
|
||||
typeTag.setColor("1383EB");
|
||||
tagStyle.add(typeTag);
|
||||
} else {
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
boolean isDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(layout.getContext());
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
typeTag.setBackground("1AFFA142");
|
||||
typeTag.setColor(isDarkModeOn ? "EB9238" : "FFA142");
|
||||
tagStyle.add(typeTag);
|
||||
if (test != null && subjectTag.equals(SUBJECT_TAG_TEST)) {
|
||||
// 显示开测表标签
|
||||
TagStyleEntity typeTag = new TagStyleEntity();
|
||||
boolean isDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(layout.getContext());
|
||||
typeTag.setName(test.getType() != null ? test.getType() : "");
|
||||
typeTag.setBackground("1AFFA142");
|
||||
typeTag.setColor(isDarkModeOn ? "EB9238" : "FFA142");
|
||||
tagStyle.add(typeTag);
|
||||
|
||||
TagStyleEntity timeTag = new TagStyleEntity();
|
||||
TagStyleEntity timeTag = new TagStyleEntity();
|
||||
if (test.getStartPending()) {
|
||||
timeTag.setName(test.getStartText());
|
||||
} else {
|
||||
timeTag.setName(GameViewUtils.getGameTestDate(test.getStart()));
|
||||
timeTag.setBackground("1A06CEA8");
|
||||
timeTag.setColor(isDarkModeOn ? "07A385" : "06CEA8");
|
||||
tagStyle.add(timeTag);
|
||||
}
|
||||
timeTag.setBackground("1A06CEA8");
|
||||
timeTag.setColor(isDarkModeOn ? "07A385" : "06CEA8");
|
||||
tagStyle.add(timeTag);
|
||||
|
||||
} else {
|
||||
tagStyle = gameEntity.getTagStyle();
|
||||
}
|
||||
@ -512,6 +524,68 @@ public class BindingAdapters {
|
||||
}
|
||||
}
|
||||
|
||||
public static void setGameTagsWithSellingPoint(LinearLayout layout, LayoutGameItemSellingPointBinding binding, GameEntity gameEntity, String subjectTag) {
|
||||
if (subjectTag.equals(SUBJECT_TAG_SELLING_POINT)) {
|
||||
layout.setVisibility(View.GONE);
|
||||
binding.getRoot().setVisibility(View.VISIBLE);
|
||||
GameEntity.SellingPoints sellingPoints = gameEntity.getSellingPoints();
|
||||
if (sellingPoints != null) {
|
||||
binding.tvSellingPoints.setVisibility(View.VISIBLE);
|
||||
binding.tvSellingPoints.setText(sellingPoints.getText());
|
||||
} else {
|
||||
binding.tvSellingPoints.setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
ArrayList<TagStyleEntity> tagStyle = gameEntity.getTagStyle();
|
||||
StringBuilder tagText = new StringBuilder();
|
||||
for (int i = 0; i < tagStyle.size(); i++) {
|
||||
if (i < 3) {
|
||||
tagText.append(i == 0 ? "" : "·").append(tagStyle.get(i).getName());
|
||||
}
|
||||
}
|
||||
binding.gtcvTags.setText(tagText);
|
||||
} else {
|
||||
layout.setVisibility(View.VISIBLE);
|
||||
binding.getRoot().setVisibility(View.GONE);
|
||||
switch (subjectTag) {
|
||||
case SUBJECT_TAG_UPDATE:
|
||||
List<TagStyleEntity> updateTags = new ArrayList<>();
|
||||
TagStyleEntity updateTag = new TagStyleEntity(
|
||||
"local_generated",
|
||||
TimeUtils.getFormatTime(gameEntity.getUpdateTime(), "MM-dd") + " 更新",
|
||||
"",
|
||||
"1383EB",
|
||||
"E8F3FF",
|
||||
"1383EB",
|
||||
false
|
||||
);
|
||||
updateTags.add(updateTag);
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, updateTags);
|
||||
break;
|
||||
case SUBJECT_TAG_TYPE:
|
||||
GameViewUtils.setLabelList(layout.getContext(), layout, gameEntity.getTagStyle());
|
||||
break;
|
||||
default:
|
||||
setGameTags(layout, gameEntity, subjectTag);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
ViewParent parent = binding.getRoot().getParent();
|
||||
if (parent instanceof ConstraintLayout) {
|
||||
ConstraintLayout constraintLayout = (ConstraintLayout) parent;
|
||||
ConstraintSet constraintSet = new ConstraintSet();
|
||||
constraintSet.clone(constraintLayout);
|
||||
constraintSet.clear(R.id.gameDesSpace, ConstraintSet.BOTTOM);
|
||||
if (subjectTag.equals(SUBJECT_TAG_SELLING_POINT)) {
|
||||
constraintSet.connect(R.id.gameDesSpace, ConstraintSet.BOTTOM, R.id.layout_selling_points, ConstraintSet.TOP);
|
||||
} else {
|
||||
constraintSet.connect(R.id.gameDesSpace, ConstraintSet.BOTTOM, R.id.label_list, ConstraintSet.TOP);
|
||||
}
|
||||
constraintSet.applyTo(constraintLayout);
|
||||
}
|
||||
}
|
||||
|
||||
public static void setVideoDetailGameTags(LinearLayout layout, GameEntity gameEntity) {
|
||||
try {
|
||||
ArrayList<TagStyleEntity> tagStyle = new ArrayList<>();
|
||||
|
||||
@ -0,0 +1,294 @@
|
||||
package com.gh.common.dialog
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
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.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.DialogFragmentAccelerateExporationBinding
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
public class AccelerateExpirationDialogFragment : BaseDialogFragment() {
|
||||
|
||||
private lateinit var binding: DialogFragmentAccelerateExporationBinding
|
||||
|
||||
private var _reminder: ExpirationReminder? = null
|
||||
|
||||
override fun onBack(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
_reminder = arguments?.getParcelable(KEY_EXPIRATION_REMINDER)
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogFragmentAccelerateExporationBinding.inflate(inflater, container, false)
|
||||
.also {
|
||||
binding = it
|
||||
}.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
val reminder = _reminder ?: return
|
||||
val (title, content) = when (reminder) {
|
||||
is ExpirationReminder.UserExpired -> {
|
||||
SensorsBridge.trackMonthlyPackageExpiresDialogShow(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance
|
||||
)
|
||||
getString(
|
||||
R.string.membership_expiration_reminder_title,
|
||||
"${reminder.days}"
|
||||
) to getString(R.string.membership_expiration_reminder_content)
|
||||
}
|
||||
|
||||
is ExpirationReminder.InsufficientDuration -> {
|
||||
SensorsBridge.trackInsufficientDurationDialogShow(
|
||||
reminder.gameId,
|
||||
reminder.gameName,
|
||||
reminder.pkgName,
|
||||
reminder.sourceEntrance
|
||||
)
|
||||
getString(
|
||||
R.string.duration_time_out_reminder_title,
|
||||
"${reminder.hours}"
|
||||
) to getString(R.string.duration_time_out_reminder_content)
|
||||
}
|
||||
|
||||
is ExpirationReminder.DurationExpired -> {
|
||||
SensorsBridge.trackTimedPackageExpiresDialogShow(
|
||||
reminder.gameId,
|
||||
reminder.gameName,
|
||||
reminder.pkgName,
|
||||
reminder.sourceEntrance
|
||||
)
|
||||
getString(
|
||||
R.string.duration_expiration_reminder_title,
|
||||
"${reminder.days}"
|
||||
) to getString(R.string.duration_expiration_reminder_content)
|
||||
}
|
||||
|
||||
is ExpirationReminder.TimeRunsOut -> {
|
||||
SensorsBridge.trackDurationExhaustedDialogShow()
|
||||
getString(R.string.accelerator_member_expired_title) to getString(R.string.accelerator_member_expired_content)
|
||||
}
|
||||
}
|
||||
|
||||
binding.tvTitle.text = title
|
||||
binding.tvContent.text = content
|
||||
binding.tvCancel.setOnClickListener {
|
||||
when (reminder) {
|
||||
is ExpirationReminder.UserExpired -> {
|
||||
SensorsBridge.trackMonthlyPackageExpiresDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.InsufficientDuration -> {
|
||||
SensorsBridge.trackInsufficientDurationDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.DurationExpired -> {
|
||||
SensorsBridge.trackTimedPackageExpiresDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_KNOW
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.TimeRunsOut -> {
|
||||
SensorsBridge.trackDurationExhaustedDialogClick(BUTTON_NAME_KNOW)
|
||||
}
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
binding.tvSubmit.setOnClickListener {
|
||||
when (reminder) {
|
||||
is ExpirationReminder.UserExpired -> {
|
||||
SensorsBridge.trackMonthlyPackageExpiresDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.InsufficientDuration -> {
|
||||
SensorsBridge.trackInsufficientDurationDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.DurationExpired -> {
|
||||
SensorsBridge.trackTimedPackageExpiresDialogClick(
|
||||
reminder.gameId, reminder.gameName, reminder.pkgName, reminder.sourceEntrance, BUTTON_NAME_BUY
|
||||
)
|
||||
}
|
||||
|
||||
is ExpirationReminder.TimeRunsOut -> {
|
||||
SensorsBridge.trackDurationExhaustedDialogClick(BUTTON_NAME_BUY)
|
||||
}
|
||||
}
|
||||
SensorsBridge.trackMyAssetsPageShow(
|
||||
reminder.pkgName,
|
||||
reminder.gameId,
|
||||
reminder.gameName,
|
||||
reminder.dialogName
|
||||
)
|
||||
dismissAllowingStateLoss()
|
||||
|
||||
DirectUtils.navigateToMyAssetsPage(requireContext(), reminder.sourceEntrance)
|
||||
}
|
||||
}
|
||||
|
||||
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 KEY_EXPIRATION_REMINDER = "key_expiration_reminder"
|
||||
|
||||
private const val BUTTON_NAME_KNOW = "知道了"
|
||||
private const val BUTTON_NAME_BUY = "前往购买"
|
||||
|
||||
fun checkDialogShown(context: Context, reminder: ExpirationReminder, callback: ((Boolean) -> Unit)? = null) {
|
||||
|
||||
when (reminder) {
|
||||
is ExpirationReminder.UserExpired -> {
|
||||
val days = SPUtils.getLong(Constants.SP_ACCELERATOR_USER_REMAINING_REMIND)
|
||||
if (reminder.days != days) {
|
||||
SPUtils.setLong(Constants.SP_ACCELERATOR_USER_REMAINING_REMIND, reminder.days)
|
||||
show(context, reminder)
|
||||
callback?.invoke(true)
|
||||
} else {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
|
||||
is ExpirationReminder.InsufficientDuration -> {
|
||||
val hours = SPUtils.getLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_HOURS)
|
||||
if (reminder.hours != hours) {
|
||||
SPUtils.setLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_HOURS, reminder.hours)
|
||||
show(context, reminder)
|
||||
callback?.invoke(true)
|
||||
} else {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
|
||||
is ExpirationReminder.DurationExpired -> {
|
||||
val days = SPUtils.getLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_DAYS)
|
||||
if (reminder.days != days) {
|
||||
SPUtils.setLong(Constants.SP_ACCELERATOR_DURATION_REMAINING_DAYS, reminder.days)
|
||||
show(context, reminder)
|
||||
callback?.invoke(true)
|
||||
} else {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
|
||||
is ExpirationReminder.TimeRunsOut -> {
|
||||
val hasShow = SPUtils.getBoolean(Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED)
|
||||
if (!hasShow) {
|
||||
SPUtils.setBoolean(Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED, true)
|
||||
show(context, reminder)
|
||||
callback?.invoke(true)
|
||||
} else {
|
||||
callback?.invoke(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun show(context: Context, reminder: ExpirationReminder) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = AccelerateExpirationDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(KEY_EXPIRATION_REMINDER, reminder)
|
||||
}
|
||||
}
|
||||
fragment.show(it, AcceleratorPermissionGuideDialogFragment::class.java.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sealed class ExpirationReminder(
|
||||
val gameId: String,
|
||||
val gameName: String,
|
||||
val pkgName: String,
|
||||
val sourceEntrance: String
|
||||
) :
|
||||
Parcelable {
|
||||
|
||||
abstract val dialogName: String
|
||||
|
||||
@Parcelize
|
||||
data class InsufficientDuration(
|
||||
val hours: Long,
|
||||
private val _gameId: String,
|
||||
private val _gameName: String,
|
||||
private val _pkgName: String,
|
||||
private val _sourceEntrance: String,
|
||||
) :
|
||||
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
|
||||
|
||||
override val dialogName: String
|
||||
get() = "时长不足提示弹窗"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class DurationExpired(
|
||||
val days: Long,
|
||||
private val _gameId: String,
|
||||
private val _gameName: String,
|
||||
private val _pkgName: String,
|
||||
private val _sourceEntrance: String,
|
||||
) :
|
||||
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
|
||||
|
||||
override val dialogName: String
|
||||
get() = "计时套餐临期提示弹窗"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class UserExpired(
|
||||
val days: Long,
|
||||
private val _gameId: String,
|
||||
private val _gameName: String,
|
||||
private val _pkgName: String,
|
||||
private val _sourceEntrance: String,
|
||||
) :
|
||||
ExpirationReminder(_gameId, _gameName, _pkgName, _sourceEntrance) {
|
||||
|
||||
override val dialogName: String
|
||||
get() = "包月套餐临期提示弹窗"
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class TimeRunsOut(
|
||||
private val _sourceEntrance: String,
|
||||
) : ExpirationReminder("", "", "", _sourceEntrance) {
|
||||
|
||||
override val dialogName: String
|
||||
get() = "时长耗尽提示弹窗"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,68 @@
|
||||
package com.gh.common.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.common.HaloApp
|
||||
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.DialogFragmentAcceleratorPermissionGuideBinding
|
||||
|
||||
class AcceleratorPermissionGuideDialogFragment : BaseDialogFragment() {
|
||||
|
||||
private lateinit var binding: DialogFragmentAcceleratorPermissionGuideBinding
|
||||
|
||||
private var callback: (() -> Unit)? = null
|
||||
|
||||
override fun onBack(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
|
||||
return DialogFragmentAcceleratorPermissionGuideBinding.inflate(inflater, container, false)
|
||||
.also {
|
||||
binding = it
|
||||
}.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
SPUtils.setBoolean(Constants.SP_ACCELERATOR_PERMISSION_GUIDE_SHOW, true)
|
||||
binding.tvSubmit.setOnClickListener {
|
||||
dismiss()
|
||||
callback?.invoke()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnCallback(block: () -> Unit) {
|
||||
this.callback = block
|
||||
}
|
||||
|
||||
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 {
|
||||
|
||||
fun show(context: Context, callback: () -> Unit) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = AcceleratorPermissionGuideDialogFragment()
|
||||
fragment.setOnCallback(callback)
|
||||
fragment.show(it, AcceleratorPermissionGuideDialogFragment::class.java.name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -16,6 +16,7 @@ import com.gh.common.pop.EditBindWechatPop
|
||||
import com.gh.common.pop.RealNameTipsPop
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.WechatConfigEntity
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toObject
|
||||
@ -83,79 +84,90 @@ class ReserveSuccessReminderDialog(
|
||||
handlers.clear()
|
||||
binding.flContentContainer.removeAllViews()
|
||||
|
||||
val smsConfig = reserveReminder.smsConfig
|
||||
val wechatConfig = reserveReminder.wechatConfig
|
||||
when {
|
||||
reserveReminder.onlyShowWechatReminder && !wechatConfig.isReminderEnable -> { // 只显示微信:未开启
|
||||
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
|
||||
arrayListOf(OnlyWechatReminderUnableHandler(16.dp, binding.flContentContainer, this))
|
||||
val configTypes = mutableListOf<Int>()
|
||||
// 短信
|
||||
if (reserveReminder.hasSmsConfig) {
|
||||
if (reserveReminder.smsConfig.notice) {
|
||||
configTypes.add(SMS_REMINDER_ENABLE_TYPE)
|
||||
} else {
|
||||
configTypes.add(SMS_REMINDER_UNABLE_TYPE)
|
||||
}
|
||||
|
||||
|
||||
reserveReminder.onlyShowWechatReminder && wechatConfig.isReminderEnable -> { // 只显示微信:已开
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
arrayListOf(
|
||||
WechatReminderEnableHandler(
|
||||
reserveReminder.wechatConfig.nickName,
|
||||
8.dp,
|
||||
binding.flContentContainer,
|
||||
this
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
!smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信,微信未开启
|
||||
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
|
||||
arrayListOf(
|
||||
SmsReminderUnableHandler(16.dp, binding.flContentContainer, this),
|
||||
WechatReminderUnableHandler(8.dp, binding.flContentContainer, this)
|
||||
)
|
||||
}
|
||||
|
||||
smsConfig.notice && wechatConfig.isReminderEnable -> {// 短信,微信已开启
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
arrayListOf(
|
||||
SmsReminderEnableHandler(reserveReminder.smsConfig, 8.dp, binding.flContentContainer, this),
|
||||
WechatReminderEnableHandler(
|
||||
reserveReminder.wechatConfig.nickName,
|
||||
8.dp,
|
||||
binding.flContentContainer,
|
||||
this
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
smsConfig.notice && !wechatConfig.isReminderEnable -> { // 短信开启,微信未开启
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
arrayListOf(
|
||||
SmsReminderEnableHandler(smsConfig, 8.dp, binding.flContentContainer, this),
|
||||
WechatReminderUnableHandler(16.dp, binding.flContentContainer, this)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
!smsConfig.notice && wechatConfig.isReminderEnable -> { // 微信开启,短信未开启
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
arrayListOf(
|
||||
WechatReminderEnableHandler(
|
||||
wechatConfig.nickName,
|
||||
8.dp,
|
||||
binding.flContentContainer,
|
||||
this
|
||||
),
|
||||
SmsReminderUnableHandler(16.dp, binding.flContentContainer, this)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
|
||||
arrayListOf()
|
||||
}
|
||||
}.let {
|
||||
handlers.clear()
|
||||
handlers.addAll(it)
|
||||
}
|
||||
|
||||
// 微信
|
||||
if (reserveReminder.wechatConfig.isReminderEnable) {
|
||||
configTypes.add(WECHAT_REMINDER_ENABLE_TYPE)
|
||||
} else {
|
||||
configTypes.add(WECHAT_REMINDER_UNABLE_TYPE)
|
||||
}
|
||||
|
||||
// 日历
|
||||
if (reserveReminder.hasCalendarConfig) {
|
||||
if (reserveReminder.calendarConfig.notice) {
|
||||
configTypes.add(CALENDAR_REMINDER_ENABLE_TYPE)
|
||||
} else {
|
||||
configTypes.add(CALENDAR_REMINDER_UNABLE_TYPE)
|
||||
}
|
||||
}
|
||||
|
||||
if (configTypes.size == 1) {
|
||||
// 只有微信提醒
|
||||
if (configTypes.first() == WECHAT_REMINDER_ENABLE_TYPE) {
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
handlers.add(
|
||||
WechatReminderEnableHandler(reserveReminder.wechatConfig, 8.dp, binding.flContentContainer, this)
|
||||
)
|
||||
} else {
|
||||
binding.tvContent.setText(R.string.reverse_success_without_reminder_tips)
|
||||
handlers.add(OnlyWechatReminderUnableHandler(16.dp, binding.flContentContainer, this))
|
||||
}
|
||||
} else {
|
||||
binding.tvContent.setText(R.string.reverse_success_with_reminder_tips)
|
||||
var isLargerSpacing = true
|
||||
fun getPaddingTop(): Int {
|
||||
val paddingTop = if (isLargerSpacing) 16.dp else 8.dp
|
||||
isLargerSpacing = false
|
||||
return paddingTop
|
||||
}
|
||||
configTypes.sorted().forEach {
|
||||
when (it) {
|
||||
SMS_REMINDER_ENABLE_TYPE -> {
|
||||
isLargerSpacing = true
|
||||
SmsReminderEnableHandler(reserveReminder.smsConfig, 8.dp, binding.flContentContainer, this)
|
||||
}
|
||||
|
||||
SMS_REMINDER_UNABLE_TYPE -> {
|
||||
SmsReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
|
||||
}
|
||||
|
||||
|
||||
WECHAT_REMINDER_ENABLE_TYPE -> {
|
||||
isLargerSpacing = true
|
||||
WechatReminderEnableHandler(
|
||||
reserveReminder.wechatConfig,
|
||||
8.dp,
|
||||
binding.flContentContainer,
|
||||
this
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
WECHAT_REMINDER_UNABLE_TYPE ->
|
||||
WechatReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
|
||||
|
||||
CALENDAR_REMINDER_ENABLE_TYPE -> {
|
||||
isLargerSpacing = true
|
||||
CalendarReminderEnableHandler(8.dp, binding.flContentContainer, this)
|
||||
}
|
||||
|
||||
CALENDAR_REMINDER_UNABLE_TYPE ->
|
||||
CalendarReminderUnableHandler(getPaddingTop(), binding.flContentContainer, this)
|
||||
|
||||
else -> null
|
||||
}?.let(handlers::add)
|
||||
}
|
||||
}
|
||||
|
||||
handlers.forEach {
|
||||
binding.flContentContainer.addView(it.init())
|
||||
}
|
||||
@ -220,9 +232,14 @@ class ReserveSuccessReminderDialog(
|
||||
listener.changeWechatBinding()
|
||||
}
|
||||
|
||||
override fun updateCalendarReminder() {
|
||||
listener.updateCalendarReminder()
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
window?.let {
|
||||
it.setDimAmount(0.4F)
|
||||
it.setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
val params = it.attributes
|
||||
params.width = DisplayUtils.dip2px(300F)
|
||||
@ -244,6 +261,13 @@ class ReserveSuccessReminderDialog(
|
||||
private val Int.dp: Int
|
||||
get() = DisplayUtils.dip2px(this.toFloat())
|
||||
|
||||
private const val SMS_REMINDER_ENABLE_TYPE = 1
|
||||
private const val WECHAT_REMINDER_ENABLE_TYPE = 2
|
||||
private const val CALENDAR_REMINDER_ENABLE_TYPE = 3
|
||||
private const val SMS_REMINDER_UNABLE_TYPE = 4
|
||||
private const val WECHAT_REMINDER_UNABLE_TYPE = 5
|
||||
private const val CALENDAR_REMINDER_UNABLE_TYPE = 6
|
||||
|
||||
fun create(context: Context, listener: OnReserveReminderListener) =
|
||||
ReserveSuccessReminderDialog(
|
||||
context,
|
||||
@ -360,7 +384,7 @@ class ReserveSuccessReminderDialog(
|
||||
}
|
||||
|
||||
class WechatReminderEnableHandler(
|
||||
private val nickName: String,
|
||||
private val wechatConfig: WechatConfigEntity,
|
||||
topMargin: Int,
|
||||
parent: ViewGroup,
|
||||
listener: OnReserveSuccessListener
|
||||
@ -380,7 +404,7 @@ class ReserveSuccessReminderDialog(
|
||||
}.root
|
||||
|
||||
override fun initView() {
|
||||
binding.tvWechat.text = nickName
|
||||
binding.tvWechat.text = wechatConfig.nickName
|
||||
binding.vModifyWechat.setOnClickListener {
|
||||
editWechatPop.showAsDropDown(
|
||||
binding.ivModifyWechat,
|
||||
@ -411,6 +435,51 @@ class ReserveSuccessReminderDialog(
|
||||
|
||||
}
|
||||
|
||||
class CalendarReminderUnableHandler(
|
||||
topMargin: Int,
|
||||
parent: ViewGroup,
|
||||
listener: OnReserveSuccessListener
|
||||
) : ReminderContentHandler(topMargin, parent, listener) {
|
||||
|
||||
private lateinit var binding: LayoutReserveWechatReminderUnableBinding
|
||||
|
||||
// 复用微信提醒未开启状态布局
|
||||
override fun createView(inflater: LayoutInflater) =
|
||||
LayoutReserveWechatReminderUnableBinding.inflate(inflater, parent, false)
|
||||
.also {
|
||||
binding = it
|
||||
}.root
|
||||
|
||||
override fun initView() {
|
||||
binding.tvWechatReminderTitle.setText(R.string.calendar_reminders)
|
||||
binding.tvWechatReminderDescription.setText(R.string.calendar_reminders_description)
|
||||
binding.vWechatAdd.setOnClickListener {
|
||||
listener.updateCalendarReminder()
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CalendarReminderEnableHandler(
|
||||
topMargin: Int,
|
||||
parent: ViewGroup,
|
||||
listener: OnReserveSuccessListener
|
||||
) : ReminderContentHandler(topMargin, parent, listener) {
|
||||
|
||||
private lateinit var binding: LayoutReserveCalendarReminderUnableBinding
|
||||
|
||||
override fun createView(inflater: LayoutInflater) =
|
||||
LayoutReserveCalendarReminderUnableBinding.inflate(inflater, parent, false)
|
||||
.also {
|
||||
binding = it
|
||||
}.root
|
||||
|
||||
override fun initView() {
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -425,4 +494,6 @@ interface OnReserveSuccessListener {
|
||||
fun verifyPhoneNumber()
|
||||
|
||||
fun changeWechatBinding()
|
||||
|
||||
fun updateCalendarReminder()
|
||||
}
|
||||
@ -19,7 +19,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
ExposureThrottleBus(
|
||||
{ commitExposure(it) },
|
||||
Consumer(Throwable::printStackTrace),
|
||||
{ commitWXCPMExposure(it) }
|
||||
{ commitExternalExposure(it) }
|
||||
)
|
||||
}
|
||||
var layoutManager: LayoutManager? = null
|
||||
@ -31,7 +31,7 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
if (fragment == f) {
|
||||
visibleState?.let { commitExposure(it) }
|
||||
visibleState?.let { commitWXCPMExposure(it) }
|
||||
visibleState?.let { commitExternalExposure(it) }
|
||||
throttleBus.clear()
|
||||
}
|
||||
}
|
||||
@ -97,23 +97,33 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
}
|
||||
|
||||
/**
|
||||
* 微信小游戏CPM曝光事件接口上报,由于普通曝光事件的上报通道存在节流特性,不符合CPM接口对于数据上报的实时性和准确性的要求,所以使用额外的通道进行上报
|
||||
* 提交第三方的曝光事件 (微信小游戏CPM 和 DSP 游戏)
|
||||
* 由于普通曝光事件的上报通道存在节流特性,不符合CPM接口对于数据上报的实时性和准确性的要求,所以使用额外的通道进行上报
|
||||
*/
|
||||
private fun commitWXCPMExposure(visibleState: ExposureThrottleBus.VisibleState) {
|
||||
private fun commitExternalExposure(visibleState: ExposureThrottleBus.VisibleState) {
|
||||
|
||||
val eventList = arrayListOf<ExposureEvent>()
|
||||
val cpmEventList = arrayListOf<ExposureEvent>()
|
||||
val dspEventList = arrayListOf<ExposureEvent>()
|
||||
|
||||
for (pos in visibleState.firstVisiblePosition..visibleState.lastVisiblePosition) {
|
||||
try {
|
||||
exposable.getEventByPosition(pos)?.let {
|
||||
if (it.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
|
||||
eventList.add(it)
|
||||
when (it.payload.miniGameType) {
|
||||
Constants.WECHAT_MINI_GAME_CPM -> cpmEventList.add(it)
|
||||
Constants.DSP_GAME -> dspEventList.add(it)
|
||||
else -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
exposable.getEventListByPosition(pos)?.let { list ->
|
||||
list.forEach {
|
||||
if (it.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
|
||||
eventList.add(it)
|
||||
when (it.payload.miniGameType) {
|
||||
Constants.WECHAT_MINI_GAME_CPM -> cpmEventList.add(it)
|
||||
Constants.DSP_GAME -> dspEventList.add(it)
|
||||
else -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -121,7 +131,8 @@ class ExposureListener(var fragment: Fragment, var exposable: IExposable) : Recy
|
||||
// Just ignore the error.
|
||||
}
|
||||
}
|
||||
ExposureManager.logCPM(eventList)
|
||||
ExposureManager.logExternal(cpmEventList, dspEventList)
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
@ -6,7 +6,9 @@ import com.gh.gamecenter.common.loghub.TLogHubHelper
|
||||
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
|
||||
import com.gh.gamecenter.common.utils.toJson
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.dsp.DspReportHelper
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.feature.minigame.wechat.WGameSubjectCPMListReportHelper
|
||||
import com.lightgame.utils.Utils
|
||||
import com.volcengine.model.tls.LogItem
|
||||
@ -36,6 +38,10 @@ object ExposureManager {
|
||||
AppExecutor.logExecutor.execute {
|
||||
if (event.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM) {
|
||||
WGameSubjectCPMListReportHelper.reportExposure(event)
|
||||
} else if (event.payload.miniGameType == Constants.DSP_GAME) {
|
||||
if (event.event == ExposureType.DOWNLOAD_COMPLETE) {
|
||||
DspReportHelper.report(event.payload.downloadUrl)
|
||||
}
|
||||
}
|
||||
if (!exposureCache.contains(event.id)) {
|
||||
exposureSet.add(event)
|
||||
@ -64,12 +70,18 @@ object ExposureManager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Log a wechat mini game cpm collection of exposure event.
|
||||
* Log a collection of exposure event for external use.
|
||||
*/
|
||||
fun logCPM(eventList: List<ExposureEvent>) {
|
||||
fun logExternal(cpmEventList: List<ExposureEvent>, dspEventList: ArrayList<ExposureEvent>) {
|
||||
AppExecutor.logExecutor.execute {
|
||||
if (eventList.isNotEmpty()) {
|
||||
WGameSubjectCPMListReportHelper.reportExposure(eventList.toSet())
|
||||
if (cpmEventList.isNotEmpty()) {
|
||||
WGameSubjectCPMListReportHelper.reportExposure(cpmEventList.toSet())
|
||||
}
|
||||
|
||||
if (dspEventList.isNotEmpty()) {
|
||||
for (event in dspEventList) {
|
||||
DspReportHelper.report(event.payload.showUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -89,6 +89,7 @@ object ExposureUtils {
|
||||
exposureEvent.payload.path = path
|
||||
exposureEvent.payload.speed = speed
|
||||
exposureEvent.payload.redirectedUrlList = redirectedUrlList
|
||||
exposureEvent.payload.downloadUrl = gameEntity.downloadUrl
|
||||
|
||||
ExposureManager.log(exposureEvent)
|
||||
ExposureManager.commitSavedExposureEvents(forcedUpload = true)
|
||||
|
||||
@ -4,6 +4,7 @@ import android.annotation.SuppressLint
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.PackageFlavorHelper
|
||||
import com.gh.gamecenter.common.utils.debounceActionWithInterval
|
||||
import com.gh.gamecenter.common.utils.toJson
|
||||
import com.gh.gamecenter.common.utils.toObject
|
||||
@ -123,10 +124,16 @@ object RegionSettingHelper {
|
||||
mIsInit = false
|
||||
}
|
||||
|
||||
val fakeIp = if (PackageFlavorHelper.IS_TEST_FLAVOR) {
|
||||
SPUtils.getString(Constants.SP_TEST_FLAVOR_IP)
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
// 使用默认的 Schdulers.io() 可能会触发 OOM
|
||||
RetrofitManager.getInstance()
|
||||
.api
|
||||
.getRegionSetting(HaloApp.getInstance().channel)
|
||||
.getRegionSetting(HaloApp.getInstance().channel, fakeIp)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<RegionSetting>() {
|
||||
override fun onSuccess(data: RegionSetting) {
|
||||
|
||||
@ -7,19 +7,20 @@ import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.HistoryGameDetailEntity
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.feature.entity.NewsEntity
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.feature.entity.NewsEntity
|
||||
import com.gh.gamecenter.feature.room.converter.*
|
||||
import com.gh.gamecenter.room.converter.*
|
||||
import com.gh.gamecenter.room.dao.*
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
@Database(
|
||||
entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class, GamesCollectionEntity::class],
|
||||
version = 14,
|
||||
entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class, GamesCollectionEntity::class, HistoryGameDetailEntity::class],
|
||||
version = 15,
|
||||
exportSchema = false
|
||||
)
|
||||
@TypeConverters(
|
||||
@ -53,6 +54,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
abstract fun gameDao(): GameDao
|
||||
abstract fun videoHistoryDao(): VideoHistoryDao
|
||||
abstract fun gamesCollectionDao(): GamesCollectionDao
|
||||
abstract fun gameDetailDao(): GameDetailHistoryDao
|
||||
|
||||
companion object {
|
||||
|
||||
@ -152,6 +154,12 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_14_15: Migration = object : Migration(14, 15) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE HistoryGameDetailEntity (id TEXT NOT NULL PRIMARY KEY, name TEXT DEFAULT '')")
|
||||
}
|
||||
}
|
||||
|
||||
val instance by lazy {
|
||||
Room.databaseBuilder(
|
||||
HaloApp.getInstance().application,
|
||||
@ -170,6 +178,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_11_12)
|
||||
.addMigrations(MIGRATION_12_13)
|
||||
.addMigrations(MIGRATION_13_14)
|
||||
.addMigrations(MIGRATION_14_15)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,6 +44,20 @@ object HistoryHelper {
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gameDao().addGame(historyGameEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun insertGameDetail(gameEntity: GameEntity) {
|
||||
val historyGameDetailEntity = HistoryGameDetailEntity(gameEntity.id, gameEntity.name)
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gameDetailDao().addGame(historyGameDetailEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getHistoryGameDetailById(id: String): HistoryGameDetailEntity? =
|
||||
try {
|
||||
HistoryDatabase.instance.gameDetailDao().getHistoryGameDetailById(id)
|
||||
} catch (e: Throwable) {
|
||||
null
|
||||
}
|
||||
|
||||
private fun convertGameUpdateEntityToHistoryGameEntity(updateEntity: GameUpdateEntity): HistoryGameEntity {
|
||||
val historyGame = HistoryGameEntity()
|
||||
|
||||
@ -142,6 +156,15 @@ object HistoryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun deleteGameDetailEntity(gameId: String) {
|
||||
runOnIoThread {
|
||||
tryCatchInRelease {
|
||||
HistoryDatabase.instance.gameDetailDao().deleteGame(HistoryGameDetailEntity(id = gameId))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@JvmStatic
|
||||
fun emptyDatabase() {
|
||||
|
||||
@ -0,0 +1,29 @@
|
||||
package com.gh.common.interceptor
|
||||
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.therouter.router.RouteItem
|
||||
import com.therouter.router.interceptor.RouterReplaceInterceptor
|
||||
import com.therouter.router.matchRouteMap
|
||||
|
||||
/**
|
||||
* 主拦截器
|
||||
*/
|
||||
class MainInterceptor: RouterReplaceInterceptor() {
|
||||
|
||||
override fun replace(routeItem: RouteItem?): RouteItem? {
|
||||
if (routeItem == null) return null
|
||||
|
||||
// 用户是否已经同意隐私政策
|
||||
val isUserAcceptPrivacyPolicy = HaloApp.isUserAcceptPrivacyPolicy(HaloApp.getInstance())
|
||||
|
||||
// 如果用户已经同意隐私政策并且应用已经启动,直接返回 routeItem ,运行跳转
|
||||
if (isUserAcceptPrivacyPolicy && HaloApp.getInstance().isAlreadyUpAndRunning) {
|
||||
return routeItem
|
||||
}
|
||||
|
||||
// 指向调整为 SplashScreenActivity
|
||||
return matchRouteMap(RouteConsts.activity.splashActivity)
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,7 +59,7 @@ class CustomFloatingWindowHandler(priority: Int) : PriorityChainHandler(priority
|
||||
override fun onProcess(): Boolean {
|
||||
when (getStatus()) {
|
||||
STATUS_VALID -> {
|
||||
_showFloatingAction.value = Event(data)
|
||||
_showFloatingAction.postValue(Event(data))
|
||||
return true
|
||||
// floatingWindowProvider.showFloatingWindowOnly(
|
||||
// mFragment!!,
|
||||
|
||||
@ -64,7 +64,8 @@ object GlobalPriorityChainHelper : ISuperiorChain {
|
||||
* 预启动所有的优先级弹窗管理链
|
||||
*/
|
||||
fun preStart(withSpecialDelay: Boolean) {
|
||||
val launchRedirectHandler = LaunchRedirectHandler(-101)
|
||||
val launchRedirectHandler = LaunchRedirectHandler(-102)
|
||||
val membershipExpiredHandler = MembershipExpiredHandler(-101)
|
||||
val updateDialogHandler = UpdateDialogHandler(-100)
|
||||
val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99)
|
||||
val notificationPermissionDialogHandler = NotificationPermissionDialogHandler(0)
|
||||
@ -74,6 +75,7 @@ object GlobalPriorityChainHelper : ISuperiorChain {
|
||||
val resumeDownloadHandler = ResumeDownloadHudHandler(4)
|
||||
|
||||
mainChain.addHandler(launchRedirectHandler)
|
||||
mainChain.addHandler(membershipExpiredHandler)
|
||||
mainChain.addHandler(updateDialogHandler)
|
||||
mainChain.addHandler(privacyPolicyDialogHandler)
|
||||
mainChain.addHandler(welcomeDialogHandler)
|
||||
@ -83,6 +85,7 @@ object GlobalPriorityChainHelper : ISuperiorChain {
|
||||
mainChain.addHandler(resumeDownloadHandler)
|
||||
|
||||
launchRedirectHandler.doPreProcess()
|
||||
membershipExpiredHandler.doPreProcess()
|
||||
updateDialogHandler.doPreProcess()
|
||||
|
||||
// 首次启动延迟 300ms,保证请求首次启动时已经获取到了 GID 、 OAID 等标记
|
||||
|
||||
@ -0,0 +1,80 @@
|
||||
package com.gh.common.prioritychain
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.gh.common.dialog.AccelerateExpirationDialogFragment
|
||||
import com.gh.gamecenter.common.constant.Constants.SP_ACCELERATOR_MEMBERSHIP_EXPIRED
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.login.user.UserRepository
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
|
||||
class MembershipExpiredHandler(priority: Int) : PriorityChainHandler(priority) {
|
||||
|
||||
fun doPreProcess() {
|
||||
UserRepository.getInstance().setAutoLoginListener {
|
||||
val hasShow = SPUtils.getBoolean(SP_ACCELERATOR_MEMBERSHIP_EXPIRED)
|
||||
val vipEntity = AcceleratorDataHolder.instance.vipEntity
|
||||
if (getStatus() == STATUS_PENDING) {
|
||||
if (vipEntity == null ||
|
||||
vipEntity.vipStatus ||
|
||||
vipEntity.isNewUser ||
|
||||
vipEntity.isVipRefundUser ||
|
||||
vipEntity.isDurationRefundUser ||
|
||||
hasShow
|
||||
) {
|
||||
processNext()
|
||||
} else {
|
||||
updateStatus(STATUS_VALID)
|
||||
process()
|
||||
}
|
||||
} else {
|
||||
if (vipEntity == null ||
|
||||
vipEntity.vipStatus ||
|
||||
vipEntity.isNewUser ||
|
||||
vipEntity.isVipRefundUser ||
|
||||
vipEntity.isDurationRefundUser ||
|
||||
hasShow
|
||||
) {
|
||||
updateStatus(STATUS_INVALID)
|
||||
} else {
|
||||
updateStatus(STATUS_VALID)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProcess(): Boolean {
|
||||
val currentActivity = CurrentActivityHolder.getCurrentActivity()
|
||||
if (GlobalPriorityChainHelper.isThisActivityValid(currentActivity)) {
|
||||
when (getStatus()) {
|
||||
STATUS_VALID -> {
|
||||
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut("")
|
||||
SPUtils.setBoolean(SP_ACCELERATOR_MEMBERSHIP_EXPIRED, true)
|
||||
val dialogFragment = AccelerateExpirationDialogFragment().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(
|
||||
AccelerateExpirationDialogFragment.KEY_EXPIRATION_REMINDER, reminder
|
||||
)
|
||||
}
|
||||
}
|
||||
dialogFragment.dialog?.setOnDismissListener {
|
||||
processNext()
|
||||
}
|
||||
dialogFragment.show(
|
||||
(currentActivity as FragmentActivity).supportFragmentManager,
|
||||
AccelerateExpirationDialogFragment::class.java.name
|
||||
)
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
STATUS_INVALID -> {
|
||||
processNext()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -4,8 +4,10 @@ import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.gh.common.databind.BindingAdapters
|
||||
import com.gh.gamecenter.common.databinding.LayoutGameItemSellingPointBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
|
||||
import com.gh.gamecenter.home.custom.adapter.CustomViewExt
|
||||
|
||||
@com.therouter.inject.ServiceProvider
|
||||
class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
|
||||
@ -18,7 +20,7 @@ class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
|
||||
}
|
||||
|
||||
override fun setGameTags(layout: LinearLayout, gameEntity: GameEntity) {
|
||||
BindingAdapters.setGameTags(layout, gameEntity)
|
||||
BindingAdapters.setGameTags(layout, gameEntity, "")
|
||||
}
|
||||
|
||||
override fun setMessageUnread(view: TextView, unreadCount: Int) {
|
||||
@ -28,4 +30,17 @@ class BindingAdaptersProviderImpl : IBindingAdaptersProvider {
|
||||
override fun setGame(view: View, gameEntity: GameEntity) {
|
||||
BindingAdapters.setGame(view, gameEntity)
|
||||
}
|
||||
|
||||
override fun setGameTagsWithSellingPoints(
|
||||
layout: LinearLayout,
|
||||
sellingPointsBinding: LayoutGameItemSellingPointBinding,
|
||||
gameEntity: GameEntity,
|
||||
subjectTag: String
|
||||
) {
|
||||
BindingAdapters.setGameTagsWithSellingPoint(layout, sellingPointsBinding, gameEntity, subjectTag)
|
||||
}
|
||||
|
||||
override fun setGameDescription(tvDesc: TextView, briefStyle: String, game: GameEntity) {
|
||||
CustomViewExt.setDescription(tvDesc, briefStyle, game)
|
||||
}
|
||||
}
|
||||
@ -31,4 +31,6 @@ class BuildConfigImpl : IBuildConfigProvider {
|
||||
override fun getWGameCPMBusiAppId(): String = BuildConfig.WGAME_CPM_BUSIAPPID
|
||||
|
||||
override fun getLogProducerProject(): String = BuildConfig.LOG_HUB_PROJECT
|
||||
|
||||
override fun getDspApiHost(): String = BuildConfig.DSP_API_HOST
|
||||
}
|
||||
@ -2,6 +2,8 @@ package com.gh.common.provider
|
||||
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity
|
||||
@ -11,6 +13,7 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.provider.IDownloadButtonClickedProvider
|
||||
import com.gh.gamecenter.feature.view.DownloadButton
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
@ -27,6 +30,9 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
|
||||
var packageName = ""
|
||||
var exposureSourceList: List<ExposureSource>? = null
|
||||
var customPageTrackData: CustomPageTrackData? = null
|
||||
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
|
||||
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
|
||||
val isFromPush = pushMessageId.isNotEmpty()
|
||||
|
||||
val boundedObject = downloadButton.getObject()
|
||||
|
||||
@ -113,8 +119,10 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
|
||||
}
|
||||
|
||||
// 小游戏的启动不需要上报下载点击事件
|
||||
// @see https://jira.shanqu.cc/browse/GHZSCY-7013
|
||||
if (boundedObject is GameEntity && boundedObject.isMiniGame()) {
|
||||
// @see https://jira.shanqu.cc/browse/GHZSCY-7013 & https://jira.shanqu.cc/browse/GHZSCY-7918
|
||||
if (boundedObject is GameEntity
|
||||
&& (boundedObject.isMiniGame() || boundedObject.isDspGame)
|
||||
) {
|
||||
return
|
||||
}
|
||||
|
||||
@ -136,6 +144,9 @@ class DownloadButtonClickedProviderImpl : IDownloadButtonClickedProvider {
|
||||
"last_page_name", GlobalActivityManager.getLastPageEntity().pageName,
|
||||
"last_page_id", GlobalActivityManager.getLastPageEntity().pageId,
|
||||
"last_page_business_id", GlobalActivityManager.getLastPageEntity().pageBusinessId,
|
||||
"is_from_push_notifications", isFromPush,
|
||||
"message_id", pushMessageId,
|
||||
"link_id", pushLinkId,
|
||||
*customPageKV
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,59 @@
|
||||
package com.gh.common.provider
|
||||
|
||||
import com.gh.common.util.PackageUtils
|
||||
import android.annotation.SuppressLint
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.core.provider.IAcceleratorDataHolderProvider
|
||||
import com.gh.gamecenter.feature.entity.BaseEntity
|
||||
import com.gh.gamecenter.feature.entity.VipEntity
|
||||
import com.gh.gamecenter.login.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
|
||||
@com.therouter.inject.ServiceProvider
|
||||
class IAcceleratorDataHolderProviderImpl : IAcceleratorDataHolderProvider {
|
||||
|
||||
override fun isPaidVip(): Boolean =
|
||||
AcceleratorDataHolder.instance.isPaidVip
|
||||
|
||||
override fun getGhVersionName(): String {
|
||||
return PackageUtils.getGhVersionName()
|
||||
}
|
||||
|
||||
/**
|
||||
* 请注意,由于光环后端无法获取 奇游时长套餐 加速时长,这里的 vipStatus不准,
|
||||
* 这里只做参考,具体的状态需要奇游sdk提供,如果奇游sdk返回失败,则以这条接口结果为准
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
override fun loadVipEntityFromGh(userId: String, callBack: (Any?) -> Unit) {
|
||||
RetrofitManager.getInstance().newApi.getVipStatus(
|
||||
userId.ifBlank { UserManager.getInstance().userId },
|
||||
"gjonline_vip",
|
||||
true
|
||||
)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<BaseEntity<VipEntity>>() {
|
||||
override fun onSuccess(data: BaseEntity<VipEntity>) {
|
||||
callBack(data.data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
callBack(null)
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
override fun updateVipEntity(data: Any) {
|
||||
if (data is VipEntity) {
|
||||
AcceleratorDataHolder.instance.setVipEntity(data)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun clear() {
|
||||
AcceleratorDataHolder.instance.clear()
|
||||
}
|
||||
}
|
||||
@ -7,9 +7,6 @@ import com.gh.gamecenter.feature.provider.IRegionSettingHelperProvider
|
||||
|
||||
@com.therouter.inject.ServiceProvider
|
||||
class RegionSettingHelperProviderImpl : IRegionSettingHelperProvider {
|
||||
override fun shouldThisGameDisplayMirrorInfo(gameId: String): Boolean {
|
||||
return RegionSettingHelper.shouldThisGameDisplayMirrorInfo(gameId)
|
||||
}
|
||||
|
||||
override fun getMirrorPosition(gameId: String): Int {
|
||||
return RegionSettingHelper.getMirrorPosition(gameId)
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
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.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.handleUserRechargeSuccess(orderEntity)
|
||||
|
||||
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
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -2,6 +2,7 @@ package com.gh.common.util
|
||||
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
@ -12,15 +13,18 @@ import okhttp3.ResponseBody
|
||||
object ActivationHelper {
|
||||
|
||||
private const val HAS_SENT_ACTIVATED_INFO = "has_sent_activated_info"
|
||||
private const val SENT_ACTIVATED_INFO_TIME = "sent_activated_info_time"
|
||||
private const val HAS_SENT_RETENTION_INFO = "has_sent_retention_info"
|
||||
|
||||
var mHasSentActivatedInfo = SPUtils.getBoolean(HAS_SENT_ACTIVATED_INFO, false)
|
||||
private var hasSentActivatedInfo = SPUtils.getBoolean(HAS_SENT_ACTIVATED_INFO, false)
|
||||
private var hasSentRetentionInfo = SPUtils.getBoolean(HAS_SENT_RETENTION_INFO, false)
|
||||
|
||||
/**
|
||||
* 发送激活信息 (用于推广)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun sendActivationInfo() {
|
||||
if (!mHasSentActivatedInfo) {
|
||||
if (!hasSentActivatedInfo) {
|
||||
RetrofitManager.getInstance()
|
||||
.api.postActivationInfo()
|
||||
.subscribeOn(Schedulers.io())
|
||||
@ -28,8 +32,36 @@ object ActivationHelper {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
|
||||
mHasSentActivatedInfo = true
|
||||
hasSentActivatedInfo = true
|
||||
SPUtils.setBoolean(HAS_SENT_ACTIVATED_INFO, true)
|
||||
SPUtils.setLong(SENT_ACTIVATED_INFO_TIME, System.currentTimeMillis())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 发送次日留存信息 (用于推广)
|
||||
*/
|
||||
@JvmStatic
|
||||
fun sendRetentionInfo() {
|
||||
if (hasSentActivatedInfo && !hasSentRetentionInfo) {
|
||||
val activateTimeMillis = SPUtils.getLong(SENT_ACTIVATED_INFO_TIME, 0)
|
||||
val currentTimeMillis = System.currentTimeMillis()
|
||||
|
||||
if (!TimeUtils.isNextDay(activateTimeMillis, currentTimeMillis)) {
|
||||
return
|
||||
}
|
||||
|
||||
RetrofitManager.getInstance()
|
||||
.api.postRetentionInfo()
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
|
||||
hasSentRetentionInfo = true
|
||||
SPUtils.setBoolean(HAS_SENT_RETENTION_INFO, true)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -13,7 +13,6 @@ import com.therouter.TheRouter;
|
||||
import com.gh.ad.AdDelegateHelper;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.constant.RouteConsts;
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
@ -112,7 +111,6 @@ public class DataUtils {
|
||||
try {
|
||||
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/device"), values);
|
||||
} catch (Exception exception) {
|
||||
SentryHelper.INSTANCE.onEvent("DEVICE_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
});
|
||||
@ -150,6 +148,7 @@ public class DataUtils {
|
||||
/**
|
||||
* 获取应用 gid 绑定的实名信息
|
||||
*/
|
||||
// TODO 这个方法启动时会被调用多次,后面考虑优化优化
|
||||
@SuppressLint("CheckResult")
|
||||
public static void getDeviceCertification(String gid) {
|
||||
RetrofitManager.getInstance()
|
||||
@ -196,7 +195,6 @@ public class DataUtils {
|
||||
// TODO 将 com.gh.gamecenter 改成 BuildConfig.ApplicationID
|
||||
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/certification"), values);
|
||||
} catch (Exception exception) {
|
||||
SentryHelper.INSTANCE.onEvent("CERTIFICATION_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
|
||||
exception.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,8 @@ public class DetailDownloadUtils {
|
||||
|
||||
/**
|
||||
* 更新底部下载区域
|
||||
* @param viewHolder 下载区域的包裹
|
||||
*
|
||||
* @param viewHolder 下载区域的包裹
|
||||
* @param ignoreDownloadEntity 忽略下载实体(往往用于下载异常时)
|
||||
*/
|
||||
public static void updateViewHolder(DetailViewHolder viewHolder, boolean ignoreDownloadEntity) {
|
||||
@ -53,6 +54,10 @@ public class DetailDownloadUtils {
|
||||
viewHolder.getMultiVersionDownloadTv().setVisibility(View.GONE);
|
||||
}
|
||||
|
||||
viewHolder.setSpeedViewsVisible(false);
|
||||
// 默认为显示状态
|
||||
viewHolder.getDownloadPb().setVisibility(View.VISIBLE);
|
||||
|
||||
// 根据预置的配置更新 ViewHolder 的状态 (譬如青少年模式、下载内容为空等)
|
||||
if (updateViewHolderWithPredefinedConfig(viewHolder, gameEntity)) {
|
||||
return;
|
||||
@ -97,9 +102,9 @@ public class DetailDownloadUtils {
|
||||
viewHolder.getLocalDownloadContainer().setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
// 畅玩未安装,且当前下载的默认类型为"普通下载",且用户未安装时,禁用双按钮,禁用畅玩
|
||||
if (!VHelper.isInstalled(gameEntity.getUniquePackageName())
|
||||
&& !PackagesManager.isInstalled(gameEntity.getUniquePackageName())
|
||||
// 1.畅玩未安装/当前游戏配置了加速,且当前下载的默认类型为"普通下载",且用户未安装时,禁用双按钮,禁用畅玩
|
||||
if ((!VHelper.isInstalled(gameEntity.getUniquePackageName())
|
||||
&& !PackagesManager.isInstalled(gameEntity.getUniquePackageName()) || gameEntity.getCanSpeed())
|
||||
&& downloadEntity != null
|
||||
&& ExtensionsKt.isLocalDownloadInDualDownloadMode(downloadEntity)
|
||||
) {
|
||||
@ -107,8 +112,22 @@ public class DetailDownloadUtils {
|
||||
if (viewHolder.getOverlayTv() != null) {
|
||||
viewHolder.getOverlayTv().setVisibility(View.GONE);
|
||||
}
|
||||
// 此时需要将 本地占位按钮样式改成单条样式
|
||||
if (viewHolder.getLocalDownloadContainer() != null) {
|
||||
viewHolder.getLocalDownloadContainer().setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_fill_gradient_blue);
|
||||
}
|
||||
if (viewHolder.getLocalDownloadTitleTv() != null) {
|
||||
viewHolder.getLocalDownloadTitleTv().setTextColor(ExtensionsKt.toColor(com.gh.gamecenter.common.R.color.text_aw_primary, viewHolder.getContext()));
|
||||
}
|
||||
} else {
|
||||
viewHolder.getDownloadPb().setVisibility(View.VISIBLE);
|
||||
// 将占位按钮样式恢复成左半边样式
|
||||
if (viewHolder.getLocalDownloadContainer() != null) {
|
||||
viewHolder.getLocalDownloadContainer().setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue);
|
||||
}
|
||||
if (viewHolder.getLocalDownloadTitleTv() != null) {
|
||||
viewHolder.getLocalDownloadTitleTv().setTextColor(ExtensionsKt.toColor(com.gh.gamecenter.common.R.color.text_theme, viewHolder.getContext()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -135,6 +154,11 @@ public class DetailDownloadUtils {
|
||||
showDualDownloadButton,
|
||||
downloadEntity
|
||||
);
|
||||
if (downloadEntity != null && downloadEntity.getStatus() != DownloadStatus.done) {
|
||||
// 如果存在未完成的任务,则不显示加速按钮
|
||||
} else {
|
||||
viewHolder.checkIfShowSpeedUi(showVGame, showDualDownloadButton);
|
||||
}
|
||||
} else {
|
||||
// 游戏包含多 APK 的情况
|
||||
viewHolder.getMultiVersionDownloadTv().setText("选择下载你的版本" + (TextUtils.isEmpty(downloadAddWord) ? "" : "-" + downloadAddWord));
|
||||
@ -219,7 +243,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)
|
||||
@ -261,7 +285,7 @@ public class DetailDownloadUtils {
|
||||
viewHolder.getLocalDownloadSizeTv().setVisibility(View.GONE);
|
||||
String size = viewHolder.getGameEntity().getApk().isEmpty() ? "" : viewHolder.getGameEntity().getApk().get(0).getSize();
|
||||
if (size != null) {
|
||||
String sizeWithoutDigit = size.replaceAll("(?<=\\d)\\.[0-9]+(?!\\d)", "");
|
||||
String sizeWithoutDigit = convertSizeString(size);
|
||||
viewHolder.getLocalDownloadSizeTv().setText(sizeWithoutDigit);
|
||||
}
|
||||
|
||||
@ -601,6 +625,30 @@ public class DetailDownloadUtils {
|
||||
return (int) Math.ceil(downloadEntity.getPercent());
|
||||
}
|
||||
|
||||
public static String convertSizeString(String sizeString) {
|
||||
String numberPart;
|
||||
String indicator;
|
||||
|
||||
// Check if the string ends with "MB"
|
||||
if (sizeString.endsWith("MB")) {
|
||||
numberPart = sizeString.substring(0, sizeString.length() - 2);
|
||||
indicator = "MB";
|
||||
} else if (sizeString.endsWith("G")) {
|
||||
numberPart = sizeString.substring(0, sizeString.length() - 1);
|
||||
indicator = "G";
|
||||
} else {
|
||||
return sizeString;
|
||||
}
|
||||
|
||||
// Round number
|
||||
double number = Double.parseDouble(numberPart);
|
||||
long roundedNumber = Math.round(number);
|
||||
|
||||
// Combine rounded number and size indicator
|
||||
return roundedNumber + indicator;
|
||||
}
|
||||
|
||||
|
||||
private static boolean handleDownloadButtonAsXapk(DownloadEntity downloadEntity, DownloadButton downloadButton) {
|
||||
String xapkStatus = downloadEntity.getMeta().get(XapkInstaller.XAPK_UNZIP_STATUS);
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import com.gh.common.exposure.ExposureTraceUtils.appendTrace
|
||||
import com.gh.common.util.EntranceUtils.jumpActivity
|
||||
import com.gh.common.util.EntranceUtils.jumpActivityCompat
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.ShellActivity.Type
|
||||
import com.gh.gamecenter.amway.AmwayActivity
|
||||
import com.gh.gamecenter.category2.CategoryV2Activity
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
@ -55,6 +56,7 @@ import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
import com.gh.gamecenter.gamecollection.hotlist.GameCollectionHotListActivity
|
||||
import com.gh.gamecenter.gamecollection.hotlist.GameCollectionListDetailActivity
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity
|
||||
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
|
||||
import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity
|
||||
import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersSubscribedGameListActivity
|
||||
import com.gh.gamecenter.gamedetail.history.HistoryApkListActivity
|
||||
@ -193,6 +195,8 @@ object DirectUtils {
|
||||
"simulator",
|
||||
"teen_mode",
|
||||
"message_center",
|
||||
"archive",
|
||||
"my_assets",
|
||||
)
|
||||
|
||||
fun directToLinkPage(
|
||||
@ -613,6 +617,10 @@ object DirectUtils {
|
||||
|
||||
"message_center" -> directToMessageCenter(0, entrance)
|
||||
|
||||
"archive" -> directToCloudArchive(context, linkEntity.link ?: "", linkEntity.text ?: "", "", entrance)
|
||||
|
||||
"my_assets" -> navigateToMyAssetsPage(context, entrance)
|
||||
|
||||
"" -> {
|
||||
// do nothing
|
||||
}
|
||||
@ -846,33 +854,42 @@ object DirectUtils {
|
||||
entrance: String? = null,
|
||||
autoDownload: Boolean? = null,
|
||||
tab: String? = "",
|
||||
traceEvent: ExposureEvent? = null
|
||||
traceEvent: ExposureEvent? = null,
|
||||
from: String? = null
|
||||
) {
|
||||
if (id.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_TO, GameDetailActivity::class.java.simpleName)
|
||||
bundle.putString(KEY_GAMEID, id)
|
||||
if (!TextUtils.isEmpty(tab)) {
|
||||
when (tab) {
|
||||
"comment" -> bundle.putString(KEY_TARGET, EntranceConsts.TAB_TYPE_RATING)
|
||||
"desc" -> bundle.putString(KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
"forum" -> bundle.putString(KEY_TARGET, EntranceConsts.TAB_TYPE_BBS)
|
||||
"zone" -> bundle.putString(KEY_TARGET, EntranceConsts.TAB_TYPE_TRENDS)
|
||||
|
||||
val uri = Uri.Builder()
|
||||
.path(RouteConsts.activity.gameDetailActivity)
|
||||
.appendQueryParameter(KEY_ENTRANCE, entrance)
|
||||
.appendQueryParameter(KEY_GAME_ID, id)
|
||||
.appendQueryParameter(KEY_FROM, from)
|
||||
.build()
|
||||
|
||||
TheRouter
|
||||
.build(uri.toString())
|
||||
.fillParams { bundle ->
|
||||
if (!TextUtils.isEmpty(tab)) {
|
||||
when (tab) {
|
||||
"comment" -> bundle.putString(KEY_TARGET, GameDetailTabEntity.TYPE_COMMENT)
|
||||
"desc" -> bundle.putString(KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
"forum" -> bundle.putString(KEY_TARGET, GameDetailTabEntity.TYPE_BBS)
|
||||
"zone" -> bundle.putString(KEY_TARGET, GameDetailTabEntity.TYPE_ZONE)
|
||||
}
|
||||
}
|
||||
if (traceEvent != null) {
|
||||
val clickEvent = createEvent(
|
||||
GameEntity(id = id, name = name),
|
||||
traceEvent.source,
|
||||
appendTrace(traceEvent),
|
||||
ExposureType.CLICK
|
||||
)
|
||||
log(clickEvent)
|
||||
bundle.putParcelable(KEY_TRACE_EVENT, clickEvent)
|
||||
}
|
||||
bundle.putBoolean(KEY_AUTO_DOWNLOAD, autoDownload ?: false)
|
||||
}
|
||||
}
|
||||
if (traceEvent != null) {
|
||||
val clickEvent = createEvent(
|
||||
GameEntity(id = id, name = name),
|
||||
traceEvent.source,
|
||||
appendTrace(traceEvent),
|
||||
ExposureType.CLICK
|
||||
)
|
||||
log(clickEvent)
|
||||
bundle.putParcelable(KEY_TRACE_EVENT, clickEvent)
|
||||
}
|
||||
bundle.putBoolean(KEY_AUTO_DOWNLOAD, autoDownload ?: false)
|
||||
jumpActivity(context, bundle)
|
||||
.navigation(context)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -903,7 +920,7 @@ object DirectUtils {
|
||||
bundle.putString(KEY_ENTRANCE, entrance)
|
||||
bundle.putString(KEY_GAMEID, id)
|
||||
bundle.putBoolean(KEY_OPEN_VIDEO_STREAMING, true)
|
||||
bundle.putString(KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
bundle.putString(KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
@ -911,15 +928,21 @@ object DirectUtils {
|
||||
fun directToGameDetail(
|
||||
context: Context,
|
||||
id: String,
|
||||
defaultTab: String = EntranceConsts.TAB_TYPE_DESC,
|
||||
defaultTab: String = GameDetailTabEntity.TYPE_DETAIL,
|
||||
entrance: String? = null
|
||||
) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, GameDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, entrance)
|
||||
bundle.putString(KEY_GAMEID, id)
|
||||
bundle.putString(KEY_TARGET, defaultTab)
|
||||
jumpActivity(context, bundle)
|
||||
val uri = Uri.Builder()
|
||||
.path(RouteConsts.activity.gameDetailActivity)
|
||||
.appendQueryParameter(KEY_ENTRANCE, entrance)
|
||||
.appendQueryParameter(KEY_GAME_ID, id)
|
||||
.build()
|
||||
|
||||
TheRouter
|
||||
.build(uri.toString())
|
||||
.fillParams { bundle ->
|
||||
bundle.putString(KEY_TARGET, defaultTab)
|
||||
}
|
||||
.navigation(context)
|
||||
}
|
||||
|
||||
// 专栏
|
||||
@ -1107,9 +1130,15 @@ object DirectUtils {
|
||||
|
||||
@JvmStatic
|
||||
fun directToWebView(context: Context, url: String, entrance: String? = null) {
|
||||
directToWebView(context, url, entrance, null)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToWebView(context: Context, url: String, entrance: String? = null, title: String? = null) {
|
||||
if (url.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(EntranceConsts.KEY_GAMENAME, title)
|
||||
if (url.contains("android_page_type=singleton")) {
|
||||
bundle.putString(KEY_TO, SingletonWebActivity::class.java.simpleName)
|
||||
} else {
|
||||
@ -1505,8 +1534,8 @@ object DirectUtils {
|
||||
if (categoryId.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, CategoryV2Activity::class.java.name)
|
||||
bundle.putString(KEY_CATEGORY_ID, categoryId)
|
||||
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
|
||||
bundle.putString(KEY_PAGE_ID, categoryId)
|
||||
bundle.putString(KEY_PAGE_NAME, categoryTitle)
|
||||
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
if (exposureEvent != null) bundle.putParcelableArrayList(
|
||||
KEY_EXPOSURE_SOURCE_LIST,
|
||||
@ -1569,7 +1598,7 @@ object DirectUtils {
|
||||
response?.apply {
|
||||
if (zone.status == "on") {
|
||||
if (zone.style == "link") {
|
||||
directToGameDetail(context, gameId, EntranceConsts.TAB_TYPE_TRENDS, entrance)
|
||||
directToGameDetail(context, gameId, GameDetailTabEntity.TYPE_ZONE, entrance)
|
||||
} else {
|
||||
directToWebView(context, url, entrance)
|
||||
}
|
||||
@ -1682,7 +1711,7 @@ object DirectUtils {
|
||||
fun directToHelpAndFeedback(context: Context, bundle: Bundle? = null) {
|
||||
TheRouter.build(RouteConsts.activity.helpAndFeedbackActivity)
|
||||
.fillParams {
|
||||
it.putAll(bundle)
|
||||
bundle?.run { it.putAll(this) }
|
||||
}
|
||||
.navigation(context)
|
||||
}
|
||||
@ -2294,4 +2323,32 @@ object DirectUtils {
|
||||
.withBoolean(KEY_DISPLAY_TYPE, isLogoutStyle)
|
||||
.navigation()
|
||||
}
|
||||
|
||||
// 跳转云存档详情页
|
||||
@JvmStatic
|
||||
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)
|
||||
bundle.putString(KEY_ARCHIVE_CONFIG_URL, configUrl)
|
||||
bundle.putString(KEY_ENTRANCE, entrance)
|
||||
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)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
@ -2,10 +2,9 @@ package com.gh.common.util
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.DialogHelper
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.common.utils.replaceLineBreakWithBr
|
||||
import com.gh.gamecenter.core.utils.EmptyCallback
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
@ -32,7 +31,7 @@ object DownloadDialogHelper {
|
||||
DialogHelper.showDialogWithHtmlContent(
|
||||
context,
|
||||
dialog.title,
|
||||
dialog.content,
|
||||
dialog.content.replaceLineBreakWithBr(),
|
||||
"继续下载",
|
||||
"取消",
|
||||
confirmClickCallback = {
|
||||
@ -59,7 +58,8 @@ object DownloadDialogHelper {
|
||||
gameName = gameEntity.name ?: "",
|
||||
gameType = gameEntity.categoryChinese
|
||||
)
|
||||
}
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true)
|
||||
)
|
||||
} else {
|
||||
callback.onCallback()
|
||||
|
||||
@ -61,7 +61,7 @@ object DownloadItemUtils {
|
||||
gameEntity: GameEntity,
|
||||
downloadEntity: DownloadEntity,
|
||||
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
|
||||
index: Int
|
||||
index: Int,
|
||||
) {
|
||||
if (gameEntity.id != downloadEntity.gameId) {
|
||||
adapter?.notifyItemChanged(index)
|
||||
@ -139,7 +139,7 @@ object DownloadItemUtils {
|
||||
context: Context,
|
||||
gameEntity: GameEntity,
|
||||
holder: GameViewHolder,
|
||||
hideDownloadBtnIfNoAvailableContent: Boolean
|
||||
hideDownloadBtnIfNoAvailableContent: Boolean,
|
||||
) {
|
||||
updateItem(
|
||||
context = context,
|
||||
@ -155,7 +155,7 @@ object DownloadItemUtils {
|
||||
context: Context,
|
||||
gameEntity: GameEntity,
|
||||
holder: GameViewHolder,
|
||||
briefStyle: String?
|
||||
briefStyle: String?,
|
||||
) {
|
||||
updateItem(
|
||||
context = context,
|
||||
@ -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 =
|
||||
@ -422,25 +452,28 @@ object DownloadItemUtils {
|
||||
DownloadStatus.subscribe,
|
||||
DownloadStatus.diskisfull,
|
||||
DownloadStatus.diskioerror,
|
||||
DownloadStatus.overflow -> {
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
||||
@ -450,7 +483,7 @@ object DownloadItemUtils {
|
||||
holder: GameViewHolder,
|
||||
gameEntity: GameEntity,
|
||||
briefStyle: String?,
|
||||
isShowRecommendStar: Boolean = false
|
||||
isShowRecommendStar: Boolean = false,
|
||||
) {
|
||||
updateItemViewStatus(holder, briefStyle, gameEntity.columnRecommend, isShowRecommendStar)
|
||||
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
|
||||
@ -475,7 +508,7 @@ object DownloadItemUtils {
|
||||
holder: GameViewHolder,
|
||||
gameEntity: GameEntity,
|
||||
briefStyle: String?,
|
||||
isShowRecommendStar: Boolean = false
|
||||
isShowRecommendStar: Boolean = false,
|
||||
) {
|
||||
val entryMap = gameEntity.getEntryMap()
|
||||
var downloadEntity: DownloadEntity? = null
|
||||
@ -503,23 +536,30 @@ object DownloadItemUtils {
|
||||
context: Context,
|
||||
holder: GameViewHolder,
|
||||
downloadEntity: DownloadEntity,
|
||||
isMultiVersion: Boolean = false
|
||||
isMultiVersion: Boolean = false,
|
||||
) {
|
||||
when (downloadEntity.status) {
|
||||
DownloadStatus.redirected,
|
||||
DownloadStatus.downloading -> {
|
||||
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
|
||||
holder.gameDownloadBtn.buttonStyle =
|
||||
DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
|
||||
holder.gameDownloadBtn.progress = (downloadEntity.percent * 10).toInt()
|
||||
holder.gameDownloadBtn.text = downloadEntity.percent.toString() + "%"
|
||||
}
|
||||
@ -540,13 +580,15 @@ object DownloadItemUtils {
|
||||
DownloadStatus.diskioerror,
|
||||
DownloadStatus.diskisfull,
|
||||
DownloadStatus.subscribe,
|
||||
DownloadStatus.overflow -> {
|
||||
DownloadStatus.overflow,
|
||||
-> {
|
||||
if (isMultiVersion) {
|
||||
holder.gameDownloadTips?.visibility = View.VISIBLE
|
||||
holder.gameDownloadTips?.setDownloadTipsAnimation(false)
|
||||
}
|
||||
holder.gameDownloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
|
||||
holder.gameDownloadBtn.text = context.getString(com.gh.gamecenter.feature.R.string.resume)
|
||||
holder.gameDownloadBtn.text =
|
||||
context.getString(com.gh.gamecenter.feature.R.string.resume)
|
||||
}
|
||||
|
||||
DownloadStatus.done -> {
|
||||
@ -579,7 +621,7 @@ object DownloadItemUtils {
|
||||
holder: GameViewHolder,
|
||||
briefStyle: String?,
|
||||
recommendStyle: LinkEntity?,
|
||||
isShowRecommendStar: Boolean = false
|
||||
isShowRecommendStar: Boolean = false,
|
||||
) {
|
||||
holder.gameDownloadTips?.visibility = View.GONE
|
||||
// 推荐指数优先,现暂时为游戏单详情列表游戏使用
|
||||
@ -618,7 +660,11 @@ object DownloadItemUtils {
|
||||
}
|
||||
|
||||
// 缺省情况下回落到游戏简介
|
||||
if (TextUtils.isEmpty(briefStyle) || briefStyle!!.contains("brief") || briefStyle.contains("recommend")) {
|
||||
if (TextUtils.isEmpty(briefStyle)
|
||||
|| briefStyle!!.contains("brief")
|
||||
|| briefStyle.contains("recommend")
|
||||
|| briefStyle.contains("test&appointment")
|
||||
) {
|
||||
holder.gameDes?.visibility = View.VISIBLE
|
||||
} else {
|
||||
holder.gameDes?.visibility = View.GONE
|
||||
@ -634,7 +680,7 @@ object DownloadItemUtils {
|
||||
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
|
||||
entrance: String,
|
||||
location: String,
|
||||
sourceEntrance: String = "其他"
|
||||
sourceEntrance: String = "其他",
|
||||
) {
|
||||
setOnClickListener(
|
||||
context,
|
||||
@ -723,7 +769,7 @@ object DownloadItemUtils {
|
||||
traceEvent: ExposureEvent?,
|
||||
clickCallback: EmptyCallback?,
|
||||
refreshCallback: EmptyCallback?,
|
||||
allStateClickCallback: EmptyCallback?
|
||||
allStateClickCallback: EmptyCallback?,
|
||||
) {
|
||||
// 为 downloadButton 添加游戏实体,供点击的时候上报用
|
||||
downloadBtn.putObject(gameEntity)
|
||||
@ -959,9 +1005,10 @@ object DownloadItemUtils {
|
||||
entrance: String,
|
||||
location: String,
|
||||
traceEvent: ExposureEvent? = null,
|
||||
refreshCallback: EmptyCallback? = 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 +1027,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 +1055,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 +1083,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)
|
||||
@ -1040,15 +1114,17 @@ object DownloadItemUtils {
|
||||
val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(apk.url)
|
||||
|
||||
com.gh.gamecenter.common.utils.NewFlatLogUtils.logGameInstall(
|
||||
gameId = downloadEntity?.gameId ?: "",
|
||||
gameName = downloadEntity?.name ?: "",
|
||||
gameId = gameEntity.id,
|
||||
gameName = gameEntity.name ?: "",
|
||||
trigger = "主动安装"
|
||||
)
|
||||
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameId = downloadEntity?.gameId ?: "",
|
||||
gameName = downloadEntity?.name ?: "",
|
||||
action = "主动安装"
|
||||
gameId = gameEntity.id,
|
||||
gameName = gameEntity.name ?: "",
|
||||
action = "主动安装",
|
||||
isDspGame = gameEntity.isDspGame,
|
||||
dspAdId = gameEntity.dspAdId
|
||||
)
|
||||
|
||||
if (gameEntity.simulator != null) {
|
||||
@ -1174,7 +1250,7 @@ object DownloadItemUtils {
|
||||
location: String,
|
||||
asVGame: Boolean,
|
||||
isSubscribe: Boolean,
|
||||
traceEvent: ExposureEvent?
|
||||
traceEvent: ExposureEvent?,
|
||||
) {
|
||||
if (gameEntity.getApk().isEmpty()) return
|
||||
|
||||
@ -1218,7 +1294,7 @@ object DownloadItemUtils {
|
||||
entrance: String,
|
||||
location: String,
|
||||
isSubscribe: Boolean,
|
||||
traceEvent: ExposureEvent?
|
||||
traceEvent: ExposureEvent?,
|
||||
) {
|
||||
val msg = FileUtils.isCanDownload(context, gameEntity.getApk().firstOrNull()?.size ?: "")
|
||||
if (TextUtils.isEmpty(msg)) {
|
||||
@ -1245,7 +1321,7 @@ object DownloadItemUtils {
|
||||
gameEntity: GameEntity,
|
||||
position: Int,
|
||||
adapter: RecyclerView.Adapter<out RecyclerView.ViewHolder?>?,
|
||||
refreshCallback: EmptyCallback?
|
||||
refreshCallback: EmptyCallback?,
|
||||
) {
|
||||
val apkEntity = gameEntity.getApk().firstOrNull()
|
||||
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
|
||||
@ -1288,7 +1364,7 @@ object DownloadItemUtils {
|
||||
location: String,
|
||||
asVGame: Boolean,
|
||||
isSubscribe: Boolean,
|
||||
traceEvent: ExposureEvent?
|
||||
traceEvent: ExposureEvent?,
|
||||
) {
|
||||
|
||||
// 执行更新操作前,先清理历史下载任务,避免冲突
|
||||
|
||||
@ -14,15 +14,16 @@ import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.callback.ConfirmListener
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.entity.SimpleGameEntity
|
||||
import com.gh.gamecenter.common.entity.SuggestType
|
||||
import com.gh.gamecenter.common.eventbus.EBShowDialog
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.NewFlatLogUtils
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.dsp.DspReportHelper
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.feature.entity.CustomPageTrackData
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
@ -281,11 +282,10 @@ object DownloadObserver {
|
||||
|
||||
private fun performDownloadCompleteAction(
|
||||
downloadEntity: DownloadEntity,
|
||||
downloadManager: DownloadManager
|
||||
downloadManager: DownloadManager,
|
||||
) {
|
||||
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
|
||||
statDoneEvent(downloadEntity)
|
||||
MtaHelper.onEvent("软件更新", "下载完成")
|
||||
// 会有 ActivityNotFoundException 异常,catch 掉不管了
|
||||
tryWithDefaultCatch {
|
||||
if (Constants.SILENT_UPDATE != downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)) {
|
||||
@ -298,7 +298,9 @@ object DownloadObserver {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameName = downloadEntity.name,
|
||||
gameId = downloadEntity.gameId,
|
||||
action = "自动安装"
|
||||
action = "自动安装",
|
||||
isDspGame = downloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
|
||||
dspAdId = downloadEntity.getMetaExtra(Constants.DSP_AD_ID)
|
||||
)
|
||||
|
||||
// TODO 在 Android 11 上没有授权安装未知应用的权限前第一次调用这个方法系统会杀掉我们的进程...
|
||||
@ -395,7 +397,9 @@ object DownloadObserver {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name,
|
||||
action = "自动安装"
|
||||
action = "自动安装",
|
||||
isDspGame = downloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
|
||||
dspAdId = downloadEntity.getMetaExtra(Constants.DSP_AD_ID)
|
||||
)
|
||||
PackageInstaller.install(mApplication, downloadEntity, false, ignoreAsVGame = false)
|
||||
}
|
||||
@ -504,7 +508,8 @@ object DownloadObserver {
|
||||
gameVersion = downloadEntity.versionName ?: "",
|
||||
isPlatformRecommend = isPlatformRecommend,
|
||||
adIconActive = downloadEntity.meta[Constants.AD_ICON_ACTIVE].toBoolean(),
|
||||
isAdData = downloadEntity.meta[Constants.IS_AD_DATA].toBoolean()
|
||||
isAdData = downloadEntity.meta[Constants.IS_AD_DATA].toBoolean(),
|
||||
downloadUrl = downloadEntity.meta[Constants.DOWNLOAD_URL]
|
||||
),
|
||||
downloadEntity.platform,
|
||||
downloadEntity.exposureTrace,
|
||||
@ -556,24 +561,81 @@ object DownloadObserver {
|
||||
} else {
|
||||
arrayOf()
|
||||
}
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DownloadProcessFinish",
|
||||
exposureEvent?.source,
|
||||
"game_id", downloadEntity.gameId,
|
||||
"game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "",
|
||||
"game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
|
||||
"game_label", downloadEntity.tags.joinToString(","),
|
||||
"game_schema_type", if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
"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,
|
||||
"download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
|
||||
"download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
*kvs
|
||||
)
|
||||
|
||||
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
|
||||
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
|
||||
val isFromPush = pushMessageId.isNotEmpty()
|
||||
|
||||
if (downloadEntity.getMetaExtra(Constants.DSP_GAME) != "true") {
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DownloadProcessFinish",
|
||||
exposureEvent?.source,
|
||||
"game_id",
|
||||
downloadEntity.gameId,
|
||||
"game_name",
|
||||
downloadEntity.meta[Constants.GAME_NAME] ?: "",
|
||||
"game_type",
|
||||
downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
|
||||
"game_label",
|
||||
downloadEntity.tags.joinToString(","),
|
||||
"game_schema_type",
|
||||
if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
"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,
|
||||
"download_status",
|
||||
downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
|
||||
"download_type",
|
||||
if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
"is_from_push_notifications",
|
||||
isFromPush,
|
||||
"message_id",
|
||||
pushMessageId,
|
||||
"link_id",
|
||||
pushLinkId,
|
||||
*kvs
|
||||
)
|
||||
} else {
|
||||
val searchContent = downloadEntity.getMetaExtra(Constants.SEARCH_KEY)
|
||||
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DspAdDownloadFinish",
|
||||
exposureEvent?.source,
|
||||
"ad_id", downloadEntity.getMetaExtra(Constants.DSP_AD_ID),
|
||||
"search_content", searchContent,
|
||||
"game_column_name", downloadEntity.getMetaExtra(Constants.SUBJECT_NAME),
|
||||
"game_column_id", downloadEntity.getMetaExtra(Constants.SUBJECT_ID),
|
||||
"game_id", downloadEntity.gameId,
|
||||
"game_name", downloadEntity.meta[Constants.GAME_NAME] ?: "",
|
||||
"game_type", downloadEntity.meta[Constants.GAME_CATEGORY_IN_CHINESE] ?: "",
|
||||
"game_label", downloadEntity.tags.joinToString(","),
|
||||
"game_schema_type", if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
"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,
|
||||
"download_status", downloadEntity.meta[Constants.DOWNLOAD_STATUS_IN_CHINESE] ?: "",
|
||||
"download_type", if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
"is_from_push_notifications", isFromPush,
|
||||
"message_id", pushMessageId,
|
||||
"link_id", pushLinkId,
|
||||
"location", if (searchContent.isEmpty()) "自定义页面" else "游戏搜索结果列表",
|
||||
*kvs
|
||||
)
|
||||
|
||||
DspReportHelper.report(downloadEntity.getMetaExtra(Constants.DOWNLOAD_URL))
|
||||
}
|
||||
}
|
||||
|
||||
DataCollectionUtils.uploadDownload(mApplication, downloadEntity, "完成")
|
||||
|
||||
@ -25,7 +25,10 @@ object GameUtils {
|
||||
/**
|
||||
* 去除与重复sourceList相同的数据
|
||||
*/
|
||||
fun removeDuplicateData(sourceList: MutableList<GameEntity>?, rawList: MutableList<GameEntity>?): MutableList<GameEntity>? {
|
||||
fun removeDuplicateData(
|
||||
sourceList: MutableList<GameEntity>?,
|
||||
rawList: MutableList<GameEntity>?
|
||||
): MutableList<GameEntity>? {
|
||||
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 ""
|
||||
}
|
||||
@ -104,7 +111,12 @@ object GameUtils {
|
||||
var gh_id: Any?
|
||||
apkFor@ for (apkEntity in gameEntity.getApk()) {
|
||||
|
||||
val isInstalledLocally = PackagesManager.isInstalled(apkEntity.packageName)
|
||||
val isInstalledLocally =
|
||||
if (gameEntity.isDspGame) {
|
||||
PackageHelper.validLocalPackageNameSet.contains(apkEntity.packageName)
|
||||
} else {
|
||||
PackagesManager.isInstalled(apkEntity.packageName)
|
||||
}
|
||||
|
||||
// filter by packageName
|
||||
val settings = Config.getSettings()
|
||||
@ -138,7 +150,8 @@ object GameUtils {
|
||||
} else if (!isFromList) {
|
||||
if (!performAsVGame
|
||||
&& gameEntity.isDualBtnModeEnabled()
|
||||
&& downloadEntity?.isVGameDownloadInDualDownloadMode() == true) {
|
||||
&& downloadEntity?.isVGameDownloadInDualDownloadMode() == true
|
||||
) {
|
||||
// 下载的任务是由畅玩触发的,并且双下载按钮启用,游戏详情页不需判定为需要安装
|
||||
downloadEntity = null
|
||||
} else if (performAsVGame && downloadEntity?.isLocalDownloadInDualDownloadMode() == true) {
|
||||
|
||||
@ -14,25 +14,26 @@ import androidx.core.content.ContextCompat;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.LibaoDetailAdapter;
|
||||
import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.entity.NotificationUgc;
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.common.retrofit.JSONObjectResponse;
|
||||
import com.gh.gamecenter.common.retrofit.Response;
|
||||
import com.gh.gamecenter.common.utils.DialogHelper;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.NotificationHelper;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.utils.ToastUtils;
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils;
|
||||
import com.gh.gamecenter.eventbus.EBUISwitch;
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity;
|
||||
import com.gh.gamecenter.feature.entity.LibaoEntity;
|
||||
import com.gh.gamecenter.feature.entity.LibaoStatusEntity;
|
||||
import com.gh.gamecenter.feature.entity.MeEntity;
|
||||
import com.gh.gamecenter.common.entity.NotificationUgc;
|
||||
import com.gh.gamecenter.feature.entity.UserDataLibaoEntity;
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse;
|
||||
import com.gh.gamecenter.eventbus.EBUISwitch;
|
||||
import com.gh.gamecenter.feature.utils.PlatformUtils;
|
||||
import com.gh.gamecenter.geetest.GeetestUtils;
|
||||
import com.gh.gamecenter.login.user.UserManager;
|
||||
import com.gh.gamecenter.common.retrofit.JSONObjectResponse;
|
||||
import com.gh.gamecenter.common.retrofit.Response;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
import com.halo.assistant.HaloApp;
|
||||
import com.lightgame.utils.Utils;
|
||||
@ -323,7 +324,19 @@ public class LibaoUtils {
|
||||
libaoBtn.setText(com.gh.gamecenter.feature.R.string.libao_finish);
|
||||
libaoBtn.setBackgroundResource(R.drawable.button_border_round_gray);
|
||||
libaoBtn.setTextColor(context.getResources().getColor(com.gh.gamecenter.common.R.color.button_gray));
|
||||
libaoBtn.setOnClickListener(v -> ToastUtils.toast("兑换码领取已结束"));
|
||||
libaoBtn.setOnClickListener(v ->
|
||||
{
|
||||
ToastUtils.toast("兑换码领取已结束");
|
||||
SensorsBridge.trackEvent("GameGiftDraw",
|
||||
"gift_type", "兑换码",
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance,
|
||||
"button_name", libaoBtn.getText().toString()
|
||||
);
|
||||
});
|
||||
} else {
|
||||
libaoBtn.setText(R.string.libao_copy);
|
||||
libaoBtn.setTextColor(ExtensionsKt.toColor(com.gh.gamecenter.common.R.color.white, context));
|
||||
@ -337,6 +350,23 @@ public class LibaoUtils {
|
||||
libaoEntity.getGame().getId(),
|
||||
libaoEntity.getGame().getName()
|
||||
);
|
||||
SensorsBridge.trackEvent("GameGiftDraw",
|
||||
"gift_type", "兑换码",
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance,
|
||||
"button_name", libaoBtn.getText().toString()
|
||||
);
|
||||
SensorsBridge.trackEvent("GameGiftDrawResult",
|
||||
"gift_type", "兑换码",
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance
|
||||
);
|
||||
ExtensionsKt.copyTextAndToast(libaoEntity.getCode(), libaoEntity.getToast());
|
||||
});
|
||||
}
|
||||
@ -345,6 +375,25 @@ public class LibaoUtils {
|
||||
|
||||
libaoBtn.setOnClickListener(v -> {
|
||||
String btnStatus = libaoBtn.getText().toString();
|
||||
String giftType;
|
||||
if ("ling".equals(libaoEntity.getStatus()) || "linged".equals(libaoEntity.getStatus())) {
|
||||
giftType = "普通礼包";
|
||||
} else if ("copy".equals(libaoEntity.getReceiveMethod())) {
|
||||
giftType = "兑换码";
|
||||
} else {
|
||||
giftType = "淘号礼包";
|
||||
}
|
||||
|
||||
SensorsBridge.trackEvent("GameGiftDraw",
|
||||
"gift_type", giftType,
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance,
|
||||
"button_name", btnStatus
|
||||
);
|
||||
|
||||
// 领取限制
|
||||
CheckLoginUtils.checkLogin(context, "礼包详情-[" + btnStatus + "]", () -> {
|
||||
if ("领取".equals(btnStatus) || "淘号".equals(btnStatus)) {
|
||||
@ -407,27 +456,11 @@ public class LibaoUtils {
|
||||
} else {
|
||||
libaoLing(context, libaoBtn, libaoEntity, adapter, isInstallRequired, null, entrance, sourceEntrance, listener);
|
||||
}
|
||||
SensorsBridge.trackEvent("GameGiftDraw",
|
||||
"gift_type", "普通礼包",
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance
|
||||
);
|
||||
break;
|
||||
case "再淘":
|
||||
case "再淘一个":
|
||||
case "淘号":
|
||||
libaoTao(context, libaoBtn, libaoEntity, isInstallRequired, adapter, status, entrance, sourceEntrance, listener);
|
||||
SensorsBridge.trackEvent("GameGiftDraw",
|
||||
"gift_type", "淘号礼包",
|
||||
"game_name", libaoEntity.getGame().getName(),
|
||||
"game_id", libaoEntity.getGame().getId(),
|
||||
"gift_id", libaoEntity.getId(),
|
||||
"gift_name", libaoEntity.getName(),
|
||||
"source_entrance", sourceEntrance
|
||||
);
|
||||
break;
|
||||
}
|
||||
});
|
||||
@ -480,7 +513,7 @@ public class LibaoUtils {
|
||||
UserDataLibaoEntity me = new UserDataLibaoEntity(libaoCode, "ling", Utils.getTime(context));
|
||||
initLibaoCode(libaoEntity, me);
|
||||
if (adapter != null) adapter.initLibaoCode(me);
|
||||
EventBus.getDefault().post(new EBReuse("libaoChanged"));
|
||||
EventBus.getDefault().post(new EBReuse(Constants.LIBAO_CHANGED_TAG));
|
||||
if (listener != null) listener.onLibaoStatusChange();
|
||||
uploadEvent(libaoEntity, true, entrance);
|
||||
String des;
|
||||
@ -514,6 +547,7 @@ public class LibaoUtils {
|
||||
try {
|
||||
JSONObject errorJson = new JSONObject(exception.response().errorBody().string());
|
||||
String detail = errorJson.getString("detail").toLowerCase();
|
||||
int code = errorJson.getInt("code");
|
||||
switch (detail) {
|
||||
case "coming":
|
||||
Utils.toast(context, "礼包领取时间未开始");
|
||||
@ -544,7 +578,13 @@ public class LibaoUtils {
|
||||
Utils.toast(context, "淘号失败,稍后重试");
|
||||
break;
|
||||
default:
|
||||
Utils.toast(context, "操作失败");
|
||||
if (code == 403211) {
|
||||
Utils.toast(context, "条件不符,请先提交游戏评价");
|
||||
} else if (code == 403212) {
|
||||
Utils.toast(context, "条件不符,请修改游戏评价");
|
||||
} else {
|
||||
Utils.toast(context, "操作失败");
|
||||
}
|
||||
break;
|
||||
|
||||
}
|
||||
@ -620,7 +660,7 @@ public class LibaoUtils {
|
||||
UserDataLibaoEntity me = new UserDataLibaoEntity(libaoCode, "ling", Utils.getTime(context));
|
||||
initLibaoCode(libaoEntity, me);
|
||||
if (listener != null) listener.onLibaoStatusChange();
|
||||
EventBus.getDefault().post(new EBReuse("libaoChanged"));
|
||||
EventBus.getDefault().post(new EBReuse(Constants.LIBAO_CHANGED_TAG));
|
||||
uploadEvent(libaoEntity, false, entrance);
|
||||
|
||||
if (adapter != null) {
|
||||
@ -662,6 +702,7 @@ public class LibaoUtils {
|
||||
String string = exception.response().errorBody().string();
|
||||
JSONObject errorJson = new JSONObject(string);
|
||||
String detail = errorJson.getString("detail").toLowerCase();
|
||||
int code = errorJson.getInt("code");
|
||||
switch (detail) {
|
||||
case "coming":
|
||||
Utils.toast(context, "礼包领取时间未开始");
|
||||
@ -701,7 +742,13 @@ public class LibaoUtils {
|
||||
Utils.toast(context, "网络状态异常,请稍后再试");
|
||||
break;
|
||||
default:
|
||||
Utils.toast(context, "操作失败");
|
||||
if (code == 403211) {
|
||||
Utils.toast(context, "条件不符,请先提交游戏评价");
|
||||
} else if (code == 403212) {
|
||||
Utils.toast(context, "条件不符,请修改游戏评价");
|
||||
} else {
|
||||
Utils.toast(context, "操作失败");
|
||||
}
|
||||
break;
|
||||
}
|
||||
} catch (Exception ex) {
|
||||
|
||||
@ -2,21 +2,29 @@ package com.gh.common.util
|
||||
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.gh.common.dialog.AccelerateExpirationDialogFragment
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.download.PackageObserver
|
||||
import com.gh.gamecenter.DownloadManagerActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.NewFlatLogUtils
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.core.provider.IAcceleratorProvider
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.hud.HeadUpDisplayHelper
|
||||
import com.gh.gamecenter.hud.HeadUpDisplayLogHelper
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.hud.HeadUpDisplayLogHelper
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.getMetaExtra
|
||||
import com.gh.gamecenter.dsp.DspReportHelper
|
||||
import com.lightgame.utils.Utils
|
||||
import com.therouter.TheRouter
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
@ -27,12 +35,13 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
private const val UNINSTALL_PENDING = 2
|
||||
private const val UPDATE_PENDING = 3
|
||||
|
||||
// <包名,pending 类型,应用版本> Triple
|
||||
private var pendingPackageTriple: Triple<String, Int, String>? = null
|
||||
private var pendingGhId: String? = null
|
||||
// <包名,pending 类型,应用版本,光环ID> 的 pending 集合
|
||||
private var pendingPackageMap: HashMap<String, PackageEntity> = hashMapOf()
|
||||
|
||||
private var pendingHUDShowed: Boolean = false
|
||||
|
||||
private var isLaunching = true
|
||||
|
||||
/**
|
||||
* 添加一个等待中,待确定是否已成功安装的应用
|
||||
*/
|
||||
@ -43,24 +52,21 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
Utils.log(TAG, "添加了: $packageName 包名等待安装成功")
|
||||
|
||||
pendingHUDShowed = false
|
||||
pendingPackageTriple = Triple(packageName, INSTALL_PENDING, "")
|
||||
pendingPackageMap[packageName] = PackageEntity(packageName, INSTALL_PENDING, "")
|
||||
} else {
|
||||
Utils.log(TAG, "添加了: $packageName 包名等待安装更新成功")
|
||||
|
||||
val ghId = PackageUtils.getGhId(packageName)
|
||||
|
||||
// 记录光环插件相关信息,用于安装成功后的处理
|
||||
if (ghId != null) {
|
||||
pendingGhId = ghId.toString()
|
||||
}
|
||||
val ghId = PackageUtils.getGhId(packageName)?.toString() ?: ""
|
||||
|
||||
pendingHUDShowed = false
|
||||
pendingPackageTriple = Triple(packageName, UPDATE_PENDING, installData.version)
|
||||
pendingPackageMap[packageName] =
|
||||
PackageEntity(packageName, UPDATE_PENDING, installData.version, ghId)
|
||||
}
|
||||
}
|
||||
|
||||
fun isInstallPendingPackage(packageName: String): Boolean {
|
||||
return pendingPackageTriple?.first == packageName
|
||||
return pendingPackageMap[packageName]?.packageName == packageName
|
||||
}
|
||||
|
||||
/**
|
||||
@ -68,87 +74,159 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
*/
|
||||
fun addUninstallPendingPackage(packageName: String) {
|
||||
Utils.log(TAG, "添加了: $packageName 包名等待卸载成功")
|
||||
pendingPackageTriple = Triple(packageName, UNINSTALL_PENDING, "")
|
||||
pendingPackageMap[packageName] = PackageEntity(packageName, UNINSTALL_PENDING, "")
|
||||
}
|
||||
|
||||
override fun onCreate(owner: LifecycleOwner) {
|
||||
super.onCreate(owner)
|
||||
isLaunching = true
|
||||
}
|
||||
|
||||
override fun onResume(owner: LifecycleOwner) {
|
||||
super.onResume(owner)
|
||||
|
||||
if (pendingPackageTriple != null) {
|
||||
val packageName = pendingPackageTriple?.first ?: return
|
||||
val isInstallPending = pendingPackageTriple?.second == INSTALL_PENDING
|
||||
val isUninstallPending = pendingPackageTriple?.second == UNINSTALL_PENDING
|
||||
val isUpdatePending = pendingPackageTriple?.second == UPDATE_PENDING
|
||||
if (pendingPackageMap.size > 0) {
|
||||
calculateAndDispatchPackageChanges()
|
||||
}
|
||||
|
||||
val pendingVersion = pendingPackageTriple?.third ?: ""
|
||||
refreshVipStatus()
|
||||
}
|
||||
|
||||
/**
|
||||
* 应用从后台切到前台,如果是登录状态,需要刷新用户的vip状态
|
||||
*/
|
||||
private fun refreshVipStatus() {
|
||||
val iAcceleratorProvider = TheRouter.get(IAcceleratorProvider::class.java)
|
||||
if (iAcceleratorProvider?.hasToken() == true &&
|
||||
AcceleratorDataHolder.instance.enableRefresh &&
|
||||
AcceleratorDataHolder.instance.vipEntity != null &&
|
||||
!isLaunching
|
||||
) {
|
||||
iAcceleratorProvider.loadQyUserPermissionData {
|
||||
val vip = AcceleratorDataHolder.instance.vipEntity
|
||||
if (vip != null && !vip.vipStatus && !vip.isNewUser && !vip.isVipRefundUser && !vip.isDurationRefundUser) {
|
||||
// 获取当前
|
||||
val currentActivity = CurrentActivityHolder.getCurrentActivity()
|
||||
val reminder = AccelerateExpirationDialogFragment.ExpirationReminder.TimeRunsOut("")
|
||||
if (currentActivity != null && HaloApp.getInstance().isRunningForeground) {
|
||||
AccelerateExpirationDialogFragment.checkDialogShown(currentActivity, reminder)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 计算并分发包名的安装状态
|
||||
*/
|
||||
private fun calculateAndDispatchPackageChanges() {
|
||||
val completeList = arrayListOf<PackageEntity>()
|
||||
val pendingHudPackageList = arrayListOf<String>()
|
||||
|
||||
for (packageEntity in pendingPackageMap.values) {
|
||||
val packageName = packageEntity.packageName
|
||||
val pendingVersion = packageEntity.version
|
||||
val pendingGhId = packageEntity.ghId
|
||||
|
||||
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
|
||||
val isInstalled = installedVersionName != null
|
||||
|
||||
if (isInstallPending) {
|
||||
if (packageEntity.isInstallPending()) {
|
||||
if (isInstalled) {
|
||||
pendingPackageTriple = null
|
||||
pendingGhId = null
|
||||
completeList += packageEntity
|
||||
|
||||
PackageRepository.addInstalledGame(PackageRepository.packageFilterManager, packageName)
|
||||
PackageRepository.addInstalledGame(
|
||||
PackageRepository.packageFilterManager,
|
||||
packageName
|
||||
)
|
||||
|
||||
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(
|
||||
packageName = packageName,
|
||||
isAdd = true
|
||||
)
|
||||
|
||||
performInstallSuccessAction(packageName)
|
||||
} else {
|
||||
// 任务不存在了,将等待更新安装结果的 triple 置为 null
|
||||
if (DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) == null) {
|
||||
pendingPackageTriple = null
|
||||
// 任务不存在了,将等待更新安装结果的 packageEntity 置为 null
|
||||
if (DownloadManager.getInstance()
|
||||
.getDownloadEntitySnapshotByPackageName(packageName) == null
|
||||
) {
|
||||
completeList += packageEntity
|
||||
} else {
|
||||
showHUDIfNeeded(packageName)
|
||||
pendingHudPackageList.add(packageName)
|
||||
}
|
||||
}
|
||||
} else if (isUninstallPending && !isInstalled) {
|
||||
pendingPackageTriple = null
|
||||
pendingGhId = null
|
||||
} else if (packageEntity.isUninstallPending() && !isInstalled) {
|
||||
completeList += packageEntity
|
||||
|
||||
// 从额外的包名白名单移除,下次启动时不再查询此包的安装情况
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = false)
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(
|
||||
packageName = packageName,
|
||||
isAdd = false
|
||||
)
|
||||
|
||||
performUninstallSuccessAction(packageName)
|
||||
} else if (isUpdatePending) {
|
||||
} else if (packageEntity.isUpdatePending()) {
|
||||
val isUpdateValid = if (installedVersionName != pendingVersion) {
|
||||
true
|
||||
} else {
|
||||
!pendingGhId.isNullOrEmpty() && pendingGhId != PackageUtils.getGhId(packageName).toString()
|
||||
pendingGhId.isNotEmpty()
|
||||
&& pendingGhId != PackageUtils.getGhId(packageName).toString()
|
||||
}
|
||||
|
||||
pendingPackageTriple = null
|
||||
pendingGhId = null
|
||||
|
||||
if (isUpdateValid) {
|
||||
performUninstallSuccessAction(packageName)
|
||||
performInstallSuccessAction(packageName)
|
||||
|
||||
completeList += packageEntity
|
||||
} else {
|
||||
// 任务不存在了,将等待更新安装结果的 triple 置为 null
|
||||
if (DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) == null) {
|
||||
pendingPackageTriple = null
|
||||
// 任务不存在了,将等待更新安装结果的 packageEntity 置为 null
|
||||
if (DownloadManager.getInstance()
|
||||
.getDownloadEntitySnapshotByPackageName(packageName) == null
|
||||
) {
|
||||
completeList += packageEntity
|
||||
} else {
|
||||
showHUDIfNeeded(packageName)
|
||||
pendingHudPackageList.add(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(
|
||||
packageName = packageName,
|
||||
isAdd = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
for (invalidPackage in completeList) {
|
||||
pendingPackageMap.remove(invalidPackage.packageName)
|
||||
}
|
||||
|
||||
if (pendingHudPackageList.isNotEmpty()) {
|
||||
showHUDIfNeeded(pendingHudPackageList.last())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStop(owner: LifecycleOwner) {
|
||||
super.onStop(owner)
|
||||
isLaunching = false
|
||||
}
|
||||
|
||||
private fun showHUDIfNeeded(packageName: String) {
|
||||
if (pendingHUDShowed) return
|
||||
|
||||
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName) ?: return
|
||||
val downloadEntity =
|
||||
DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
|
||||
?: return
|
||||
if (downloadEntity.gameId == Constants.GHZS_GAME_ID) return
|
||||
val activity = CurrentActivityHolder.getCurrentActivity() ?: return
|
||||
if (activity is DownloadManagerActivity) return
|
||||
|
||||
pendingHUDShowed = true
|
||||
HeadUpDisplayHelper.showHUD(activity,
|
||||
HeadUpDisplayHelper.showHUD(
|
||||
activity,
|
||||
downloadEntity.icon ?: "",
|
||||
activity.getString(R.string.hud_has_pending_install_game),
|
||||
activity.getString(R.string.hud_head_to_download_manager),
|
||||
@ -189,16 +267,26 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
private fun performInstallSuccessAction(
|
||||
packageName: String,
|
||||
cachedGameEntity: GameEntity? = null,
|
||||
withLog: Boolean = true
|
||||
withLog: Boolean = true,
|
||||
) {
|
||||
Utils.log(TAG, "安装了: $packageName 包名的程序")
|
||||
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
|
||||
val gameId = if (downloadEntity != null && downloadEntity.gameId != null) downloadEntity.gameId else ""
|
||||
val gameName = if (downloadEntity != null && downloadEntity.name != null) downloadEntity.name else ""
|
||||
val downloadEntity =
|
||||
DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
|
||||
val gameId = downloadEntity?.gameId ?: ""
|
||||
val gameName = downloadEntity?.name ?: ""
|
||||
|
||||
if (withLog) {
|
||||
if (withLog && gameId.isNotEmpty()) {
|
||||
NewFlatLogUtils.logGameInstallComplete(gameId, gameName)
|
||||
SensorsBridge.trackInstallGameFinish(gameId, gameName)
|
||||
SensorsBridge.trackInstallGameFinish(
|
||||
gameId,
|
||||
gameName,
|
||||
downloadEntity?.getMetaExtra(Constants.DSP_GAME) == "true",
|
||||
downloadEntity?.getMetaExtra(Constants.DSP_AD_ID) ?: ""
|
||||
)
|
||||
|
||||
if (downloadEntity?.getMetaExtra(Constants.DSP_GAME) == "true") {
|
||||
DspReportHelper.report(downloadEntity.getMetaExtra(Constants.INSTALL_URL))
|
||||
}
|
||||
}
|
||||
|
||||
InstallUtils.getInstance().removeInstall(packageName)
|
||||
@ -237,4 +325,23 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
EventBus.getDefault().post(uninstallEb)
|
||||
}
|
||||
|
||||
private class PackageEntity(
|
||||
val packageName: String,
|
||||
val type: Int,
|
||||
val version: String,
|
||||
val ghId: String = "",
|
||||
) {
|
||||
fun isInstallPending(): Boolean {
|
||||
return type == INSTALL_PENDING
|
||||
}
|
||||
|
||||
fun isUninstallPending(): Boolean {
|
||||
return type == UNINSTALL_PENDING
|
||||
}
|
||||
|
||||
fun isUpdatePending(): Boolean {
|
||||
return type == UPDATE_PENDING
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -12,6 +12,7 @@ import androidx.fragment.app.FragmentActivity
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.download.server.BrowserInstallHelper
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.DialogHelper
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||||
@ -24,7 +25,6 @@ import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.entity.WhitePackageListEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.SettingsEntity
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -459,6 +459,9 @@ object PackageHelper {
|
||||
PackageRepository.initData {
|
||||
refreshLocalPackageList()
|
||||
refreshPackageNameList()
|
||||
|
||||
// 初始化使用浏览器安装的条件
|
||||
BrowserInstallHelper.initIfConditionMatched(Config.getNewSettingsEntity())
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -64,23 +64,27 @@ object PackageInstaller {
|
||||
showUnzipToast: Boolean,
|
||||
ignoreAsVGame: Boolean,
|
||||
) {
|
||||
val pkgPath = downloadEntity.path
|
||||
val isXapk = XapkInstaller.XAPK_EXTENSION_NAME == pkgPath.getExtension()
|
||||
val isDownloadAsVGame = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.VGAME
|
||||
|| downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE) == Constants.DUAL_DOWNLOAD_VGAME
|
||||
|
||||
val currentActivity = AppManager.getInstance().currentActivity() ?: return
|
||||
val properContext = if (context is AppCompatActivity && !context.isFinishing) {
|
||||
context
|
||||
} else {
|
||||
AppManager.getInstance().currentActivity()
|
||||
}
|
||||
|
||||
properContext ?: return
|
||||
|
||||
if (!ignoreAsVGame && isDownloadAsVGame) {
|
||||
VHelper.install(currentActivity, downloadEntity)
|
||||
VHelper.install(properContext, downloadEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// 已知问题
|
||||
// 1. 此处可能遇到 activity 是 WXEntryActivity,因为 WXEntryActivity 不是 AppCompatActivity 调不起弹窗
|
||||
// 2. 当 activity 全部出栈,但是应用还在下载游戏,下载完会唤不起安装
|
||||
if (currentActivity is AppCompatActivity && !currentActivity.isFinishing) {
|
||||
InstallPermissionDialogFragment.show(currentActivity, downloadEntity) { isFromPermissionGrantedCallback ->
|
||||
if (properContext is AppCompatActivity && !properContext.isFinishing) {
|
||||
InstallPermissionDialogFragment.show(properContext, downloadEntity) { isFromPermissionGrantedCallback ->
|
||||
// 取消状态栏下载完成的通知,若存在
|
||||
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
@ -93,7 +97,7 @@ object PackageInstaller {
|
||||
context.startActivity(mainIntent)
|
||||
Runtime.getRuntime().exit(0)
|
||||
} else {
|
||||
if (isXapk) {
|
||||
if (XapkInstaller.isXapk(downloadEntity)) {
|
||||
XapkInstaller.install(context, downloadEntity, showUnzipToast)
|
||||
} else {
|
||||
install(context, downloadEntity.isPlugin, downloadEntity.path, downloadEntity)
|
||||
@ -205,9 +209,6 @@ object PackageInstaller {
|
||||
pkgPath: String,
|
||||
sessionId: Int = -1
|
||||
) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||
return
|
||||
}
|
||||
val installer = context.packageManager.packageInstaller
|
||||
val session = installer.openSession(sessionId)
|
||||
// 监听安装回调的组件,可以是Activity、Service或者是BroadcastReceiver
|
||||
|
||||
@ -95,7 +95,8 @@ object ReservationHelper {
|
||||
it.categoryChinese,
|
||||
data.wechatConfig.isReminderEnable,
|
||||
if (data.hasSmsConfig) data.smsConfig.notice else null,
|
||||
if (data.isEnableAutoDownload) data.wifiAutoDownload else null
|
||||
if (data.isEnableAutoDownload) data.wifiAutoDownload else null,
|
||||
if (data.hasCalendarConfig) false else null
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -32,6 +32,7 @@ import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONArray
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
import java.util.*
|
||||
|
||||
@ -229,15 +230,19 @@ object UsageStatsHelper {
|
||||
mApi.getUsageStatusUpdateTime(HaloApp.getInstance().gid)
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
val body = JSONObject(data.string())
|
||||
val lastPostTime = body.getLong("update_time") * 1000
|
||||
try {
|
||||
val body = JSONObject(data.string())
|
||||
val lastPostTime = body.getLong("update_time") * 1000
|
||||
|
||||
val beginTime = if (lastPostTime == 0L) {
|
||||
getDefaultBeginTime()
|
||||
} else {
|
||||
lastPostTime
|
||||
val beginTime = if (lastPostTime == 0L) {
|
||||
getDefaultBeginTime()
|
||||
} else {
|
||||
lastPostTime
|
||||
}
|
||||
postUsageStats(beginTime)
|
||||
} catch (e: JSONException) {
|
||||
Utils.log("UsageStats: 获取上次上传时间失败,错误信息:${e.message}")
|
||||
}
|
||||
postUsageStats(beginTime)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@ -2,12 +2,10 @@ package com.gh.common.util
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.therouter.TheRouter
|
||||
import com.gh.common.iinterface.ISuperiorChain
|
||||
import com.gh.gamecenter.amway.AmwayFragment
|
||||
import com.gh.gamecenter.category2.CategoryV2Fragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.provider.IHelpAndFeedbackProvider
|
||||
import com.gh.gamecenter.discovery.DiscoveryFragment
|
||||
@ -19,7 +17,7 @@ import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFrag
|
||||
import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailFragment
|
||||
import com.gh.gamecenter.gamecollection.hotlist.GameCollectionHotListWrapperFragment
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.gh.gamecenter.gamedetail.GameDetailWrapperFragment
|
||||
import com.gh.gamecenter.info.InfoWrapperFragment
|
||||
import com.gh.gamecenter.libao.LibaoDetailFragment
|
||||
import com.gh.gamecenter.libao.LibaoFragment
|
||||
@ -38,6 +36,7 @@ import com.gh.gamecenter.toolbox.ToolboxFragment
|
||||
import com.gh.gamecenter.video.detail.HomeVideoFragment
|
||||
import com.gh.gamecenter.wrapper.ToolbarWrapperFragment
|
||||
import com.halo.assistant.fragment.WebFragment
|
||||
import com.therouter.TheRouter
|
||||
|
||||
/**
|
||||
* 通用跳转Fragment方法
|
||||
@ -88,7 +87,7 @@ object ViewPagerFragmentHelper {
|
||||
// 游戏详情页
|
||||
TYPE_GAME -> {
|
||||
bundle.putString(EntranceConsts.KEY_GAMEID, linkEntity.link)
|
||||
GameDetailFragment().with(bundle)
|
||||
GameDetailWrapperFragment().with(bundle)
|
||||
}
|
||||
// 我的光环
|
||||
TYPE_MY_HALO -> {
|
||||
@ -148,12 +147,21 @@ object ViewPagerFragmentHelper {
|
||||
bundle.putString(EntranceConsts.KEY_QUESTIONS_ID, linkEntity.link)
|
||||
NewQuestionDetailFragment().with(bundle)
|
||||
}
|
||||
// 专题合集详情页
|
||||
TYPE_COLUMN_COLLECTION -> {
|
||||
bundle.putString(EntranceConsts.KEY_COLLECTION_ID, linkEntity.link)
|
||||
bundle.putInt(EntranceConsts.KEY_POSITION, 0)
|
||||
bundle.putString(EntranceConsts.KEY_COLUMNNAME, linkEntity.text)
|
||||
bundle.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true)
|
||||
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "tab")
|
||||
ColumnCollectionDetailFragment().with(bundle)
|
||||
}
|
||||
// 其他原来带Toolbar的Fragment
|
||||
else -> createToolbarWrapperFragment(parentFragment, bundle, linkEntity, isTabWrapper)
|
||||
else -> createToolbarWrapperFragment(bundle, linkEntity, isTabWrapper)
|
||||
}
|
||||
}
|
||||
|
||||
private fun createToolbarWrapperFragment(parentFragment: Fragment?, bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
|
||||
private fun createToolbarWrapperFragment(bundle: Bundle, entity: LinkEntity, isTabWrapper: Boolean): Fragment {
|
||||
var className = ReloadFragment::class.java.name
|
||||
|
||||
when (entity.type) {
|
||||
@ -177,15 +185,6 @@ object ViewPagerFragmentHelper {
|
||||
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "detail")
|
||||
bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper)
|
||||
}
|
||||
// 专题合集详情页
|
||||
TYPE_COLUMN_COLLECTION -> {
|
||||
className = ColumnCollectionDetailFragment::class.java.name
|
||||
bundle.putString(EntranceConsts.KEY_COLLECTION_ID, entity.link)
|
||||
bundle.putInt(EntranceConsts.KEY_POSITION, 0)
|
||||
bundle.putString(EntranceConsts.KEY_COLUMNNAME, entity.text)
|
||||
bundle.putBoolean(EntranceConsts.KEY_IS_COLUMN_COLLECTION, true)
|
||||
bundle.putString(EntranceConsts.KEY_SUBJECT_TYPE, "tab")
|
||||
}
|
||||
// 开服表
|
||||
TYPE_SERVER -> {
|
||||
className = GameServersPublishFragment::class.java.name
|
||||
@ -200,8 +199,8 @@ object ViewPagerFragmentHelper {
|
||||
// 分类2.0
|
||||
TYPE_CATEGORY_V2 -> {
|
||||
className = CategoryV2Fragment::class.java.name
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, entity.link)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, entity.text)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_ID, entity.link)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_NAME, entity.text)
|
||||
bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper)
|
||||
}
|
||||
// 通用内容合集详情页
|
||||
|
||||
@ -1,38 +1,48 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.CompoundButton
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import android.widget.RadioButton
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.forEach
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.setDrawableEnd
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.LayoutCategoryFilterBinding
|
||||
import com.gh.gamecenter.databinding.LayoutCategoryFilterSizeBinding
|
||||
import com.gh.gamecenter.databinding.PopCategoryFilterSizeBinding
|
||||
import com.gh.gamecenter.databinding.PopCategoryFilterTypeBinding
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
class CategoryFilterView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
) : ConstraintLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var mTypeTv: TextView
|
||||
private var mCatalogTv: TextView
|
||||
private var mSizeTv: TextView
|
||||
private var mTypeContainer: View
|
||||
private var mCatalogContainer: View
|
||||
private var mSizeContainer: View
|
||||
private val binding: LayoutCategoryFilterBinding
|
||||
|
||||
private var mTypeFilterArray = arrayOf(SortType.RECOMMENDED, SortType.NEWEST, SortType.RATING)
|
||||
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
|
||||
private val mTypeFilterArray = arrayOf(SortType.RECOMMENDED, SortType.NEWEST, SortType.RATING)
|
||||
private val sizeFilterArray by lazy {
|
||||
listOf(
|
||||
SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"),
|
||||
SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"),
|
||||
SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"),
|
||||
SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"),
|
||||
SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"),
|
||||
SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上")
|
||||
)
|
||||
}
|
||||
|
||||
private var mOnCategoryFilterSetupListener: OnCategoryFilterSetupListener? = null
|
||||
private var mOnFilterClickListener: OnFilterClickListener? = null
|
||||
@ -41,29 +51,28 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
private var mSizePopupWindow: PopupWindow? = null
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.layout_category_filter, this)
|
||||
|
||||
mTypeTv = findViewById(R.id.type_tv)
|
||||
mCatalogTv = findViewById(R.id.catalog_tv)
|
||||
mSizeTv = findViewById(R.id.size_tv)
|
||||
mTypeContainer = findViewById(R.id.container_type)
|
||||
mCatalogContainer = findViewById(R.id.container_category)
|
||||
mSizeContainer = findViewById(R.id.container_size)
|
||||
mTypeTv.text = mTypeFilterArray[0].value
|
||||
val inflater = LayoutInflater.from(context)
|
||||
binding = LayoutCategoryFilterBinding.inflate(inflater, this, true)
|
||||
|
||||
mTypeContainer.setOnClickListener {
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
|
||||
binding.tvType.text = mTypeFilterArray[0].value
|
||||
|
||||
binding.llTypeContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onTypeClick()
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
showSelectTypePopupWindow()
|
||||
}
|
||||
|
||||
mCatalogContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onCategoryClick()
|
||||
mOnCategoryFilterSetupListener?.onSetupSortCategory()
|
||||
}
|
||||
|
||||
mSizeContainer.setOnClickListener {
|
||||
binding.llSizeContainer.setOnClickListener {
|
||||
mOnFilterClickListener?.onSizeClick()
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
showSelectSizePopupWindow()
|
||||
}
|
||||
}
|
||||
|
||||
@ -76,154 +85,133 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun resetSortSize() {
|
||||
mSizeTv.text = "全部大小"
|
||||
binding.tvType.text = R.string.size.toResString()
|
||||
}
|
||||
|
||||
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
if (highlightIt) {
|
||||
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.bg_tag_text)
|
||||
targetTextView.setTextColor(Color.WHITE)
|
||||
} else {
|
||||
targetTextView.background = null
|
||||
targetTextView.setTextColor(ContextCompat.getColor(targetTextView.context, com.gh.gamecenter.common.R.color.text_757575))
|
||||
}
|
||||
}
|
||||
private fun showSelectTypePopupWindow() {
|
||||
binding.tvType.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
private fun showSelectTypePopupWindow(containerView: View, typeTv: TextView, typeText: String) {
|
||||
typeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_up)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
val inflater = LayoutInflater.from(typeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val windowWidth = typeTv.context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val typeBinding = PopCategoryFilterTypeBinding.inflate(inflater, null, false)
|
||||
val windowWidth = resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val windowHeight = mOnCategoryFilterSetupListener?.getPopHeight() ?: 0
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
typeBinding.root,
|
||||
windowWidth,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
if (windowHeight == 0) ViewGroup.LayoutParams.WRAP_CONTENT else (windowHeight - height)
|
||||
).apply { mTypePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
typeBinding.root.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (type in mTypeFilterArray) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
val checkedId = when (binding.tvType.text.toString()) {
|
||||
"最新" -> R.id.rb_newest
|
||||
"评分" -> R.id.rb_score
|
||||
else -> R.id.rb_popular
|
||||
}
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = windowWidth / 3
|
||||
val height = item.layoutParams.height
|
||||
val checkButton = typeBinding.root.findViewById<RadioButton>(checkedId)
|
||||
checkButton.setTypeface(checkButton.typeface, Typeface.BOLD)
|
||||
checkButton.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
checkButton.setDrawableEnd(R.drawable.ic_basic_checkmark_circle_fill)
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = type.value
|
||||
|
||||
toggleHighlightedTextView(tv, typeText == type.value)
|
||||
|
||||
tv.tag = type.value
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
typeTv.text = type.value
|
||||
val onCheckedChangeListener = CompoundButton.OnCheckedChangeListener { button, isChecked ->
|
||||
if (isChecked) {
|
||||
button.setTypeface(button.typeface, Typeface.BOLD)
|
||||
button.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
button.setDrawableEnd(R.drawable.ic_basic_checkmark_circle_fill)
|
||||
|
||||
val type = when (button.id) {
|
||||
R.id.rb_newest -> SortType.NEWEST
|
||||
R.id.rb_score -> SortType.RATING
|
||||
else -> SortType.RECOMMENDED
|
||||
}
|
||||
binding.tvType.text = type.value
|
||||
mOnCategoryFilterSetupListener?.onSetupSortType(type)
|
||||
popupWindow.dismiss()
|
||||
} else {
|
||||
button.setTypeface(button.typeface, Typeface.NORMAL)
|
||||
button.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
button.setDrawableEnd(null)
|
||||
}
|
||||
}
|
||||
typeBinding.rgFilter.forEach {
|
||||
if (it is RadioButton) {
|
||||
it.setOnCheckedChangeListener(onCheckedChangeListener)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
typeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
binding.tvType.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
binding.ivTypeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivTypeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
mTypePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
popupWindow.showAsDropDown(this, 0, 0)
|
||||
}
|
||||
|
||||
private fun showSelectSizePopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
private fun showSelectSizePopupWindow() {
|
||||
binding.tvSize.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_up)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
|
||||
val inflater = LayoutInflater.from(sizeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val windowWidth = sizeTv.context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val sizeBinding = PopCategoryFilterSizeBinding.inflate(inflater, null, false)
|
||||
val windowWidth = context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
val windowHeight = mOnCategoryFilterSetupListener?.getPopHeight() ?: 0
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
sizeBinding.root,
|
||||
windowWidth,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
if (windowHeight == 0) ViewGroup.LayoutParams.WRAP_CONTENT else (windowHeight - height)
|
||||
).apply { mSizePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
sizeFilterArray = if (sizeFilterArray == null) {
|
||||
getDefaultSizeFilterArray()
|
||||
} else {
|
||||
sizeFilterArray?.apply {
|
||||
if (firstOrNull()?.text != "全部大小") {
|
||||
add(0, SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
sizeBinding.root.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (size in sizeFilterArray!!) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = windowWidth / 3
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = size.text
|
||||
|
||||
toggleHighlightedTextView(tv, sizeText == size.text)
|
||||
|
||||
tv.tag = size.text
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
sizeTv.text = size.text
|
||||
|
||||
mOnCategoryFilterSetupListener?.onSetupSortSize(size)
|
||||
}
|
||||
sizeBinding.rvSize.setOnClickListener {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
val sizeAdapter = CategoryFilterSizeAdapter {
|
||||
if (it.min == INVALID_SIZE && it.max == INVALID_SIZE) {
|
||||
binding.tvSize.text = R.string.size.toResString()
|
||||
} else {
|
||||
binding.tvSize.text = it.text
|
||||
}
|
||||
popupWindow.dismiss()
|
||||
mOnCategoryFilterSetupListener?.onSetupSortSize(it)
|
||||
}
|
||||
sizeBinding.rvSize.layoutManager = GridLayoutManager(context, 3, RecyclerView.VERTICAL, false)
|
||||
sizeBinding.rvSize.adapter = sizeAdapter
|
||||
val selectedPosition =
|
||||
sizeFilterArray.indexOfFirst { it.text?.contains(binding.tvSize.text.toString()) == true }
|
||||
|
||||
sizeAdapter.setData(sizeFilterArray, selectedPosition)
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
binding.tvSize.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
binding.ivSizeArrow.setImageResource(R.drawable.ic_arrow_down)
|
||||
binding.ivSizeArrow.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_neutral.toColor(context))
|
||||
mSizePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
}
|
||||
|
||||
private fun getDefaultSizeFilterArray(): ArrayList<SubjectSettingEntity.Size> {
|
||||
return arrayListOf<SubjectSettingEntity.Size>().apply {
|
||||
add(SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
add(SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"))
|
||||
add(SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"))
|
||||
add(SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"))
|
||||
add(SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"))
|
||||
add(SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上"))
|
||||
}
|
||||
popupWindow.showAsDropDown(this, 0, 0)
|
||||
}
|
||||
|
||||
fun setRootBackgroundColor(@ColorInt color: Int) {
|
||||
@ -231,21 +219,23 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
fun setItemTextColor(@ColorInt color: Int) {
|
||||
mTypeTv.setTextColor(color)
|
||||
mCatalogTv.setTextColor(color)
|
||||
mSizeTv.setTextColor(color)
|
||||
val colorInt = com.gh.gamecenter.common.R.color.text_neutral.toColor(context)
|
||||
binding.tvType.setTextColor(color)
|
||||
binding.tvSize.setTextColor(color)
|
||||
binding.ivTypeArrow.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
binding.ivSizeArrow.imageTintList = ColorStateList.valueOf(colorInt)
|
||||
}
|
||||
|
||||
fun updatePopupWindow() {
|
||||
when {
|
||||
mTypePopupWindow != null && mTypePopupWindow!!.isShowing -> {
|
||||
mTypePopupWindow?.dismiss()
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
showSelectTypePopupWindow()
|
||||
}
|
||||
|
||||
mSizePopupWindow != null && mSizePopupWindow!!.isShowing -> {
|
||||
mSizePopupWindow?.dismiss()
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
showSelectSizePopupWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -253,18 +243,69 @@ class CategoryFilterView @JvmOverloads constructor(
|
||||
interface OnCategoryFilterSetupListener {
|
||||
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
|
||||
fun onSetupSortType(sortType: SortType)
|
||||
fun onSetupSortCategory()
|
||||
fun getPopHeight(): Int
|
||||
}
|
||||
|
||||
interface OnFilterClickListener {
|
||||
fun onCategoryClick()
|
||||
fun onTypeClick()
|
||||
fun onSizeClick()
|
||||
}
|
||||
|
||||
enum class SortType(val value: String) {
|
||||
RECOMMENDED("热门推荐"),
|
||||
NEWEST("最新上线"),
|
||||
RATING("最高评分")
|
||||
RECOMMENDED("热门"),
|
||||
NEWEST("最新"),
|
||||
RATING("评分")
|
||||
}
|
||||
|
||||
class CategoryFilterSizeAdapter(private val itemClick: (SubjectSettingEntity.Size) -> Unit) :
|
||||
RecyclerView.Adapter<CategoryFilterSizeAdapter.SizeViewHolder>() {
|
||||
|
||||
private val dataList = arrayListOf<SubjectSettingEntity.Size>()
|
||||
private var selectedPosition = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(data: List<SubjectSettingEntity.Size>, position: Int) {
|
||||
dataList.clear()
|
||||
dataList.addAll(data)
|
||||
selectedPosition = position
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): SizeViewHolder {
|
||||
return SizeViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return dataList.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SizeViewHolder, position: Int) {
|
||||
val item = dataList[position]
|
||||
val context = holder.binding.root.context
|
||||
if (selectedPosition == position) {
|
||||
holder.binding.root.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
holder.binding.root.setBackgroundResource(R.drawable.shape_category_filter_size_item_selected)
|
||||
} else {
|
||||
holder.binding.root.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
holder.binding.root.setBackgroundResource(R.drawable.shape_category_filter_size_item_normal)
|
||||
}
|
||||
holder.binding.root.text = item.text
|
||||
holder.binding.root.setOnClickListener {
|
||||
if (selectedPosition == holder.bindingAdapterPosition) {
|
||||
return@setOnClickListener
|
||||
}
|
||||
val lastPosition = selectedPosition
|
||||
selectedPosition = holder.bindingAdapterPosition
|
||||
notifyItemChanged(lastPosition)
|
||||
notifyItemChanged(selectedPosition)
|
||||
itemClick(item)
|
||||
}
|
||||
}
|
||||
|
||||
class SizeViewHolder(val binding: LayoutCategoryFilterSizeBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INVALID_SIZE = -1
|
||||
}
|
||||
}
|
||||
@ -1,18 +1,23 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.CheckedTextView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.setPadding
|
||||
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.setDrawableEnd
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
@ -30,11 +35,15 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
var recommendedTv: TextView
|
||||
var updateTv: TextView //更新
|
||||
var container: View
|
||||
private var dot1: View
|
||||
private var dot2: View
|
||||
private var dot3: View
|
||||
|
||||
var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
|
||||
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
|
||||
|
||||
private var mOnConfigFilterSetupListener: OnConfigFilterSetupListener? = null
|
||||
private var mSelectionTvList: ArrayList<TextView>
|
||||
private var highlightedSortedType: SortType? = null
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.layout_config_filter, this)
|
||||
@ -45,6 +54,9 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
updateTv = findViewById(R.id.updateTv)
|
||||
recommendedTv = findViewById(R.id.recommended_tv)
|
||||
container = findViewById(R.id.config_controller)
|
||||
dot1 = findViewById(R.id.dot1)
|
||||
dot2 = findViewById(R.id.dot2)
|
||||
dot3 = findViewById(R.id.dot3)
|
||||
|
||||
mSelectionTvList = arrayListOf(newestTv, ratingTv, updateTv, recommendedTv)
|
||||
|
||||
@ -54,24 +66,30 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
ratingTv.setOnClickListener {
|
||||
highlightedSortedType = SortType.RATING
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RATING)
|
||||
updateHighlightedTextView(ratingTv)
|
||||
}
|
||||
|
||||
updateTv.setOnClickListener {
|
||||
highlightedSortedType = SortType.UPDATE
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.UPDATE)
|
||||
updateHighlightedTextView(updateTv)
|
||||
}
|
||||
|
||||
newestTv.setOnClickListener {
|
||||
highlightedSortedType = SortType.NEWEST
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.NEWEST)
|
||||
updateHighlightedTextView(newestTv)
|
||||
}
|
||||
|
||||
recommendedTv.setOnClickListener {
|
||||
highlightedSortedType = SortType.RECOMMENDED
|
||||
mOnConfigFilterSetupListener?.onSetupSortType(SortType.RECOMMENDED)
|
||||
updateHighlightedTextView(recommendedTv)
|
||||
}
|
||||
|
||||
updateAllTextView(SortType.UPDATE)
|
||||
}
|
||||
|
||||
private fun updateHighlightedTextView(highlightedTv: TextView) {
|
||||
@ -80,14 +98,17 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun updateAllTextView(sortType: SortType) {
|
||||
when (sortType) {
|
||||
SortType.RECOMMENDED -> updateHighlightedTextView(recommendedTv)
|
||||
SortType.NEWEST -> updateHighlightedTextView(newestTv)
|
||||
SortType.RATING -> updateHighlightedTextView(ratingTv)
|
||||
SortType.UPDATE -> updateHighlightedTextView(updateTv)
|
||||
fun updateAllTextView(sortType: SortType? = highlightedSortedType) {
|
||||
highlightedSortedType = sortType
|
||||
sortType?.let {
|
||||
when (it) {
|
||||
SortType.RECOMMENDED -> updateHighlightedTextView(recommendedTv)
|
||||
SortType.NEWEST -> updateHighlightedTextView(newestTv)
|
||||
SortType.RATING -> updateHighlightedTextView(ratingTv)
|
||||
SortType.UPDATE -> updateHighlightedTextView(updateTv)
|
||||
}
|
||||
mSizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
}
|
||||
mSizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
}
|
||||
|
||||
fun setOnConfigSetupListener(onConfigFilterSetupListener: OnConfigFilterSetupListener) {
|
||||
@ -96,11 +117,11 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
|
||||
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
if (highlightIt) {
|
||||
targetTextView.background = R.drawable.bg_tag_text.toDrawable()
|
||||
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_white.toColor(context))
|
||||
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
targetTextView.setTypeface(Typeface.DEFAULT, Typeface.BOLD)
|
||||
} else {
|
||||
targetTextView.background = null
|
||||
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(context))
|
||||
targetTextView.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
targetTextView.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
|
||||
}
|
||||
}
|
||||
|
||||
@ -112,8 +133,8 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
}
|
||||
|
||||
private fun showSelectionPopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(sizeTv.context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(sizeTv.context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_auxiliary_arrow_up_primary_8)
|
||||
|
||||
val inflater = LayoutInflater.from(sizeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
@ -136,43 +157,39 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
flexboxLayout.setOnClickListener { }
|
||||
backgroundView.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (size in sizeFilterArray!!) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
val item = inflater.inflate(R.layout.item_config_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 4 个,强行设置宽度为屏幕的 1/4
|
||||
val width = sizeTv.context.resources.displayMetrics.widthPixels / 4
|
||||
val width = (sizeTv.context.resources.displayMetrics.widthPixels - 56F.dip2px()) / 4
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
val tv = item.findViewById<CheckedTextView>(R.id.size_tv)
|
||||
tv.text = size.text
|
||||
|
||||
if (sizeText == size.text) {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
} else {
|
||||
toggleHighlightedTextView(tv, false)
|
||||
}
|
||||
|
||||
tv.isChecked = sizeText == size.text
|
||||
tv.tag = size.text
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
tv.setOnClickListener {
|
||||
flexboxLayout.forEach { checkedTv ->
|
||||
(checkedTv as CheckedTextView).isChecked = checkedTv == tv
|
||||
}
|
||||
popupWindow.dismiss()
|
||||
sizeTv.text = size.text
|
||||
|
||||
mOnConfigFilterSetupListener?.onSetupSortSize(size)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_757575.toColor(sizeTv.context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(sizeTv.context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_auxiliary_arrow_down_8)
|
||||
mPopupWindow = null
|
||||
}
|
||||
|
||||
@ -193,6 +210,49 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
|
||||
fun initSubjectFilterView(subjectSetting: SubjectSettingEntity) {
|
||||
ratingTv.visibility = View.VISIBLE
|
||||
|
||||
if (subjectSetting.filterOptions.size > 1) {
|
||||
// 重排序
|
||||
subjectSetting.filterOptions.forEachIndexed { index, s ->
|
||||
when (index) {
|
||||
0 -> updateTv.text = s
|
||||
1 -> recommendedTv.text = s
|
||||
2 -> newestTv.text = s
|
||||
3 -> ratingTv.text = s
|
||||
}
|
||||
}
|
||||
} else {
|
||||
updateTv.setPadding(0)
|
||||
updateTv.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
|
||||
updateTv.isClickable = false
|
||||
updateTv.text = when (subjectSetting.filterOptions.first()) {
|
||||
"推荐" -> "根据光环推荐排序"
|
||||
"最新" -> "根据游戏上新排序"
|
||||
"评分" -> "根据游戏评分排序"
|
||||
"更新" -> "根据更新时间排序"
|
||||
else -> subjectSetting.filterOptions.first()
|
||||
}
|
||||
}
|
||||
|
||||
// 隐藏相关选项
|
||||
updateTv.goneIf(subjectSetting.filterOptions.isEmpty())
|
||||
recommendedTv.goneIf(subjectSetting.filterOptions.size <= 1)
|
||||
dot1.goneIf(subjectSetting.filterOptions.size <= 1)
|
||||
newestTv.goneIf(subjectSetting.filterOptions.size <= 2)
|
||||
dot2.goneIf(subjectSetting.filterOptions.size <= 2)
|
||||
ratingTv.goneIf(subjectSetting.filterOptions.size <= 3)
|
||||
dot3.goneIf(subjectSetting.filterOptions.size <= 3)
|
||||
sizeFilterArray = subjectSetting.filterSizes
|
||||
|
||||
if (subjectSetting.filterOptions.size == 1) {
|
||||
updateTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
updateTv.setTypeface(Typeface.DEFAULT, Typeface.NORMAL)
|
||||
highlightedSortedType = null
|
||||
}
|
||||
}
|
||||
|
||||
interface OnConfigFilterSetupListener {
|
||||
fun onShowSortSize()
|
||||
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
|
||||
|
||||
@ -32,6 +32,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
private var mLastItemWidth = 0//最后更多按钮宽度
|
||||
private var mTotalWidth = 0
|
||||
private var mStrokeWidth = 0
|
||||
private var mShowMore = true
|
||||
var onClickListener: OnItemClickListener? = null
|
||||
|
||||
init {
|
||||
@ -44,11 +45,12 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
mTextSize = ta.getDimension(R.styleable.FlexLinearLayout_itemTextSize, 10F.sp2px().toFloat())
|
||||
mLastItemWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_lastItemWidth, 18F.dip2px())
|
||||
mStrokeWidth = ta.getDimensionPixelSize(R.styleable.FlexLinearLayout_strokeWidth, 0.5F.dip2px())
|
||||
mShowMore = ta.getBoolean(R.styleable.FlexLinearLayout_showMore, true)
|
||||
|
||||
ta.recycle()
|
||||
}
|
||||
|
||||
fun setTags(tags: ArrayList<TagStyleEntity>) {
|
||||
fun setTags(tags: List<TagStyleEntity>) {
|
||||
mTags.clear()
|
||||
mTotalCount = tags.size
|
||||
mTotalWidth = measuredWidth
|
||||
@ -86,7 +88,7 @@ class FlexLinearLayout @JvmOverloads constructor(context: Context, attrs: Attrib
|
||||
mTags.forEachIndexed { index, tag ->
|
||||
addView(createView(tag, index))
|
||||
}
|
||||
if (mTotalCount != mTags.size) {
|
||||
if (mShowMore && mTotalCount != mTags.size) {
|
||||
val imageView = ImageView(context).apply {
|
||||
val params = LayoutParams(mLastItemWidth, mItemHeight)
|
||||
layoutParams = params
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
package com.gh.common.xapk
|
||||
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
interface IXapkUnzipListener {
|
||||
fun onProgress(downloadEntity: DownloadEntity, unzipPath: String, unzipSize: Long, unzipProgress: Long)
|
||||
|
||||
fun onNext(downloadEntity: DownloadEntity, unzipPath: String)
|
||||
|
||||
fun onCancel(downloadEntity: DownloadEntity)
|
||||
|
||||
fun onFailure(downloadEntity: DownloadEntity, exception: Exception)
|
||||
|
||||
fun onSuccess(downloadEntity: DownloadEntity)
|
||||
}
|
||||
@ -3,9 +3,15 @@ package com.gh.common.xapk
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import android.provider.DocumentsContract
|
||||
import android.provider.Settings
|
||||
import androidx.activity.result.ActivityResultLauncher
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.documentfile.provider.DocumentFile
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.DownloadNotificationHelper
|
||||
@ -24,6 +30,7 @@ import com.gh.gamecenter.xapk.core.XApkUnZipCallback
|
||||
import com.gh.gamecenter.xapk.core.XApkUnZipEntry
|
||||
import com.gh.gamecenter.xapk.core.XApkUnZipOutputFactory
|
||||
import com.gh.gamecenter.xapk.io.NonSplitApksOutput
|
||||
import com.gh.gamecenter.xapk.io.OBBDocOutput
|
||||
import com.gh.gamecenter.xapk.io.OBBFileOutput
|
||||
import com.gh.gamecenter.xapk.io.SplitApksOutput
|
||||
import com.gh.gamecenter.xapk.io.XApkFileOutput
|
||||
@ -34,6 +41,8 @@ import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import java.io.File
|
||||
import java.util.*
|
||||
import androidx.core.net.toUri
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
|
||||
/**
|
||||
* 目前已知的Xapk内容是:只有一个apk包和一个或做多个obb数据包(多余文件不解压,如果存在多个apk包,则以下安装无效)
|
||||
@ -43,6 +52,12 @@ import java.util.*
|
||||
*
|
||||
* obb文件直接解压至根目录即可,无需操作文件
|
||||
* apk文件这解压的gh-files文件夹中
|
||||
*
|
||||
* update: 2025/4/29
|
||||
* 简单来说,有两种XAPK文件的格式:
|
||||
* XAPK = Android App Bundle(基础APK + 配置APK) 【downloadentity.format == xapk(apks)】
|
||||
* XAPK = APK文件 + OBB数据文件 【downloadentity.format == xapk】
|
||||
*
|
||||
*/
|
||||
@SuppressLint("StaticFieldLeak")
|
||||
object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
@ -65,6 +80,10 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
systemMatched && fileListMatched
|
||||
}
|
||||
|
||||
// 是否使用 DocumentUi 的方式访问 obb 文件夹
|
||||
private var useDocStyleToUnzip = false
|
||||
private var tempLauncher: ActivityResultLauncher<Uri>? = null
|
||||
|
||||
private const val GUIDE_TYPE_MIUI_OPTIMIZATION = "miui_optimization"
|
||||
private const val MIUI_OPTIMIZATION_WARNING_DIALOG_ENTRANCE = "MIUI优化关闭提示弹窗"
|
||||
|
||||
@ -79,69 +98,171 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
|
||||
private val mPendingSessionInfoMap = HashMap<String, XapkPendingSessionInfo>()
|
||||
|
||||
fun isXapk(downloadEntity: DownloadEntity) = XAPK_EXTENSION_NAME == downloadEntity.path.getExtension()
|
||||
|
||||
private fun isXapkApks(downloadEntity: DownloadEntity) = downloadEntity.format == Constants.XAPK_APKS_FORMAT
|
||||
|
||||
/**
|
||||
* precondition: assert_true(isXapk())
|
||||
*
|
||||
*/
|
||||
// 按并行解压
|
||||
@JvmStatic
|
||||
fun install(context: Context, downloadEntity: DownloadEntity, showUnzipToast: Boolean = false) {
|
||||
this.mContext = context
|
||||
|
||||
val filePath = downloadEntity.path
|
||||
if (XAPK_EXTENSION_NAME == filePath.getExtension()) {
|
||||
if (MiuiUtils.isMiui() && !MiuiUtils.isMiuiOptimizationDisabled() && downloadEntity.format == Constants.XAPK_APKS_FORMAT) {// 小米手机开启miui以后,需要引导用户关闭miui优化
|
||||
DialogHelper.showMiuiOptimizationWarning(
|
||||
context,
|
||||
downloadEntity,
|
||||
onHintClick = {
|
||||
val guides = Config.getNewApiSettingsEntity()?.install
|
||||
val miuiOptimizationGuide = guides?.guides?.findLast {
|
||||
it.type == GUIDE_TYPE_MIUI_OPTIMIZATION
|
||||
}
|
||||
if (miuiOptimizationGuide != null) {
|
||||
DirectUtils.directToLinkPage(
|
||||
context,
|
||||
miuiOptimizationGuide.link,
|
||||
MIUI_OPTIMIZATION_WARNING_DIALOG_ENTRANCE,
|
||||
""
|
||||
)
|
||||
}
|
||||
},
|
||||
onConfirmClick = {
|
||||
if (SystemUtils.isDevelopmentSettingsEnabled(context)) {
|
||||
context.startActivity(
|
||||
Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
it.markDismissByTouchInside()
|
||||
it.dismiss()
|
||||
} else {
|
||||
ToastUtils.showToast(context.getString(R.string.miui_open_adb_hint))
|
||||
}
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
val isXapkApks = isXapkApks(downloadEntity)
|
||||
|
||||
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(context) {
|
||||
if (isXapkApks && MiuiUtils.isMiui() && !MiuiUtils.isMiuiOptimizationDisabled()) {
|
||||
// 小米手机开启miui以后,需要引导用户关闭miui优化
|
||||
DialogHelper.showMiuiOptimizationWarning(
|
||||
context,
|
||||
downloadEntity,
|
||||
onHintClick = {
|
||||
val guides = Config.getNewApiSettingsEntity()?.install
|
||||
val miuiOptimizationGuide = guides?.guides?.findLast {
|
||||
it.type == GUIDE_TYPE_MIUI_OPTIMIZATION
|
||||
}
|
||||
if (miuiOptimizationGuide != null) {
|
||||
DirectUtils.directToLinkPage(
|
||||
context,
|
||||
miuiOptimizationGuide.link,
|
||||
MIUI_OPTIMIZATION_WARNING_DIALOG_ENTRANCE,
|
||||
""
|
||||
)
|
||||
}
|
||||
},
|
||||
onConfirmClick = {
|
||||
if (SystemUtils.isDevelopmentSettingsEnabled(context)) {
|
||||
context.startActivity(
|
||||
Intent(Settings.ACTION_APPLICATION_DEVELOPMENT_SETTINGS)
|
||||
.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
|
||||
)
|
||||
it.markDismissByTouchInside()
|
||||
it.dismiss()
|
||||
} else {
|
||||
ToastUtils.showToast(context.getString(R.string.miui_open_adb_hint))
|
||||
}
|
||||
}
|
||||
)
|
||||
return
|
||||
}
|
||||
|
||||
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(context) {
|
||||
val unzipAction = {
|
||||
DownloadManager.getInstance().getDownloadEntitySnapshot(downloadEntity.url, downloadEntity.gameId)
|
||||
?.let {
|
||||
unzipXapkFile(it)
|
||||
unzipXapkFile(it, isXapkApks)
|
||||
if (showUnzipToast) {
|
||||
Utils.toast(mContext, "解压过程请勿退出光环助手!")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
throwExceptionInDebug("如果是Apk包请使用PackageInstaller进行安装")
|
||||
PackageInstaller.install(mContext, downloadEntity)
|
||||
|
||||
// XAPK (apks) 格式,不在乎 obb 文件夹是否可读
|
||||
if (isXapkApks) {
|
||||
unzipAction.invoke()
|
||||
return@checkManageAllFilesOrStoragePermissionBeforeAction
|
||||
}
|
||||
|
||||
// 以 file 的方式访问 obb 文件夹是否可行
|
||||
val isFileStyleObbFolderReadable =
|
||||
if (systemHasFlaw) {
|
||||
File(Environment.getExternalStorageDirectory().path, "\u200bAndroid/obb").list() != null
|
||||
} else {
|
||||
File(Environment.getExternalStorageDirectory().path, "Android/obb").list() != null
|
||||
}
|
||||
|
||||
// 如果是文件夹风格的 obb 文件夹可读,或当前上下文不是 AppCompatActivity,或当前上下文已经被销毁,则直接解压
|
||||
if (isFileStyleObbFolderReadable
|
||||
|| context !is AppCompatActivity
|
||||
|| context.isFinishing
|
||||
) {
|
||||
unzipAction.invoke()
|
||||
} else {
|
||||
unzipWithCulpritHandled(context, downloadEntity.url, unzipAction)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unzipXapkFile(downloadEntity: DownloadEntity) {
|
||||
mXApkUnZipper.unzip(
|
||||
XApkUnZipEntry(
|
||||
downloadEntity.path,
|
||||
File(downloadEntity.path)
|
||||
)
|
||||
)
|
||||
/**
|
||||
* 兜底解压方案,适用于一些奇葩系统
|
||||
*/
|
||||
private fun unzipWithCulpritHandled(activity: AppCompatActivity, xApkUrl: String, unzipAction: () -> Unit?) {
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.R
|
||||
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.S
|
||||
) {
|
||||
val isDocStyleObbFolderReadable = isDocStyleObbFolderReadable(activity)
|
||||
|
||||
if (isDocStyleObbFolderReadable) {
|
||||
useDocStyleToUnzip = true
|
||||
unzipAction.invoke()
|
||||
} else {
|
||||
DialogHelper.showDialog(
|
||||
context = activity,
|
||||
title = "安装提示",
|
||||
content = "为了安装 XAPK 文件,请授予 OBB 文件夹的访问权限",
|
||||
confirmText = "确定",
|
||||
cancelText = "取消",
|
||||
confirmClickCallback = {
|
||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
|
||||
intent.setFlags(
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION
|
||||
and Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
and Intent.FLAG_GRANT_PERSISTABLE_URI_PERMISSION
|
||||
and Intent.FLAG_GRANT_PREFIX_URI_PERMISSION
|
||||
)
|
||||
|
||||
val obbUri = OBBDocOutput.ANDROID_OBB_DOC_STYLE_URI.toUri()
|
||||
val df = DocumentFile.fromTreeUri(activity, obbUri)
|
||||
if (df != null) {
|
||||
intent.putExtra(DocumentsContract.EXTRA_INITIAL_URI, df.uri)
|
||||
}
|
||||
|
||||
tempLauncher =
|
||||
activity.registerActivityResultLauncher(ActivityResultContracts.OpenDocumentTree()) { uri ->
|
||||
if (uri != null) {
|
||||
activity.contentResolver.takePersistableUriPermission(
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION
|
||||
)
|
||||
|
||||
useDocStyleToUnzip = true
|
||||
unzipAction.invoke()
|
||||
}
|
||||
|
||||
tempLauncher?.unregister()
|
||||
}
|
||||
|
||||
tempLauncher?.launch(obbUri)
|
||||
},
|
||||
cancelClickCallback = {
|
||||
unzipAction.invoke()
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(activity) {
|
||||
// 设备大于 12,需要重启才能生效 (是的,我们又重启了!)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
// 记录应用重启前需要重解压的信息
|
||||
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, activity.javaClass.name)
|
||||
SPUtils.setString(Constants.SP_XAPK_URL, xApkUrl)
|
||||
|
||||
val pm = activity.packageManager
|
||||
val intent = pm.getLaunchIntentForPackage(activity.packageName)
|
||||
val mainIntent = Intent.makeRestartActivityTask(intent!!.component)
|
||||
activity.startActivity(mainIntent)
|
||||
Runtime.getRuntime().exit(0)
|
||||
} else {
|
||||
unzipAction.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun unzipXapkFile(downloadEntity: DownloadEntity, isXapkApks: Boolean) {
|
||||
XApkUnZipEntry(downloadEntity.path, File(downloadEntity.path), isXapkApks).let(mXApkUnZipper::unzip)
|
||||
mDownloadEntityMap[downloadEntity.path] = downloadEntity
|
||||
}
|
||||
|
||||
@ -191,21 +312,14 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
DownloadNotificationHelper.addOrUpdateDownloadNotification(downloadEntity)
|
||||
NDataChanger.notifyDataChanged(downloadEntity)
|
||||
DownloadManager.getInstance().updateDownloadEntity(downloadEntity)
|
||||
// 仅官网渠道上报 XAPK 异常信息
|
||||
if (HaloApp.getInstance().channel == "GH_206") {
|
||||
SentryHelper.onEvent(
|
||||
"XAPK_UNZIP_ERROR",
|
||||
"gameName", downloadEntity.name,
|
||||
"errorDigest", exception.localizedMessage
|
||||
)
|
||||
}
|
||||
|
||||
DownloadDataHelper.uploadDownloadStatusEvent(downloadEntity, "xapk解压失败")
|
||||
|
||||
SensorsBridge.trackGameDecompressionFailed(
|
||||
downloadEntity.gameId,
|
||||
downloadEntity.name,
|
||||
downloadEntity.categoryChinese
|
||||
downloadEntity.categoryChinese,
|
||||
exception.localizedMessage ?: "unknown error"
|
||||
)
|
||||
}
|
||||
|
||||
@ -324,7 +438,7 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
}
|
||||
|
||||
override fun onCreateOBBOutput(apk: XApkFile): XApkFileOutput<Unit> {
|
||||
return OBBFileOutput()
|
||||
return if (useDocStyleToUnzip) OBBDocOutput(mContext) else OBBFileOutput()
|
||||
}
|
||||
|
||||
override fun onCreateApkOutput(apk: XApkFile): XApkFileOutput<IPackageInstaller> {
|
||||
@ -340,6 +454,16 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否能以 DocumentUi 的方式访问 obb 文件夹
|
||||
*/
|
||||
private fun isDocStyleObbFolderReadable(context: Context): Boolean {
|
||||
return context
|
||||
.contentResolver
|
||||
.persistedUriPermissions
|
||||
.any { it.uri == OBBDocOutput.ANDROID_OBB_URI.toUri() }
|
||||
}
|
||||
|
||||
private class SplitApksInstaller(
|
||||
private val xApkFile: XApkFile,
|
||||
private val sessionId: Int,
|
||||
@ -351,8 +475,16 @@ object XapkInstaller : XApkUnZipCallback, XApkUnZipOutputFactory {
|
||||
|
||||
mPendingSessionInfoMap[downloadEntity.path] = XapkPendingSessionInfo(downloadEntity.path, sessionId)
|
||||
AppExecutor.ioExecutor.execute {// 有可能卡顿造成anr
|
||||
PackageInstaller.installMultiple(applicationContext, downloadEntity.packageName, downloadEntity.path, sessionId)
|
||||
NDataChanger.notifyDataChanged(downloadEntity)
|
||||
try {
|
||||
PackageInstaller.installMultiple(
|
||||
applicationContext,
|
||||
downloadEntity.packageName,
|
||||
downloadEntity.path,
|
||||
sessionId
|
||||
)
|
||||
NDataChanger.notifyDataChanged(downloadEntity)
|
||||
} catch (ignore: Exception) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,243 +0,0 @@
|
||||
package com.gh.common.xapk
|
||||
|
||||
import android.os.Build
|
||||
import android.os.Environment
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.MD5Utils
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import net.lingala.zip4j.progress.ProgressMonitor
|
||||
import java.io.File
|
||||
import java.util.zip.ZipFile
|
||||
|
||||
|
||||
class XapkUnzipThread(
|
||||
private var mDownloadEntity: DownloadEntity,
|
||||
private var mUnzipListener: IXapkUnzipListener
|
||||
) : Thread() {
|
||||
|
||||
private val mDefaultBufferSize = 1024 * 1024
|
||||
|
||||
var canceled = false
|
||||
|
||||
override fun run() {
|
||||
super.run()
|
||||
try {
|
||||
val path = mDownloadEntity.path
|
||||
var unzipSize = 0L
|
||||
try {
|
||||
unzipSize = getUnzipSize(path)
|
||||
} catch (e: Exception) {
|
||||
planB()
|
||||
return
|
||||
}
|
||||
|
||||
val msg = FileUtils.isCanDownload(HaloApp.getInstance().application, unzipSize)
|
||||
if (!msg.isNullOrEmpty()) {
|
||||
// 空间不足应该不用刷新页面,保持不变就好
|
||||
Utils.toast(HaloApp.getInstance().application, "设备存储空间不足,请清理后重试!")
|
||||
mUnzipListener.onCancel(mDownloadEntity)
|
||||
return
|
||||
}
|
||||
var unzipProgress = 0L
|
||||
val absolutePath = Environment.getExternalStorageDirectory().absolutePath
|
||||
val xapkFile = File(path)
|
||||
|
||||
val unzipClosure: (zip: ZipFile) -> Unit = { zip ->
|
||||
for (zipEntry in zip.entries().asSequence()) {
|
||||
val outputFile = if (zipEntry.name.getExtension() == XapkInstaller.XAPK_DATA_EXTENSION_NAME) {
|
||||
File(absolutePath + File.separator + zipEntry.name)
|
||||
} else if (zipEntry.name.getExtension() == XapkInstaller.PACKAGE_EXTENSION_NAME) {
|
||||
// apk文件名称 = xapk文件名 + MD5(本身的文件名称) + ".apk"
|
||||
// 如 abc_com.gh.gamecenter.apk 这样的文件名,在使用浏览器安装时系统浏览器可能会抹掉文件类型,导致无法唤起下载完自动安装,这里去掉多个 "." 号,保证浏览器安装正常使用
|
||||
val fileName = xapkFile.nameWithoutExtension + "_" + MD5Utils.getContentMD5(zipEntry.name) + "." + XapkInstaller.PACKAGE_EXTENSION_NAME
|
||||
File(FileUtils.getDownloadPath(HaloApp.getInstance().application, fileName))
|
||||
} else continue // 暂时只需要解压xpk/obb文件
|
||||
|
||||
if (zipEntry.isDirectory) {
|
||||
if (!outputFile.exists()) {
|
||||
throwException("unzip create file path failure", !outputFile.mkdirs())
|
||||
}
|
||||
continue
|
||||
}
|
||||
|
||||
if (!outputFile.parentFile.exists()) {
|
||||
throwException("unzip create file path failure", !outputFile.parentFile.mkdirs())
|
||||
}
|
||||
|
||||
// 如果存在同名且大小一致,可以认为该文件已经解压缩(在不解压缩的情况下如果如果获取压缩文件的MD5????)
|
||||
if (!outputFile.exists()) {
|
||||
throwException("unzip create file failure", !outputFile.createNewFile())
|
||||
} else if (outputFile.length() != zipEntry.size) {
|
||||
throwException("unzip delete existing file failure", !outputFile.delete())
|
||||
throwException("unzip create file failure", !outputFile.createNewFile())
|
||||
} else {
|
||||
unzipProgress += zipEntry.size
|
||||
mUnzipListener.onProgress(mDownloadEntity, outputFile.path, unzipSize, unzipProgress)
|
||||
mUnzipListener.onNext(mDownloadEntity, outputFile.path)
|
||||
continue
|
||||
}
|
||||
|
||||
// unzip
|
||||
zip.getInputStream(zipEntry).use { input ->
|
||||
outputFile.outputStream().use { output ->
|
||||
val buffer = ByteArray(mDefaultBufferSize)
|
||||
var bytes = input.read(buffer)
|
||||
while (bytes >= 0) {
|
||||
output.write(buffer, 0, bytes)
|
||||
unzipProgress += bytes
|
||||
bytes = input.read(buffer)
|
||||
if (canceled) {
|
||||
mUnzipListener.onCancel(mDownloadEntity)
|
||||
return@use
|
||||
} else {
|
||||
// 防止多次短时间内多次触发onProgress方法导致阻塞主线程(低端机会出现十分明显的卡顿)
|
||||
debounceActionWithInterval(-1, 500) {
|
||||
mUnzipListener.onProgress(
|
||||
mDownloadEntity,
|
||||
outputFile.path,
|
||||
unzipSize,
|
||||
unzipProgress
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
mUnzipListener.onNext(mDownloadEntity, outputFile.path)
|
||||
}
|
||||
}
|
||||
|
||||
// Kotlin 1.4.X 在安卓 4.4 以下使用 use 默认关闭 ZipFile 的流时会触发
|
||||
// java.lang.IncompatibleClassChangeError: interface not implemented 的 Error (Throwable)
|
||||
// 但实测是不影响解压的,所以这里换用 let 不关闭流,确保不闪退,并且不影响解压结果
|
||||
// 帮用户解压了,但游戏能不能安装就看天吧 (毕竟支持4.X的游戏也不多了)
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
ZipFile(File(path)).use { unzipClosure.invoke(it) }
|
||||
} else {
|
||||
ZipFile(File(path)).let { unzipClosure.invoke(it) }
|
||||
}
|
||||
mUnzipListener.onSuccess(mDownloadEntity)
|
||||
} catch (e: Exception) {
|
||||
if (BuildConfig.DEBUG) throw e
|
||||
mUnzipListener.onFailure(mDownloadEntity, e)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 部分未知情况(有可能是项目配置问题,有可能时Zip包破损)原生的ZipFile无法打开压缩包,则启动以下备用方案
|
||||
* 具体复现机型:小米 9(Android 9)
|
||||
* 具体复现的压缩包:太空竞速2 具体可以咨询陈思雨
|
||||
*
|
||||
* 实现方式请参考:https://github.com/srikanth-lingala/zip4j
|
||||
* 注意使用该库的ZipInputStream依然无法解压,提示头文件丢失
|
||||
*
|
||||
* 后续如果有需要可以直接使用该方法进行解压
|
||||
*/
|
||||
private fun planB() {
|
||||
val zipPath = mDownloadEntity.path
|
||||
|
||||
val absolutePath = Environment.getExternalStorageDirectory().absolutePath
|
||||
|
||||
val zipFile = net.lingala.zip4j.ZipFile(zipPath)
|
||||
val progressMonitor = zipFile.progressMonitor
|
||||
zipFile.isRunInThread = true
|
||||
|
||||
val fileHeaders = zipFile.fileHeaders
|
||||
var unzipSize = 0L
|
||||
for (fileHeader in fileHeaders) {
|
||||
unzipSize += fileHeader.uncompressedSize;
|
||||
}
|
||||
|
||||
var unzipProgress = 0L
|
||||
var completedSize = 0L
|
||||
|
||||
for (fileHeader in fileHeaders) {
|
||||
if (canceled) {
|
||||
mUnzipListener.onCancel(mDownloadEntity)
|
||||
return
|
||||
}
|
||||
|
||||
// 暂时只需要解压xpk/obb文件
|
||||
val extension = fileHeader.fileName.getExtension()
|
||||
if (extension != XapkInstaller.XAPK_DATA_EXTENSION_NAME && extension != XapkInstaller.PACKAGE_EXTENSION_NAME) continue
|
||||
|
||||
|
||||
var unzipPath = ""
|
||||
if (extension == XapkInstaller.XAPK_DATA_EXTENSION_NAME) {
|
||||
unzipPath = absolutePath + File.separator + fileHeader.fileName
|
||||
|
||||
if (hasUnzipFile(unzipPath, fileHeader.uncompressedSize)) {
|
||||
mUnzipListener.onNext(mDownloadEntity, unzipPath)
|
||||
continue
|
||||
}
|
||||
|
||||
zipFile.extractFile(fileHeader.fileName, absolutePath)
|
||||
}
|
||||
|
||||
if (extension == XapkInstaller.PACKAGE_EXTENSION_NAME) {
|
||||
val downloadDir = FileUtils.getDownloadDir(HaloApp.getInstance().application)
|
||||
val unzipFileName = File(zipPath).nameWithoutExtension + "_" + fileHeader.fileName
|
||||
unzipPath = downloadDir + File.separator + unzipFileName
|
||||
|
||||
if (hasUnzipFile(unzipPath, fileHeader.uncompressedSize)) {
|
||||
mUnzipListener.onNext(mDownloadEntity, unzipPath)
|
||||
continue
|
||||
}
|
||||
|
||||
zipFile.extractFile(fileHeader.fileName, downloadDir, unzipFileName)
|
||||
}
|
||||
|
||||
throwExceptionInDebug("check unzipPath", unzipPath.isEmpty())
|
||||
|
||||
// 回调太频繁了,变态吗? 4K回调一次,还不能改,FUCK
|
||||
var filterCounter = 0
|
||||
val filterInterval = 1024 * 10
|
||||
while (progressMonitor.state != ProgressMonitor.State.READY) {
|
||||
filterCounter++
|
||||
if (filterCounter % filterInterval == 0) {
|
||||
unzipProgress = completedSize + progressMonitor.workCompleted
|
||||
mUnzipListener.onProgress(mDownloadEntity, unzipPath, unzipSize, unzipProgress)
|
||||
|
||||
if (canceled) {
|
||||
progressMonitor.isCancelAllTasks = true
|
||||
mUnzipListener.onCancel(mDownloadEntity)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
completedSize += fileHeader.uncompressedSize
|
||||
mUnzipListener.onNext(mDownloadEntity, unzipPath)
|
||||
}
|
||||
|
||||
mUnzipListener.onSuccess(mDownloadEntity)
|
||||
}
|
||||
|
||||
private fun hasUnzipFile(unzipPath: String, uncompressedSize: Long): Boolean {
|
||||
val unzipFile = File(unzipPath)
|
||||
if (unzipFile.exists() || unzipFile.length() == uncompressedSize) return true
|
||||
return false
|
||||
}
|
||||
|
||||
private fun getUnzipSize(path: String): Long {
|
||||
var totalSize = 0L
|
||||
|
||||
val calculateSizeClosure: (zip: ZipFile) -> Unit = { zip ->
|
||||
for (entry in zip.entries()) {
|
||||
totalSize += entry.size
|
||||
}
|
||||
}
|
||||
|
||||
// Kotlin 1.4.X 在安卓 4.4 以下使用 use 默认ZipFile 的流时会触发
|
||||
// java.lang.IncompatibleClassChangeError: interface not implemented 的 Error (Throwable)
|
||||
// 实测是不影响解压,所以这里换用 let 不关闭流,确保不闪退
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
|
||||
ZipFile(File(path)).use { calculateSizeClosure.invoke(it) }
|
||||
} else {
|
||||
ZipFile(File(path)).let { calculateSizeClosure.invoke(it) }
|
||||
}
|
||||
return totalSize
|
||||
}
|
||||
}
|
||||
@ -17,15 +17,6 @@ import androidx.collection.ArrayMap;
|
||||
import androidx.lifecycle.LiveData;
|
||||
import androidx.lifecycle.MutableLiveData;
|
||||
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.core.GHThreadFactory;
|
||||
import com.gh.gamecenter.feature.entity.TagStyleEntity;
|
||||
import com.gh.gamecenter.feature.entity.CustomPageTrackData;
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent;
|
||||
import com.gh.common.exposure.ExposureUtils;
|
||||
import com.gh.common.history.HistoryHelper;
|
||||
import com.gh.common.simulator.SimulatorGameManager;
|
||||
@ -36,10 +27,17 @@ import com.gh.common.util.LunchType;
|
||||
import com.gh.common.util.PackageInstaller;
|
||||
import com.gh.common.util.PackageUtils;
|
||||
import com.gh.gamecenter.BuildConfig;
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager;
|
||||
import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.entity.LinkEntity;
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.common.utils.DeviceUtils;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.FileUtils;
|
||||
import com.gh.gamecenter.common.utils.NetworkUtils;
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.gamecenter.core.GHThreadFactory;
|
||||
import com.gh.gamecenter.core.utils.GsonUtils;
|
||||
import com.gh.gamecenter.core.utils.PageSwitchDataHelper;
|
||||
import com.gh.gamecenter.core.utils.SPUtils;
|
||||
@ -48,8 +46,11 @@ import com.gh.gamecenter.entity.GameUpdateEntity;
|
||||
import com.gh.gamecenter.entity.HomePluggableFilterEntity;
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus;
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity;
|
||||
import com.gh.gamecenter.feature.entity.CustomPageTrackData;
|
||||
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.exposure.ExposureEvent;
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper;
|
||||
import com.gh.gamecenter.login.user.UserManager;
|
||||
import com.gh.gamecenter.manager.PackagesManager;
|
||||
@ -360,6 +361,17 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, VHelper.KEY_REQUIRED_G_APPS, gameEntity.getGAppsSwitch());
|
||||
}
|
||||
|
||||
if (gameEntity.isDspGame()) {
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DSP_GAME, "true");
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SEARCH_KEY, gameEntity.getSearchKey());
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SUBJECT_NAME, gameEntity.getSubjectName());
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.SUBJECT_ID, gameEntity.getSubjectId());
|
||||
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.INSTALL_URL, gameEntity.getInstallUrl());
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DOWNLOAD_URL, gameEntity.getDownloadUrl());
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.DSP_AD_ID, gameEntity.getDspAdId());
|
||||
}
|
||||
|
||||
ExtensionsKt.addMetaExtra(downloadEntity, Constants.KEY_BIT, apkEntity.getBit());
|
||||
|
||||
// 记录是否为双下载按钮模式
|
||||
@ -451,8 +463,15 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
trackDownloadType = "本地下载";
|
||||
}
|
||||
|
||||
String pushMessageId = HaloApp.get(Constants.PUSH_MESSAGE_ID, false) instanceof String
|
||||
? (String) HaloApp.get(Constants.PUSH_MESSAGE_ID, false)
|
||||
: "";
|
||||
String pushLinkId = HaloApp.get(Constants.PUSH_LINK_ENTITY, false) instanceof LinkEntity
|
||||
? ((LinkEntity) HaloApp.get(Constants.PUSH_LINK_ENTITY, false)).getLink()
|
||||
: "";
|
||||
boolean isFromPush = !pushMessageId.isEmpty();
|
||||
|
||||
String[] arrayKv = {
|
||||
Object[] arrayKv = {
|
||||
"game_id", gameEntity.getId(),
|
||||
"game_name", gameEntity.getName(),
|
||||
"game_type", gameEntity.getCategoryChinese(),
|
||||
@ -465,17 +484,49 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
"last_page_id", GlobalActivityManager.getLastPageEntity().getPageId(),
|
||||
"last_page_business_id", GlobalActivityManager.getLastPageEntity().getPageBusinessId(),
|
||||
"download_status", gameEntity.getDownloadStatusChinese(),
|
||||
"download_type", trackDownloadType
|
||||
"download_type", trackDownloadType,
|
||||
"is_from_push_notifications", isFromPush,
|
||||
"message_id", pushMessageId,
|
||||
"link_id", pushLinkId,
|
||||
};
|
||||
|
||||
List<String> kvs = new ArrayList<>(Arrays.asList(arrayKv));
|
||||
List<Object> kvs = new ArrayList<>(Arrays.asList(arrayKv));
|
||||
|
||||
if (customPageTrackData != null) {
|
||||
kvs.addAll(Arrays.asList(customPageTrackData.toKV()));
|
||||
}
|
||||
|
||||
SensorsBridge.trackEventWithExposureSource("DownloadProcessBegin",
|
||||
downloadExposureEvent.getSource(), kvs.toArray(new String[0])
|
||||
);
|
||||
if (!gameEntity.isDspGame()) {
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DownloadProcessBegin",
|
||||
downloadExposureEvent.getSource(), kvs.toArray(new Object[0])
|
||||
);
|
||||
} else {
|
||||
String searchKey = gameEntity.getSearchKey();
|
||||
String loc;
|
||||
|
||||
if (TextUtils.isEmpty(searchKey)) {
|
||||
loc = "自定义页面";
|
||||
} else {
|
||||
loc = "游戏搜索结果列表";
|
||||
}
|
||||
|
||||
kvs.add("ad_id");
|
||||
kvs.add(gameEntity.getDspAdId());
|
||||
kvs.add("search_content");
|
||||
kvs.add(searchKey);
|
||||
kvs.add("game_column_name");
|
||||
kvs.add(gameEntity.getSubjectName());
|
||||
kvs.add("game_column_id");
|
||||
kvs.add(gameEntity.getSubjectId());
|
||||
kvs.add("location");
|
||||
kvs.add(loc);
|
||||
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DspAdDownloadBegin",
|
||||
downloadExposureEvent.getSource(), kvs.toArray(new Object[0])
|
||||
);
|
||||
}
|
||||
|
||||
//TODO remove
|
||||
DownloadManager.getInstance().putStatus(downloadEntity.getUrl(), DownloadStatus.downloading);
|
||||
@ -1296,7 +1347,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
getInstance().packageExecutor.execute(() -> {
|
||||
boolean markHasChanged = false;
|
||||
|
||||
ArrayList<GameUpdateEntity> updates = PackageRepository.INSTANCE.getGameUpdate();
|
||||
List<GameUpdateEntity> updates = PackageRepository.INSTANCE.getGameUpdate();
|
||||
for (GameUpdateEntity update : updates) {
|
||||
if (update == null) continue;
|
||||
String mark = update.getId() + update.getPackageName();
|
||||
@ -1317,7 +1368,7 @@ public class DownloadManager implements DownloadStatusListener {
|
||||
* 将可用更新标记为已读的事件
|
||||
*/
|
||||
public void saveUpdateMarkToStorage() {
|
||||
ArrayList<GameUpdateEntity> updates = PackageRepository.INSTANCE.getGameUpdate();
|
||||
List<GameUpdateEntity> updates = PackageRepository.INSTANCE.getGameUpdate();
|
||||
if (updates.size() == mUpdateMarks.size()) {
|
||||
SPUtils.setStringSet(UPDATE_IS_READ_MARK, mUpdateMarks);
|
||||
return;
|
||||
|
||||
@ -175,7 +175,9 @@ object PackageObserver {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameId = mDownloadEntity.gameId,
|
||||
gameName = mDownloadEntity.name,
|
||||
action = "自动安装"
|
||||
action = "自动安装",
|
||||
isDspGame = mDownloadEntity.getMetaExtra(Constants.DSP_GAME) == "true",
|
||||
dspAdId = mDownloadEntity.getMetaExtra(Constants.DSP_AD_ID)
|
||||
)
|
||||
PackageInstaller.install(application, mDownloadEntity)
|
||||
}
|
||||
|
||||
@ -15,7 +15,8 @@ import androidx.lifecycle.Observer
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import androidx.recyclerview.widget.*
|
||||
import androidx.recyclerview.widget.RecyclerView.SmoothScroller
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.R
|
||||
@ -30,9 +31,9 @@ import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.TimeElapsedHelper
|
||||
import com.gh.gamecenter.databinding.DialogDownloadBinding
|
||||
import com.gh.gamecenter.entity.GamePlatform
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DataWatcher
|
||||
@ -70,6 +71,8 @@ class DownloadDialog : BaseDraggableDialogFragment() {
|
||||
private var mEntrance: String = "" // 入口位置
|
||||
private var mLocation: String = "" // 最终位置
|
||||
|
||||
private var onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
|
||||
|
||||
private val mDataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
@ -122,7 +125,7 @@ class DownloadDialog : BaseDraggableDialogFragment() {
|
||||
}
|
||||
mViewModel.listLiveData.observeNonNull(this, callback = { itemList ->
|
||||
mAdapter =
|
||||
DownloadDialogAdapter(requireContext(), mViewModel, itemList, false, mTraceEvent, mEntrance, mLocation)
|
||||
DownloadDialogAdapter(requireContext(), mViewModel, itemList, false, mTraceEvent, mEntrance, mLocation, onDownloadClickAction)
|
||||
mBinding.contentList.layoutManager = createLayoutManager(itemList)
|
||||
mBinding.contentList.adapter = mAdapter
|
||||
performAutoDownload(itemList, mBinding.contentList)
|
||||
@ -161,7 +164,8 @@ class DownloadDialog : BaseDraggableDialogFragment() {
|
||||
true,
|
||||
mTraceEvent,
|
||||
mEntrance,
|
||||
mLocation
|
||||
mLocation,
|
||||
onDownloadClickAction
|
||||
)
|
||||
mBinding.collectionList.layoutManager = createLayoutManager(itemList)
|
||||
mBinding.collectionList.adapter = mCollectionAdapter
|
||||
@ -426,7 +430,8 @@ class DownloadDialog : BaseDraggableDialogFragment() {
|
||||
gameEntity: GameEntity,
|
||||
traceEvent: ExposureEvent?,
|
||||
entrance: String?,
|
||||
location: String?
|
||||
location: String?,
|
||||
onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
) {
|
||||
val fragmentActivity: FragmentActivity = if (context is FragmentActivity) {
|
||||
context
|
||||
@ -452,6 +457,7 @@ class DownloadDialog : BaseDraggableDialogFragment() {
|
||||
bundle.putParcelable(EntranceConsts.KEY_TRACE_EVENT, traceEvent)
|
||||
arguments = bundle
|
||||
}
|
||||
downloadDialog.onDownloadClickAction = onDownloadClickAction
|
||||
downloadDialog.show(fragmentActivity.supportFragmentManager, DownloadDialog::class.java.name)
|
||||
}
|
||||
|
||||
|
||||
@ -24,7 +24,8 @@ class DownloadDialogAdapter(
|
||||
val isCollectionPage: Boolean,
|
||||
private val mTraceEvent: ExposureEvent?,
|
||||
private val mEntrance: String,
|
||||
private val mLocation: String
|
||||
private val mLocation: String,
|
||||
private val onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
) : BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private val mPath = if (isCollectionPage) {
|
||||
@ -170,7 +171,8 @@ class DownloadDialogAdapter(
|
||||
mTraceEvent,
|
||||
mEntrance,
|
||||
mPath,
|
||||
mLocation
|
||||
mLocation,
|
||||
onDownloadClickAction
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,7 +41,8 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
|
||||
traceEvent: ExposureEvent?,
|
||||
entrance: String,
|
||||
path: String,
|
||||
location: String
|
||||
location: String,
|
||||
onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
) {
|
||||
|
||||
val apkEntity = listData[position].normal!!
|
||||
@ -181,7 +182,7 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
|
||||
}
|
||||
}
|
||||
|
||||
setDownloadClickListener(itemView, apkEntity, viewModel, traceEvent, entrance, path, location)
|
||||
setDownloadClickListener(itemView, apkEntity, viewModel, traceEvent, entrance, path, location, onDownloadClickAction)
|
||||
}
|
||||
|
||||
private fun changeRecommendUI(apkEntity: ApkEntity, listData: List<DownloadDialogItemData>, position: Int) {
|
||||
@ -226,7 +227,8 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
|
||||
traceEvent: ExposureEvent?,
|
||||
entrance: String,
|
||||
path: String,
|
||||
location: String
|
||||
location: String,
|
||||
onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
) {
|
||||
|
||||
val gameEntity = viewModel.gameEntity
|
||||
@ -234,6 +236,7 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
|
||||
when (itemView.getTag(DownloadDialogAdapter.ITEM_TAG_KEY)) {
|
||||
DownloadDialogItemStatus.DOWNLOAD -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, traceEvent, entrance, location)
|
||||
onDownloadClickAction?.invoke(false)
|
||||
}
|
||||
DownloadDialogItemStatus.LAUNCH -> {
|
||||
PackageLauncher.launchApp(it.context, gameEntity, apkEntity.packageName)
|
||||
@ -291,6 +294,7 @@ class DownloadDialogItemViewHolder(val binding: DownloadDialogItemBinding) : Bas
|
||||
}
|
||||
DownloadDialogItemStatus.UPDATE -> {
|
||||
createDownloadTask(it.context, apkEntity, gameEntity, traceEvent, entrance, location)
|
||||
onDownloadClickAction?.invoke(true)
|
||||
}
|
||||
DownloadDialogItemStatus.COLLECTION -> {
|
||||
val apkCollection = apkEntity.apkCollection
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.download.server
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.util.Base64
|
||||
import androidx.annotation.WorkerThread
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.LogUtils
|
||||
@ -246,6 +247,19 @@ object BrowserInstallHelper {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 初始化是否满足开启浏览器安装的条件(后续会使用缓存来判断)
|
||||
* @param settingsEntity 服务器返回的配置
|
||||
*
|
||||
* 因为可能需要查询已安装的应用,所以需要在子线程中调用
|
||||
*/
|
||||
@WorkerThread
|
||||
fun initIfConditionMatched(settingsEntity: NewSettingsEntity?) {
|
||||
settingsEntity?.let {
|
||||
isConditionMatched(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否满足开启浏览器安装的条件
|
||||
*/
|
||||
|
||||
@ -5,15 +5,10 @@ import android.content.ContextWrapper
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.therouter.router.Autowired
|
||||
import com.therouter.router.Route
|
||||
import com.therouter.TheRouter
|
||||
import com.gh.base.DownloadToolbarActivity
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.exposure.ExposureTraceUtils.appendTrace
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity.NORMAL_FRAGMENT_BUNDLE
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity.NORMAL_FRAGMENT_NAME
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
@ -24,7 +19,11 @@ import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.gh.gamecenter.gamedetail.GameDetailWrapperFragment
|
||||
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
|
||||
import com.therouter.TheRouter
|
||||
import com.therouter.router.Autowired
|
||||
import com.therouter.router.Route
|
||||
|
||||
@Route(
|
||||
path = RouteConsts.activity.gameDetailActivity,
|
||||
@ -74,11 +73,11 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
generateDataFromRoute()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
DisplayUtils.transparentStatusBar(this)
|
||||
DisplayUtils.setStatusBarColor(this, com.gh.gamecenter.common.R.color.transparent, !mIsDarkModeOn)
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, GameDetailActivity::class.java, GameDetailFragment::class.java)
|
||||
return getTargetIntent(this, GameDetailActivity::class.java, GameDetailWrapperFragment::class.java)
|
||||
}
|
||||
|
||||
override fun getLayoutId() = R.layout.activity_game_detail
|
||||
@ -115,7 +114,6 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
view,
|
||||
listOf(
|
||||
R.id.menu_download_iv,
|
||||
R.id.gameBigEvent,
|
||||
R.id.cardContainer,
|
||||
R.id.iv_reserve,
|
||||
R.id.iv_concern,
|
||||
@ -127,10 +125,15 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
private fun generateDataFromRoute() {
|
||||
val bundle = intent.extras
|
||||
|
||||
intent?.putExtra(NORMAL_FRAGMENT_NAME, GameDetailFragment::class.java.canonicalName)
|
||||
intent?.putExtra(NORMAL_FRAGMENT_NAME, GameDetailWrapperFragment::class.java.canonicalName)
|
||||
intent?.putExtra(NORMAL_FRAGMENT_BUNDLE, bundle)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
DisplayUtils.setStatusBarColor(this, com.gh.gamecenter.common.R.color.transparent, !mIsDarkModeOn)
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
@JvmStatic
|
||||
@ -190,12 +193,12 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
}
|
||||
|
||||
if (scrollToLibao) {
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
bundle.putBoolean(EntranceConsts.KEY_SCROLL_TO_LIBAO, true)
|
||||
}
|
||||
|
||||
if (scrollToServer) {
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
bundle.putBoolean(EntranceConsts.KEY_SCROLL_TO_SERVER, true)
|
||||
}
|
||||
|
||||
@ -302,7 +305,7 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
}
|
||||
if (openVideoStreaming) {
|
||||
bundle.putBoolean(EntranceConsts.KEY_OPEN_VIDEO_STREAMING, true)
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
}
|
||||
if (openPlatformWindow) {
|
||||
bundle.putBoolean(EntranceConsts.KEY_OPEN_PLATFORM_WINDOW, true)
|
||||
@ -316,7 +319,7 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
}
|
||||
}
|
||||
if (scrollToLibao) {
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, EntranceConsts.TAB_TYPE_DESC)
|
||||
bundle.putString(EntranceConsts.KEY_TARGET, GameDetailTabEntity.TYPE_DETAIL)
|
||||
bundle.putBoolean(EntranceConsts.KEY_SCROLL_TO_LIBAO, true)
|
||||
}
|
||||
bundle.putString(EntranceConsts.KEY_GAME_ID, gameId)
|
||||
|
||||
@ -119,6 +119,7 @@ import com.sina.weibo.sdk.auth.AuthInfo;
|
||||
import com.sina.weibo.sdk.openapi.IWBAPI;
|
||||
import com.sina.weibo.sdk.openapi.WBAPIFactory;
|
||||
|
||||
import com.therouter.TheRouter;
|
||||
import org.greenrobot.eventbus.Subscribe;
|
||||
import org.greenrobot.eventbus.ThreadMode;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
@ -136,6 +137,7 @@ import io.reactivex.SingleSource;
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers;
|
||||
import io.reactivex.functions.Function;
|
||||
import io.reactivex.schedulers.Schedulers;
|
||||
import kotlin.Pair;
|
||||
import kotlin.Unit;
|
||||
import kotlin.jvm.functions.Function0;
|
||||
import okhttp3.RequestBody;
|
||||
@ -181,7 +183,6 @@ public class MainActivity extends BaseActivity {
|
||||
.get(MainWrapperViewModel.class);
|
||||
|
||||
DisplayUtils.transparentStatusBar(this);
|
||||
DisplayUtils.updateGlobalScreen(this);
|
||||
|
||||
super.onCreate(savedInstanceState);
|
||||
setStatusBarColor(Color.TRANSPARENT);
|
||||
@ -222,7 +223,7 @@ public class MainActivity extends BaseActivity {
|
||||
DialogHelper.showCenterWarningDialog(this, "发生闪退", "光环助手发生了闪退,建议安装到最新版本修复异常"
|
||||
, "马上反馈", "马上安装修复",
|
||||
() -> {
|
||||
DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "", "crash", true, "desc", null);
|
||||
DirectUtils.directToGameDetail(this, Constants.GHZS_GAME_ID, "", "crash", true, "desc", null, "");
|
||||
return null;
|
||||
},
|
||||
() -> {
|
||||
@ -255,9 +256,12 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
// 跳转至其它页面
|
||||
if (getIntent() != null
|
||||
&& getIntent().getExtras() != null
|
||||
&& getIntent().getBooleanExtra(EntranceConsts.KEY_REQUIRE_REDIRECT, false)) {
|
||||
doSkip();
|
||||
&& getIntent().getExtras() != null) {
|
||||
if (getIntent().getBooleanExtra(EntranceConsts.KEY_REQUIRE_REDIRECT, false)) {
|
||||
doSkip();
|
||||
} else if (!TextUtils.isEmpty(getIntent().getStringExtra(EntranceConsts.KEY_THE_ROUTER_PATH))) {
|
||||
doRedirect(getIntent().getStringExtra(EntranceConsts.KEY_THE_ROUTER_PATH));
|
||||
}
|
||||
}
|
||||
|
||||
// debug 模式下的快速跳转页面
|
||||
@ -456,6 +460,10 @@ public class MainActivity extends BaseActivity {
|
||||
|
||||
handler.removeCallbacksAndMessages(null);
|
||||
releaseExoSourceCache();
|
||||
|
||||
// 移除推送触发启动记录
|
||||
HaloApp.remove(Constants.PUSH_MESSAGE_ID);
|
||||
HaloApp.remove(Constants.PUSH_LINK_ENTITY);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -820,6 +828,19 @@ public class MainActivity extends BaseActivity {
|
||||
}, 500);
|
||||
}
|
||||
|
||||
/**
|
||||
* 重定向至 TheRouter 配置的页面
|
||||
*/
|
||||
private void doRedirect(String path) {
|
||||
if (getIntent().getExtras() != null) {
|
||||
// 更新 intent 数据,避免页面重建重新跳转
|
||||
getIntent().getExtras().putString(EntranceConsts.KEY_THE_ROUTER_PATH, "");
|
||||
AppExecutor.getUiExecutor().executeWithDelay(() -> {
|
||||
TheRouter.build(path).navigation(this);
|
||||
}, 500L);
|
||||
}
|
||||
}
|
||||
|
||||
@NonNull
|
||||
private Function0<Unit> launchGame(String gamePackageName) {
|
||||
return () -> {
|
||||
@ -923,7 +944,9 @@ public class MainActivity extends BaseActivity {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
finalDownloadEntity.getGameId(),
|
||||
finalDownloadEntity.getName(),
|
||||
"主动安装"
|
||||
"主动安装",
|
||||
"true".equals(ExtensionsKt.getMetaExtra(finalDownloadEntity, Constants.DSP_GAME)),
|
||||
ExtensionsKt.getMetaExtra(finalDownloadEntity, Constants.DSP_AD_ID)
|
||||
);
|
||||
PackageInstaller.install(MainActivity.this, finalDownloadEntity);
|
||||
}, 200);
|
||||
@ -1044,7 +1067,9 @@ public class MainActivity extends BaseActivity {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
downloadEntity.getGameId(),
|
||||
downloadEntity.getName(),
|
||||
"自动安装"
|
||||
"自动安装",
|
||||
"true".equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_GAME)),
|
||||
ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_AD_ID)
|
||||
);
|
||||
PackageInstaller.install(this, downloadEntity, false, false);
|
||||
}
|
||||
@ -1084,4 +1109,12 @@ public class MainActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Pair<String, String> getBusinessId() {
|
||||
if (mMainWrapperFragment != null) {
|
||||
return mMainWrapperFragment.getBusinessId();
|
||||
}
|
||||
return super.getBusinessId();
|
||||
}
|
||||
}
|
||||
|
||||
@ -91,7 +91,6 @@ open class SearchActivity : BaseActivity() {
|
||||
mSourceEntrance = intent.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
|
||||
val hint = intent.getStringExtra(EntranceConsts.KEY_HINT)
|
||||
val searchImmediately = intent.getBooleanExtra(KEY_SEARCH_IMMEDIATELY, false)
|
||||
var ignoreTextChanges = savedInstanceState != null
|
||||
|
||||
mPublishSubject = PublishSubject.create()
|
||||
|
||||
@ -102,12 +101,9 @@ open class SearchActivity : BaseActivity() {
|
||||
.subscribe {
|
||||
if (searchEt.text.isNotEmpty()
|
||||
&& searchEt.text != searchEt.hint
|
||||
&& !ignoreTextChanges
|
||||
) {
|
||||
search(SearchType.AUTO, it)
|
||||
}
|
||||
|
||||
ignoreTextChanges = false
|
||||
}
|
||||
|
||||
initSearchBar()
|
||||
@ -202,7 +198,6 @@ open class SearchActivity : BaseActivity() {
|
||||
|
||||
protected open fun handleEmptySearch(newSearchKey: String) {
|
||||
popBackToFragment(SearchDefaultFragment::class.java.name)// 回退到搜索首页
|
||||
updateDisplayType(DisplayType.DEFAULT)
|
||||
mPublishSubject?.onNext(newSearchKey)
|
||||
}
|
||||
|
||||
|
||||
@ -5,11 +5,12 @@ import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.os.Parcelable
|
||||
import android.view.View
|
||||
import com.gh.gamecenter.amway.AmwaySuccessFragment
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.amway.AmwaySuccessFragment
|
||||
import com.gh.gamecenter.gamedetail.LibaoListFragment
|
||||
import com.gh.gamecenter.gamedetail.cloudarchive.CloudArchiveFragment
|
||||
import com.gh.gamecenter.gamedetail.libao.LibaoListFragment
|
||||
import com.halo.assistant.fragment.SwitchInstallMethodFragment
|
||||
import com.halo.assistant.fragment.user.ManuallyRealNameFragment
|
||||
import com.halo.assistant.fragment.user.RealNameInfoFragment
|
||||
@ -38,6 +39,7 @@ class ShellActivity : ToolBarActivity() {
|
||||
Type.REAL_NAME_INFO -> startFragment(RealNameInfoFragment().with(bundle))
|
||||
Type.MANUALLY_REAL_NAME -> startFragment(ManuallyRealNameFragment().with(extraData))
|
||||
Type.SIMPLE_LIBAO_LIST -> startFragment(LibaoListFragment.newInstance(extraData))
|
||||
Type.CLOUD_ARCHIVE -> startFragment(CloudArchiveFragment().with(extraData))
|
||||
}
|
||||
}
|
||||
|
||||
@ -72,7 +74,8 @@ class ShellActivity : ToolBarActivity() {
|
||||
SWITCH_INSTALL_METHOD("switch_install_method"),
|
||||
REAL_NAME_INFO("real_name_info"),
|
||||
MANUALLY_REAL_NAME("manually_real_name"),
|
||||
SIMPLE_LIBAO_LIST("simple_libao_list");
|
||||
SIMPLE_LIBAO_LIST("simple_libao_list"),
|
||||
CLOUD_ARCHIVE("cloud_archive");
|
||||
|
||||
companion object {
|
||||
fun fromString(typeString: String): Type {
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.gamecenter;
|
||||
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_BROWSER;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_OTHER;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_PUSH;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ANSWER;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARCHIVE_LOGIN;
|
||||
@ -35,6 +36,7 @@ import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_VIDEO_STREAM
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_VIDEO_STREAMING_HOME;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_WEB;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_DATA;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_FROM;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_GAME_NAME;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_NAME;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.KEY_PACKAGENAME;
|
||||
@ -100,7 +102,6 @@ public class SkipActivity extends BaseActivity {
|
||||
@Override
|
||||
protected void onCreate(Bundle savedInstanceState) {
|
||||
super.onCreate(savedInstanceState);
|
||||
|
||||
Uri uri = getIntent().getData();
|
||||
Bundle bundle;
|
||||
if (uri != null) {
|
||||
@ -160,7 +161,8 @@ public class SkipActivity extends BaseActivity {
|
||||
DirectUtils.directToArticle(this, path, entrance);
|
||||
break;
|
||||
case HOST_GAME:
|
||||
DirectUtils.directToGameDetail(this, path, "", entrance, "true".equals(uri.getQueryParameter("auto_download")), to, null);
|
||||
String from = uri.getQueryParameter(KEY_FROM);
|
||||
DirectUtils.directToGameDetail(this, path, "", entrance, "true".equals(uri.getQueryParameter("auto_download")), to, null, from);
|
||||
break;
|
||||
case HOST_COLUMN:
|
||||
DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), entrance, null, SubjectData.SubjectType.NORMAL);
|
||||
@ -180,7 +182,7 @@ public class SkipActivity extends BaseActivity {
|
||||
DirectUtils.directToAnswerDetail(this, path, entrance, pathName);
|
||||
break;
|
||||
case HOST_QUESTION:
|
||||
DirectUtils.directToQuestionDetail(this, path, entrance, pathName, "");
|
||||
DirectUtils.directToQuestionDetail(this, path, entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
case HOST_TOOLBOX:
|
||||
DirectUtils.directToToolbox(this, uri.getQueryParameter("gameId"), uri.getQueryParameter("toolboxUrl"), entrance);
|
||||
@ -190,7 +192,7 @@ public class SkipActivity extends BaseActivity {
|
||||
break;
|
||||
// 社区文章格式一
|
||||
case "community.article":
|
||||
DirectUtils.directToCommunityArticle(this, uri.getQueryParameter("articleId"), uri.getQueryParameter("communityId"), entrance, pathName, "");
|
||||
DirectUtils.directToCommunityArticle(this, uri.getQueryParameter("articleId"), uri.getQueryParameter("communityId"), entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
// 社区文章格式二
|
||||
case "communities":
|
||||
@ -211,13 +213,13 @@ public class SkipActivity extends BaseActivity {
|
||||
}
|
||||
}
|
||||
if ("articles".equals(type)) {
|
||||
DirectUtils.directToCommunityArticle(this, typeId, communityId, entrance, pathName, "");
|
||||
DirectUtils.directToCommunityArticle(this, typeId, communityId, entrance, pathName, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
case HOST_VIDEO:
|
||||
DirectUtils.directToVideoDetail(this, path, VideoDetailContainerViewModel.Location.HOTTEST_GAME_VIDEO.getValue(),
|
||||
false, id, entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, "");
|
||||
false, id, entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
case HOST_UPLOAD_VIDEO://跳转上传视频
|
||||
String titleParameter = uri.getQueryParameter("title");
|
||||
@ -237,7 +239,7 @@ public class SkipActivity extends BaseActivity {
|
||||
break;
|
||||
case HOST_VIDEO_SINGLE:
|
||||
DirectUtils.directToVideoDetail(this, path, VideoDetailContainerViewModel.Location.SINGLE_VIDEO.getValue(),
|
||||
false, "", entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, "");
|
||||
false, "", entrance, pathName, TextUtils.isEmpty(referer) ? "" : referer, isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
case HOST_VIDEO_MORE:
|
||||
gameId = uri.getQueryParameter("gameId");
|
||||
@ -294,7 +296,7 @@ public class SkipActivity extends BaseActivity {
|
||||
EntranceUtils.jumpActivityCompat(this, bundle);
|
||||
break;
|
||||
case EntranceConsts.HOST_VIDEO_DETAIL:
|
||||
DirectUtils.directToVideoDetail(this, path, entrance, "", "");
|
||||
DirectUtils.directToVideoDetail(this, path, entrance, "", isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
break;
|
||||
case HOST_LIBAO:
|
||||
DirectUtils.directToGiftDetail(this, path, entrance);
|
||||
@ -315,7 +317,7 @@ public class SkipActivity extends BaseActivity {
|
||||
break;
|
||||
|
||||
case HOST_COLUMN_COLLECTION:
|
||||
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null,false);
|
||||
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null, false);
|
||||
break;
|
||||
case EntranceConsts.HOST_BLOCK:
|
||||
name = uri.getQueryParameter("name");
|
||||
@ -357,7 +359,7 @@ public class SkipActivity extends BaseActivity {
|
||||
byte[] linkData = Base64.decode(dataString, Base64.DEFAULT);
|
||||
String linkDataString = new String(linkData, "UTF-8");
|
||||
LaunchRedirect launchRedirect = GsonUtils.fromJson(linkDataString, LaunchRedirect.class);
|
||||
DirectUtils.directToLinkPage(this, launchRedirect, entrance, "", "");
|
||||
DirectUtils.directToLinkPage(this, launchRedirect, entrance, "", isFromPush ? ENTRANCE_PUSH : ENTRANCE_OTHER);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
@ -414,13 +416,13 @@ public class SkipActivity extends BaseActivity {
|
||||
break;
|
||||
case HOST_ARCHIVE_LOGIN:
|
||||
String gamePkg = uri.getQueryParameter(EntranceConsts.KEY_GAME_PKG);
|
||||
if(CheckLoginUtils.isLogin()) {
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
VHelper.INSTANCE.updateAuthorizeInfo(true);
|
||||
} else {
|
||||
Bundle newBundle = new Bundle();
|
||||
newBundle.putString(EntranceConsts.KEY_TO, LoginActivity.class.getName());
|
||||
EntranceUtils.jumpActivityCompat(this, newBundle, null, (resultCode, data) -> {
|
||||
if(CheckLoginUtils.isLogin()) {
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
VHelper.INSTANCE.updateAuthorizeInfo(true);
|
||||
}
|
||||
VHelper.launch(this, gamePkg, false, false);
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.NotificationManager
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.content.SharedPreferences
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.preference.PreferenceManager
|
||||
@ -14,8 +15,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
|
||||
@ -26,6 +27,9 @@ import com.gh.common.util.UsageStatsHelper.checkAndPostUsageStats
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_FROM
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts.KEY_GAMEID
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.common.tracker.TrackerLogger
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
@ -90,8 +94,10 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
|
||||
showPrivacyDialog()
|
||||
} else {
|
||||
val spanBuilder = buildSpannedString {
|
||||
append("这个弹窗只会在右上角有环境标签的测试包出现" +
|
||||
"\n进入应用以后还可以到关于我们页面长按应用图标重新选择")
|
||||
append(
|
||||
"这个弹窗只会在右上角有环境标签的测试包出现" +
|
||||
"\n进入应用以后还可以到关于我们页面长按应用图标重新选择"
|
||||
)
|
||||
bold {
|
||||
color(com.gh.gamecenter.common.R.color.text_theme.toColor(this@SplashScreenActivity)) {
|
||||
append("\n点击这里进行预设置渠道")
|
||||
@ -103,7 +109,7 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
|
||||
executeDex2OatInAdvance()
|
||||
DialogHelper.showDialog(
|
||||
context = this,
|
||||
title ="选择环境",
|
||||
title = "选择环境",
|
||||
content = spanBuilder,
|
||||
confirmText = "正式环境",
|
||||
cancelText = "测试环境",
|
||||
@ -144,6 +150,10 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
|
||||
val trackEvent = JSONObject()
|
||||
// 是否首次使用神策
|
||||
val isFirstTime = SPUtils.getBoolean(Constants.SP_SENSORS_IS_FIRST_TIME, true)
|
||||
val therouterPath = intent.extras?.getString(EntranceConsts.KEY_THE_ROUTER_PATH) ?: ""
|
||||
val uri = Uri.parse(therouterPath)
|
||||
val isFromWechat = WECHAT_APPOINTMENT == if (!uri.isOpaque) uri.getQueryParameter(KEY_FROM) else false
|
||||
val gameId = if (!uri.isOpaque) uri.getQueryParameter(KEY_GAMEID) else ""
|
||||
tryCatchInRelease {
|
||||
trackEvent.run {
|
||||
put("\$is_first_time", isFirstTime)
|
||||
@ -153,6 +163,10 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
|
||||
put("signature", signatureHash)
|
||||
put("app_name", appProvider?.getAppName())
|
||||
put("install_first_time", if (HaloApp.getInstance().isBrandNewInstall) "是" else "否")
|
||||
if (isFromWechat) {
|
||||
put("source_entrance", WECHAT_NOTIFICATION)
|
||||
put("page_business_id", gameId)
|
||||
}
|
||||
}
|
||||
}
|
||||
SensorsBridge.trackEvent("AppLaunch", trackEvent)
|
||||
@ -323,6 +337,9 @@ class SplashScreenActivity : BaseActivity(), ISplashScreen {
|
||||
const val HONOR_CULPRIT_ID = 12324
|
||||
const val HONOR_CULPRIT_CHANNEL = "荣耀通道"
|
||||
|
||||
private const val WECHAT_APPOINTMENT = "wechat_appointment"
|
||||
private const val WECHAT_NOTIFICATION = "微信通知"
|
||||
|
||||
@JvmStatic
|
||||
fun getSplashScreenIntent(context: Context?, bundle: Bundle?): Intent {
|
||||
val intent = Intent(context, SplashScreenActivity::class.java)
|
||||
|
||||
@ -2,6 +2,7 @@ package com.gh.gamecenter.adapter;
|
||||
|
||||
import android.annotation.SuppressLint;
|
||||
import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.text.Html;
|
||||
import android.text.SpannableStringBuilder;
|
||||
import android.text.Spanned;
|
||||
@ -12,6 +13,7 @@ import android.text.style.ClickableSpan;
|
||||
import android.view.MotionEvent;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.widget.LinearLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.content.ContextCompat;
|
||||
@ -20,9 +22,14 @@ import androidx.recyclerview.widget.LinearLayoutManager;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder;
|
||||
|
||||
import com.facebook.drawee.drawable.ScalingUtils;
|
||||
import com.facebook.drawee.generic.GenericDraweeHierarchyBuilder;
|
||||
import com.facebook.drawee.generic.RoundingParams;
|
||||
import com.facebook.drawee.view.SimpleDraweeView;
|
||||
import com.gh.common.util.DirectUtils;
|
||||
import com.gh.common.util.LibaoUtils;
|
||||
import com.gh.gamecenter.GameDetailActivity;
|
||||
import com.gh.gamecenter.ImageViewerActivity;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.adapter.viewholder.LibaoDetailContentViewHolder;
|
||||
import com.gh.gamecenter.adapter.viewholder.LibaoDetailTopViewHolder;
|
||||
@ -31,6 +38,7 @@ import com.gh.gamecenter.common.entity.SimpleGameEntity;
|
||||
import com.gh.gamecenter.common.entity.SuggestType;
|
||||
import com.gh.gamecenter.common.retrofit.Response;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.ImageUtils;
|
||||
import com.gh.gamecenter.common.utils.PicassoImageGetter;
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder;
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils;
|
||||
@ -79,6 +87,7 @@ public class LibaoDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
private String mEntrance;
|
||||
private final int TYPE_FOOTER = 100;
|
||||
public LibaoDetailTopViewHolder libaoDetailTopViewHolder;
|
||||
private ArrayList<View> mImageViewList = new ArrayList<>();
|
||||
|
||||
public LibaoDetailAdapter(Context context, OnRequestCallBackListener onRequestCallBackListener,
|
||||
OnCodeScrollListener onCodeScrollListener, LibaoEntity libaoEntity,
|
||||
@ -317,6 +326,35 @@ public class LibaoDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
holder.binding.libaodetailContentLl.setVisibility(View.VISIBLE);
|
||||
holder.binding.libaodetailContent.setText(Html.fromHtml(mLibaoEntity.getContent()));
|
||||
}
|
||||
if (mLibaoEntity.getMaterials().isEmpty()) {
|
||||
holder.binding.horizontalScrollView.setVisibility(View.GONE);
|
||||
} else {
|
||||
holder.binding.horizontalScrollView.setVisibility(View.VISIBLE);
|
||||
holder.binding.imagesContainer.removeAllViews();
|
||||
mImageViewList.clear();
|
||||
for (int i = 0; i < mLibaoEntity.getMaterials().size(); i++) {
|
||||
String imageUrl = mLibaoEntity.getMaterials().get(i).getImg();
|
||||
SimpleDraweeView imageView = new SimpleDraweeView(mContext);
|
||||
RoundingParams roundingParams = new RoundingParams();
|
||||
roundingParams.setCornersRadius(DisplayUtils.dip2px(4F));
|
||||
roundingParams.setOverlayColor(ContextCompat.getColor(mContext, com.gh.gamecenter.common.R.color.ui_surface));
|
||||
imageView.setHierarchy(new GenericDraweeHierarchyBuilder(mContext.getResources())
|
||||
.setFadeDuration(500)
|
||||
.setRoundingParams(roundingParams)
|
||||
.setPlaceholderImage(com.gh.gamecenter.common.R.drawable.occupy, ScalingUtils.ScaleType.FIT_XY)
|
||||
.build());
|
||||
ImageUtils.display(imageView, imageUrl);
|
||||
final int index = i;
|
||||
imageView.setOnClickListener(v -> {
|
||||
Intent intent = ImageViewerActivity.getIntent(mContext, mLibaoEntity.getMaterialImgList(), index, mImageViewList, mEntrance);
|
||||
mContext.startActivity(intent);
|
||||
});
|
||||
mImageViewList.add(imageView);
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(DisplayUtils.dip2px(24F), DisplayUtils.dip2px(24F));
|
||||
layoutParams.setMargins(DisplayUtils.dip2px(i == 0 ? 8F : 16F), 0, 0, 0);
|
||||
holder.binding.imagesContainer.addView(imageView, layoutParams);
|
||||
}
|
||||
}
|
||||
if (mLibaoDetailEntity != null) {
|
||||
holder.binding.libaodetailTimeLl.setVisibility(View.VISIBLE);
|
||||
|
||||
@ -384,9 +422,66 @@ public class LibaoDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
ExtensionsKt.setDrawableEnd(holder.binding.libaodetailCondition, com.gh.gamecenter.common.R.drawable.ic_libao_activity_arrow, null, null);
|
||||
holder.binding.libaodetailCondition.setCompoundDrawablePadding(DisplayUtils.dip2px(4F));
|
||||
}
|
||||
} else if (mLibaoDetailEntity.getReceiveCondition() != null) {
|
||||
holder.binding.libaodetailCondition.setVisibility(View.VISIBLE);
|
||||
holder.binding.libaodetailConditionDescTv.setVisibility(View.VISIBLE);
|
||||
holder.binding.libaodetailCondition.setText("领取条件:");
|
||||
holder.binding.libaodetailConditionDescTv.setText(
|
||||
getConditionDescText(
|
||||
mGameEntity.getName(),
|
||||
mLibaoDetailEntity.getReceiveCondition().getStar(),
|
||||
mLibaoDetailEntity.getReceiveCondition().getWords()
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据评分和字数限制生成文案。
|
||||
*
|
||||
* @param gameName 游戏名称
|
||||
* @param star 评分选项 (-1: 无限制, 5: 5星好评, 4: 4星及以上, 3: 3星及以上, 2: 2星及以上, 1: 1星及以上)
|
||||
* @param words 字数限制 (-1: 无限制, n: 字数不少于n)
|
||||
* @return 生成的文案
|
||||
*/
|
||||
private String getConditionDescText(String gameName, int star, int words) {
|
||||
StringBuilder text = new StringBuilder();
|
||||
|
||||
text.append("发表《").append(gameName).append("》");
|
||||
|
||||
if (words == -1) {
|
||||
text.append("的游戏评价");
|
||||
} else {
|
||||
text.append("不少于").append(words).append("字的游戏评价");
|
||||
}
|
||||
|
||||
if (star != -1) {
|
||||
text.append("并给予");
|
||||
switch (star) {
|
||||
case 5:
|
||||
text.append("5星好评");
|
||||
break;
|
||||
case 4:
|
||||
text.append("4星及以上评分");
|
||||
break;
|
||||
case 3:
|
||||
text.append("3星及以上评分");
|
||||
break;
|
||||
case 2:
|
||||
text.append("2星及以上评分");
|
||||
break;
|
||||
case 1:
|
||||
text.append("1星及以上评分");
|
||||
break;
|
||||
default:
|
||||
//throw new IllegalArgumentException("Invalid star value: " + star); // 或者返回一个默认值,比如空字符串或错误消息
|
||||
return ""; // or return a default string or throw an exception.
|
||||
}
|
||||
}
|
||||
|
||||
return text.toString();
|
||||
}
|
||||
|
||||
public LibaoEntity getLibaoEntity() {
|
||||
return mLibaoEntity;
|
||||
}
|
||||
|
||||
@ -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,12 +16,14 @@ 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
|
||||
import com.gh.common.simulator.SimulatorDownloadManager
|
||||
import com.gh.common.simulator.SimulatorGameManager
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.GameUtils.getDownloadBtnText
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.common.xapk.XapkInstaller
|
||||
import com.gh.common.xapk.XapkUnzipStatus
|
||||
@ -31,7 +36,6 @@ import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.NewFlatLogUtils
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
@ -41,11 +45,14 @@ import com.gh.gamecenter.core.utils.StringUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.eventbus.EBScroll
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.PluginLocation
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.view.DownloadButton
|
||||
import com.gh.gamecenter.feature.view.DownloadButton.ButtonStyle
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
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
|
||||
import com.gh.vspace.VHelper
|
||||
import com.lightgame.download.DownloadEntity
|
||||
@ -56,13 +63,16 @@ import java.io.File
|
||||
// 虽然叫 ViewHolder,但其实就是一个用来临时放 View 和相关操作的包裹类
|
||||
class DetailViewHolder(
|
||||
view: View,
|
||||
val viewModel: GameDetailViewModel?,
|
||||
val gameEntity: GameEntity,
|
||||
val isNewsDetail: Boolean, // 新闻详情不显示下载的游戏名, 只显示下载状态
|
||||
entrance: String?,
|
||||
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
|
||||
var downloadBottom: View
|
||||
@ -90,21 +100,43 @@ class DetailViewHolder(
|
||||
// 多版本下载文字
|
||||
var multiVersionDownloadTv: TextView?
|
||||
|
||||
// 加速按钮
|
||||
val speedContainer: ConstraintLayout?
|
||||
private val ivFreeVipTag: ImageView?
|
||||
private val gMoreZone: Group?
|
||||
private val gUpdate: Group?
|
||||
private val ivUpdate: ImageView?
|
||||
private val tvSize: TextView?
|
||||
private val vUpdate: View?
|
||||
private val tvUpdate: TextView?
|
||||
|
||||
private var isShowVaUpdate = true
|
||||
|
||||
// 注意 View 的命名
|
||||
init {
|
||||
downloadBottom = view.findViewById(R.id.detail_ll_bottom)
|
||||
downloadPb = view.findViewById(R.id.detail_progressbar)
|
||||
downloadTips = view.findViewById(R.id.downloadTipsLottie)
|
||||
downloadPb = downloadBottom.findViewById(R.id.detail_progressbar)
|
||||
downloadTips = downloadBottom.findViewById(R.id.downloadTipsLottie)
|
||||
overlayTv = view.findViewById(R.id.overlayTv)
|
||||
overlayContainer = view.findViewById(R.id.overlayContainer)
|
||||
extraOverlayTv = view.findViewById(R.id.extraOverlayTv)
|
||||
multiVersionDownloadTv = view.findViewById(R.id.multiVersionDownloadTv)
|
||||
multiVersionDownloadTv = downloadBottom.findViewById(R.id.multiVersionDownloadTv)
|
||||
localDownloadContainer = view.findViewById(R.id.localDownloadContainer)
|
||||
localDownloadSizeTv = view.findViewById(R.id.localDownloadSizeTv)
|
||||
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)
|
||||
gUpdate = view.findViewById(R.id.g_update)
|
||||
ivUpdate = view.findViewById(R.id.iv_update)
|
||||
tvSize = view.findViewById(R.id.tv_size)
|
||||
vUpdate = view.findViewById(R.id.v_update)
|
||||
tvUpdate = view.findViewById(R.id.tv_update)
|
||||
|
||||
context = view.context
|
||||
com.gh.gamecenter.common.R.color.text_aw_primary.toColor()
|
||||
|
||||
var gameDownloadMode = gameEntity.getGameDownloadButtonMode()
|
||||
|
||||
@ -115,7 +147,8 @@ class DetailViewHolder(
|
||||
mTitle = title ?: "",
|
||||
mAsVGame = false,
|
||||
mShowDualDownloadButton = gameDownloadMode == GameEntity.GAME_DOWNLOAD_BUTTON_MODE_DUAL,
|
||||
mTraceEvent = traceEvent
|
||||
mTraceEvent = traceEvent,
|
||||
onDownloadClickAction = onDownloadClickAction
|
||||
)
|
||||
|
||||
val vGameDownloadListener = OnDetailDownloadClickListener(
|
||||
@ -125,7 +158,8 @@ class DetailViewHolder(
|
||||
mTitle = title ?: "",
|
||||
mAsVGame = true,
|
||||
mShowDualDownloadButton = gameDownloadMode == GameEntity.GAME_DOWNLOAD_BUTTON_MODE_DUAL,
|
||||
mTraceEvent = traceEvent
|
||||
mTraceEvent = traceEvent,
|
||||
onDownloadClickAction = onDownloadClickAction
|
||||
)
|
||||
|
||||
// 不支持双下载按钮的情况时,优选一个下载方式显示
|
||||
@ -158,9 +192,22 @@ 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
|
||||
)
|
||||
)
|
||||
|
||||
vUpdate?.setOnClickListener {
|
||||
if (isShowVaUpdate) {
|
||||
vGameDownloadListener.onClick(vUpdate)
|
||||
} else {
|
||||
localDownloadListener.onClick(vUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
downloadPb.putWidgetBusinessName("游戏详情页")
|
||||
// "DownLoadbuttonClick" 埋点需要上报traceEvent信息
|
||||
|
||||
@ -170,12 +217,97 @@ class DetailViewHolder(
|
||||
}
|
||||
|
||||
private fun restoreDialogFragment() {
|
||||
val gamePermissionDialogFragment = (context as AppCompatActivity).supportFragmentManager.findFragmentByTag(
|
||||
val gamePermissionDialogFragment = (context.getActivity() as? AppCompatActivity)?.supportFragmentManager?.findFragmentByTag(
|
||||
GamePermissionDialogFragment::class.java.name
|
||||
) as DialogFragment?
|
||||
gamePermissionDialogFragment?.dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
|
||||
fun checkIfShowSpeedUi(showVGame: Boolean, showDualDownloadButton: Boolean) {
|
||||
acceleratorUiHelper?.let {
|
||||
val showSpeedUi = when {
|
||||
gameEntity.canSpeed && showDualDownloadButton -> { // 双按钮
|
||||
val localText = getDownloadBtnText(context, gameEntity, false, false, PluginLocation.only_game);
|
||||
val downloadText =
|
||||
getDownloadBtnText(context, gameEntity, false, true, PluginLocation.only_game)
|
||||
when {
|
||||
localText.contains(com.gh.gamecenter.feature.R.string.update.toResString()) -> { // 本地游戏需要更新
|
||||
localDownloadContainer?.goneIf(true)
|
||||
downloadPb.goneIf(true)
|
||||
overlayTv?.goneIf(true)
|
||||
|
||||
gUpdate?.visibility = View.VISIBLE
|
||||
ivUpdate?.goneIf(true)
|
||||
tvSize?.goneIf(false)
|
||||
tvSize?.text =
|
||||
DetailDownloadUtils.convertSizeString(gameEntity.getApk().firstOrNull()?.size ?: "")
|
||||
tvUpdate?.setText(R.string.update)
|
||||
isShowVaUpdate = false
|
||||
true
|
||||
}
|
||||
|
||||
localText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) && downloadText == "更新" -> { // 畅玩游戏需要更新:显示 加速/更新
|
||||
localDownloadContainer?.goneIf(true)
|
||||
downloadPb.goneIf(true)
|
||||
overlayTv?.goneIf(true)
|
||||
|
||||
gUpdate?.visibility = View.VISIBLE
|
||||
ivUpdate?.goneIf(false)
|
||||
tvSize?.goneIf(true)
|
||||
tvUpdate?.setText(R.string.update)
|
||||
isShowVaUpdate = true
|
||||
true
|
||||
}
|
||||
|
||||
localText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) -> { // 本地游戏为启动状态:显示 加速/畅玩
|
||||
localDownloadContainer?.goneIf(true)
|
||||
downloadPb.goneIf(true)
|
||||
overlayTv?.goneIf(true)
|
||||
|
||||
gUpdate?.visibility = View.VISIBLE
|
||||
tvUpdate?.setText(R.string.v_play)
|
||||
tvSize?.goneIf(true)
|
||||
isShowVaUpdate = true
|
||||
true
|
||||
}
|
||||
|
||||
else -> {
|
||||
gUpdate?.visibility = View.GONE
|
||||
tvSize?.goneIf(true)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
gameEntity.canSpeed && !showVGame -> {
|
||||
gUpdate?.visibility = View.GONE
|
||||
val downloadText = getDownloadBtnText(context, gameEntity, false, false, PluginLocation.only_game)
|
||||
when {
|
||||
downloadText.contains(com.gh.gamecenter.feature.R.string.launch.toResString()) -> {
|
||||
localDownloadContainer?.goneIf(true)
|
||||
downloadPb.goneIf(true)
|
||||
true
|
||||
}
|
||||
|
||||
downloadText.contains(R.string.update.toResString()) -> true
|
||||
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
else -> false
|
||||
}
|
||||
it.checkIfShowSpeedUi(showSpeedUi, showDualDownloadButton)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
fun setSpeedViewsVisible(isVisible: Boolean) {
|
||||
ivFreeVipTag?.goneIf(!isVisible)
|
||||
speedContainer?.goneIf(!isVisible)
|
||||
}
|
||||
|
||||
internal class OnDetailDownloadClickListener(
|
||||
private val mViewHolder: DetailViewHolder,
|
||||
private val mEntrance: String?,
|
||||
@ -183,7 +315,8 @@ class DetailViewHolder(
|
||||
private val mTitle: String,
|
||||
private val mAsVGame: Boolean,
|
||||
private val mShowDualDownloadButton: Boolean,
|
||||
private val mTraceEvent: ExposureEvent?
|
||||
private val mTraceEvent: ExposureEvent?,
|
||||
private val onDownloadClickAction: ((Boolean) -> Unit)? = null
|
||||
) : View.OnClickListener {
|
||||
|
||||
private val mGameEntity: GameEntity = mViewHolder.gameEntity
|
||||
@ -261,7 +394,9 @@ class DetailViewHolder(
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
mGameEntity.id,
|
||||
mGameEntity.name ?: "",
|
||||
"主动安装"
|
||||
"主动安装",
|
||||
mGameEntity.isDspGame,
|
||||
mGameEntity.dspAdId
|
||||
)
|
||||
PackageInstaller.install(mViewHolder.context, mDownloadEntity)
|
||||
}
|
||||
@ -281,11 +416,13 @@ class DetailViewHolder(
|
||||
showLandPageAddressDialogIfNeeded()
|
||||
}
|
||||
}
|
||||
|
||||
"toast" -> {
|
||||
EventBus.getDefault().post(EBReuse(GameDetailFragment.SKIP_RATING))
|
||||
mViewHolder.viewModel?.performTabSelected(GameDetailTabEntity.TYPE_COMMENT)
|
||||
ToastUtils.toast("该游戏因故暂不提供下载,具体详情可在相关评论中查看,敬请谅解~")
|
||||
showLandPageAddressDialogIfNeeded()
|
||||
}
|
||||
|
||||
"third_party" -> {
|
||||
showLandPageAddressDialogIfNeeded()
|
||||
}
|
||||
@ -334,7 +471,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(
|
||||
@ -354,7 +503,8 @@ class DetailViewHolder(
|
||||
gameEntity = mGameEntity,
|
||||
traceEvent = mTraceEvent,
|
||||
entrance = StringUtils.buildString(mEntrance, "+(", mName, "[", mTitle, "])"),
|
||||
location = "$mName:$mTitle"
|
||||
location = "$mName:$mTitle",
|
||||
onDownloadClickAction = onDownloadClickAction
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -371,7 +521,9 @@ class DetailViewHolder(
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
gameId = mGameEntity.id,
|
||||
gameName = mGameEntity.name ?: "",
|
||||
action = "主动安装"
|
||||
action = "主动安装",
|
||||
isDspGame = mGameEntity.isDspGame,
|
||||
dspAdId = mGameEntity.dspAdId
|
||||
)
|
||||
val url = mGameEntity.getApk().firstOrNull()?.url
|
||||
val downloadEntity = SimulatorGameManager.findDownloadEntityByUrl(url)
|
||||
@ -594,8 +746,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)
|
||||
@ -671,6 +824,7 @@ class DetailViewHolder(
|
||||
builder.addHandler(OverseaDownloadHandler())
|
||||
builder.addHandler(CheckDownloadHandler())
|
||||
builder.setProcessEndCallback(mGameEntity.id) { asVGame: Boolean, isSubscribe: Any? ->
|
||||
performDownloadClickAction()
|
||||
download(asVGame, isSubscribe as Boolean)
|
||||
}
|
||||
} else {
|
||||
@ -688,7 +842,8 @@ class DetailViewHolder(
|
||||
mTitle,
|
||||
"])"
|
||||
),
|
||||
"$mName:$mTitle"
|
||||
"$mName:$mTitle",
|
||||
onDownloadClickAction
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -699,5 +854,20 @@ class DetailViewHolder(
|
||||
mAsVGame
|
||||
)
|
||||
}
|
||||
|
||||
private fun performDownloadClickAction() {
|
||||
val buttonText = if (mShowDualDownloadButton && !mAsVGame) {
|
||||
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
|
||||
onDownloadClickAction?.invoke(isUpdate)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,82 +1,89 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.CategoryDirectoryItemBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class CategoryDirectoryAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private var mList: List<CategoryEntity>
|
||||
) : BaseRecyclerAdapter<CategoryDirectoryAdapter.CategoryDirectoryItemViewHolder>(context) {
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener
|
||||
) : RecyclerView.Adapter<CategoryDirectoryAdapter.CategoryDirectoryItemViewHolder>() {
|
||||
|
||||
val width = mContext.resources.displayMetrics.widthPixels * 260 / 360
|
||||
private val data = arrayListOf<CategoryEntity>()
|
||||
|
||||
fun setListData(list: List<CategoryEntity>) {
|
||||
mList = list
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setListData(newData: List<CategoryEntity>) {
|
||||
data.clear()
|
||||
data.addAll(newData)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
CategoryDirectoryItemViewHolder(CategoryDirectoryItemBinding.inflate(mLayoutInflater))
|
||||
CategoryDirectoryItemViewHolder(listener, parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: CategoryDirectoryItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
root.layoutParams = root.layoutParams?.apply {
|
||||
width = mContext.resources.displayMetrics.widthPixels * 260 / 360
|
||||
} ?: RecyclerView.LayoutParams(width, RecyclerView.LayoutParams.WRAP_CONTENT)
|
||||
holder.onBind(position, data[position])
|
||||
}
|
||||
|
||||
val padTop = if (position == 0) 16F.dip2px() else 24F.dip2px()
|
||||
root.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 0)
|
||||
override fun onBindViewHolder(holder: CategoryDirectoryItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
} else {
|
||||
holder.notifyItemSelectedChanged()
|
||||
}
|
||||
|
||||
val entity = mList[position]
|
||||
title.text = entity.name
|
||||
title.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
}
|
||||
|
||||
subCategoryRv.run {
|
||||
if (adapter is SubCategoryAdapter) {
|
||||
layoutManager = GridLayoutManager(mContext, 3)
|
||||
adapter = entity.data?.let {
|
||||
SubCategoryAdapter(
|
||||
mContext,
|
||||
mViewModel,
|
||||
it,
|
||||
position
|
||||
)
|
||||
}
|
||||
} else {
|
||||
layoutManager = GridLayoutManager(mContext, 3)
|
||||
adapter = entity.data?.let {
|
||||
SubCategoryAdapter(
|
||||
mContext,
|
||||
mViewModel,
|
||||
it,
|
||||
position
|
||||
)
|
||||
}
|
||||
addItemDecoration(
|
||||
GridSpacingItemColorDecoration(
|
||||
mContext,
|
||||
6,
|
||||
6,
|
||||
com.gh.gamecenter.common.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
fun notifyItemSelectedChanged(parentId: String) {
|
||||
val position = data.indexOfFirst { it.id == parentId }
|
||||
if (position != -1) {
|
||||
notifyItemChanged(position, "")
|
||||
}
|
||||
}
|
||||
|
||||
class CategoryDirectoryItemViewHolder(val binding: CategoryDirectoryItemBinding) :
|
||||
BaseRecyclerViewHolder<Any>(binding.root)
|
||||
class CategoryDirectoryItemViewHolder(
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener,
|
||||
val binding: CategoryDirectoryItemBinding
|
||||
) :
|
||||
ViewHolder(binding.root) {
|
||||
|
||||
private val childAdapter by lazy {
|
||||
SubCategoryAdapter(listener)
|
||||
}
|
||||
|
||||
fun onBind(position: Int, item: CategoryEntity) {
|
||||
|
||||
val context = binding.root.context
|
||||
binding.title.text = item.name
|
||||
|
||||
if (binding.subCategoryRv.adapter == null) {
|
||||
binding.subCategoryRv.layoutManager = object : GridLayoutManager(context, 4) {
|
||||
override fun canScrollVertically(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
binding.subCategoryRv.adapter = childAdapter
|
||||
binding.subCategoryRv.addItemDecoration(
|
||||
GridSpacingItemColorDecoration(
|
||||
context,
|
||||
8,
|
||||
8,
|
||||
com.gh.gamecenter.common.R.color.transparent
|
||||
)
|
||||
)
|
||||
}
|
||||
childAdapter.setData(position, item)
|
||||
}
|
||||
|
||||
fun notifyItemSelectedChanged() {
|
||||
childAdapter.notifyItemRangeChanged(0, childAdapter.itemCount, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -37,8 +37,8 @@ class CategoryV2Activity : DownloadToolbarActivity() {
|
||||
companion object {
|
||||
fun getIntent(context: Context, catalogId: String, catalogTitle: String, entrance: String): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, catalogId)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_ID, catalogId)
|
||||
bundle.putString(EntranceConsts.KEY_PAGE_NAME, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
|
||||
return getTargetIntent(context, CategoryV2Activity::class.java, CategoryV2Fragment::class.java, bundle)
|
||||
}
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
@ -13,11 +14,13 @@ import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class CategoryV2Adapter(
|
||||
context: Context,
|
||||
private val mFragment: CategoryV2Fragment,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private val mList: List<SidebarsEntity.SidebarEntity>
|
||||
) : BaseRecyclerAdapter<CategoryV2Adapter.CategoryV2ItemViewHolder>(context) {
|
||||
|
||||
var selectedPosition = 0
|
||||
private set
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
@ -28,25 +31,35 @@ class CategoryV2Adapter(
|
||||
val catalogEntity = mList[position]
|
||||
catalogName.text = catalogEntity.name
|
||||
recommendTag.goneIf(!catalogEntity.recommended)
|
||||
if (catalogEntity.name == mViewModel.selectedCategoryName) {
|
||||
if (position == selectedPosition) {
|
||||
selectedTag.visibility = View.VISIBLE
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
catalogName.setTypeface(null, Typeface.BOLD)
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(mContext))
|
||||
} else {
|
||||
selectedTag.visibility = View.GONE
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
catalogName.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext))
|
||||
catalogName.setTypeface(null, Typeface.NORMAL)
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(mContext))
|
||||
}
|
||||
root.setOnClickListener {
|
||||
if (catalogEntity.name != mViewModel.selectedCategoryName) {
|
||||
mViewModel.selectedCategoryName = catalogEntity.name
|
||||
mFragment.changeCategory(position)
|
||||
if (position != selectedPosition) {
|
||||
mViewModel.logClickSide()
|
||||
notifyDataSetChanged()
|
||||
mViewModel.selectSidebarsPosition(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun selectPosition(newPosition: Int) {
|
||||
if (selectedPosition == newPosition) {
|
||||
return
|
||||
}
|
||||
val oldSelection = selectedPosition
|
||||
selectedPosition = newPosition
|
||||
notifyItemChanged(oldSelection)
|
||||
notifyItemChanged(newPosition)
|
||||
}
|
||||
|
||||
class CategoryV2ItemViewHolder(val binding: CategoryV2ItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Typeface
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.GravityCompat
|
||||
import androidx.drawerlayout.widget.DrawerLayout
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.core.widget.TextViewCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.SearchActivity
|
||||
@ -22,35 +22,49 @@ import com.gh.gamecenter.common.view.FixLinearLayoutManager
|
||||
import com.gh.gamecenter.core.utils.PageSwitchDataHelper
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.FragmentCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.entity.SidebarsEntity
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.wrapper.SearchToolbarTabWrapperViewModel
|
||||
|
||||
class CategoryV2Fragment : LazyFragment() {
|
||||
|
||||
private var mBinding: FragmentCategoryBinding? = null
|
||||
private var mViewModel: CategoryV2ViewModel? = null
|
||||
|
||||
private val viewModel by viewModels<CategoryV2ViewModel>()
|
||||
|
||||
private var mHomeViewModel: SearchToolbarTabWrapperViewModel? = null
|
||||
private var mEntity: SidebarsEntity? = null
|
||||
private var mSpecialCatalogFragment: SpecialCatalogFragment? = null
|
||||
private var mCategoryV2ListFragment: CategoryV2ListFragment? = null
|
||||
private var mLastPageDataMap: HashMap<String, String>? = null
|
||||
|
||||
private var mCategoryId: String = ""
|
||||
private var mCategoryTitle: String = ""
|
||||
private var pageId: String = ""
|
||||
private var pageName: String = ""
|
||||
private var mLastSelectedPosition = -1
|
||||
|
||||
private var searchCategoryPop: SearchCategoryPop? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
pageId = arguments?.getString(EntranceConsts.KEY_PAGE_ID) ?: ""
|
||||
pageName = arguments?.getString(EntranceConsts.KEY_PAGE_NAME) ?: ""
|
||||
mLastPageDataMap = PageSwitchDataHelper.popLastPageData()
|
||||
savedInstanceState?.run {
|
||||
mLastSelectedPosition = getInt(EntranceConsts.KEY_LAST_SELECTED_POSITION)
|
||||
}
|
||||
|
||||
// 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值
|
||||
var entrance = if (mEntrance.contains("首页")) "首页" else "板块"
|
||||
|
||||
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true && multiTabNavId.isNotEmpty()) {
|
||||
mHomeViewModel =
|
||||
viewModelProviderFromParent(SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, ""), multiTabNavId)
|
||||
entrance = "首页Tab栏"
|
||||
}
|
||||
viewModel.init(entrance)
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
mViewModel?.run {
|
||||
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, selectedCategoryPosition)
|
||||
}
|
||||
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, viewModel.selectedSidebarsPosition.value ?: 0)
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
@ -61,100 +75,40 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
mCategoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
|
||||
mCategoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
|
||||
mLastPageDataMap = PageSwitchDataHelper.popLastPageData()
|
||||
mViewModel = viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId)
|
||||
|
||||
// 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值
|
||||
mViewModel?.entrance = if (mEntrance.contains("首页")) "首页" else "板块"
|
||||
|
||||
val multiTabNavId = arguments?.getString(EntranceConsts.KEY_MULTI_TAB_NAV_ID, "") ?: ""
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true && multiTabNavId.isNotEmpty()) {
|
||||
mHomeViewModel = viewModelProviderFromParent(SearchToolbarTabWrapperViewModel.Factory(multiTabNavId, ""), multiTabNavId)
|
||||
mViewModel?.entrance = "首页Tab栏"
|
||||
}
|
||||
mViewModel?.logAppearance()
|
||||
viewModel.logAppearance()
|
||||
|
||||
super.onFragmentFirstVisible()
|
||||
|
||||
viewModel.loadData(pageId, pageName)
|
||||
}
|
||||
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
initMenu(R.menu.menu_search)
|
||||
setNavigationTitle(mCategoryTitle)
|
||||
setNavigationTitle(pageName)
|
||||
|
||||
mBinding?.run {
|
||||
val width = resources.displayMetrics.widthPixels * 260 / 360
|
||||
drawerLayout.setScrimColor(com.gh.gamecenter.common.R.color.black_alpha_30.toColor())
|
||||
// 关闭手势滑动
|
||||
drawerLayout.setDrawerLockMode(DrawerLayout.LOCK_MODE_LOCKED_CLOSED)
|
||||
drawerLayout.addDrawerListener(object : DrawerLayout.DrawerListener {
|
||||
override fun onDrawerStateChanged(newState: Int) {
|
||||
}
|
||||
|
||||
override fun onDrawerSlide(drawerView: View, slideOffset: Float) {
|
||||
}
|
||||
|
||||
override fun onDrawerClosed(drawerView: View) {
|
||||
showGuide()
|
||||
}
|
||||
|
||||
override fun onDrawerOpened(drawerView: View) {
|
||||
}
|
||||
|
||||
})
|
||||
directoryContainer.layoutParams.width = width
|
||||
directoryRv.layoutParams.width = width
|
||||
|
||||
// 嵌入在首页时特殊处理
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
|
||||
root.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(requireContext()))
|
||||
root.setPadding(0, 8F.dip2px(), 0, 0)
|
||||
directoryRv.isNestedScrollingEnabled = false
|
||||
categoryRv.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_background.toColor(requireContext()))
|
||||
}
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
tvTagNumber.typeface =
|
||||
Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
|
||||
}
|
||||
|
||||
mViewModel?.directoriesLiveData?.observeNonNull(viewLifecycleOwner) {
|
||||
initDirectoryView(it)
|
||||
}
|
||||
|
||||
mViewModel?.selectedCountLiveData?.observeNonNull(viewLifecycleOwner) {
|
||||
mBinding?.run {
|
||||
if (it == 0) {
|
||||
confirmTv.text = "确定"
|
||||
} else {
|
||||
mViewModel?.run {
|
||||
if (selectedCategoryName != "全部") {
|
||||
selectedCategoryName = "全部"
|
||||
selectedCategoryPosition = if (mEntity?.hasSpecial == true) 1 else 0
|
||||
categoryRv.adapter?.notifyDataSetChanged()
|
||||
mCategoryV2ListFragment?.updateSubCategoryId("all")
|
||||
}
|
||||
}
|
||||
|
||||
confirmTv.text = "确定(已选${it})"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mViewModel?.sidebarsLiveData?.observe(viewLifecycleOwner, Observer {
|
||||
viewModel.sidebarsLiveData.observe(viewLifecycleOwner) { sidebars ->
|
||||
mBinding?.run {
|
||||
reuseLoading.root.visibility = View.GONE
|
||||
if (it != null) {
|
||||
if (sidebars != null) {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
categoryContainer.visibility = View.VISIBLE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
mEntity = it
|
||||
(mEntity!!.sidebars as ArrayList).run {
|
||||
mEntity = sidebars
|
||||
(sidebars.sidebars as ArrayList).run {
|
||||
val allEntity = SidebarsEntity.SidebarEntity(name = "全部", categoryId = "all")
|
||||
if (mEntity!!.hasSpecial) {
|
||||
add(0, allEntity)
|
||||
if (sidebars.hasSpecial) {
|
||||
val specialEntity = SidebarsEntity.SidebarEntity(name = "精选")
|
||||
add(0, specialEntity)
|
||||
add(1, allEntity)
|
||||
} else {
|
||||
add(0, allEntity)
|
||||
add(1, specialEntity)
|
||||
}
|
||||
}
|
||||
initView()
|
||||
@ -165,12 +119,70 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
reuseNoConnection.root.setOnClickListener {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
reuseLoading.root.visibility = View.VISIBLE
|
||||
mViewModel?.getSidebars()
|
||||
mViewModel?.getCategoryDirectories()
|
||||
viewModel.loadData(pageId, pageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
viewModel.directoriesLiveData.observe(viewLifecycleOwner) {
|
||||
searchCategoryPop?.setData(it)
|
||||
}
|
||||
|
||||
viewModel.selectedSubCategories.observe(viewLifecycleOwner) { selectedList ->
|
||||
searchCategoryPop?.updateCategorySelected(selectedList)
|
||||
updateMoreCategory(selectedList.size)
|
||||
}
|
||||
|
||||
viewModel.selectedSidebarsPosition.observe(viewLifecycleOwner) {
|
||||
mLastSelectedPosition = it
|
||||
onSelectedPositionChanged(it)
|
||||
|
||||
val adapter = mBinding?.categoryRv?.adapter
|
||||
if (adapter is CategoryV2Adapter) {
|
||||
adapter.selectPosition(it)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.notifySubCategorySelected.observe(
|
||||
viewLifecycleOwner,
|
||||
EventObserver {
|
||||
searchCategoryPop?.notifyItemSelectedChanged(it)
|
||||
})
|
||||
}
|
||||
|
||||
private fun createSearchPop(isAutoRequestFocus: Boolean): SearchCategoryPop {
|
||||
val height = mBinding!!.root.height
|
||||
return SearchCategoryPop.newInstance(requireContext(), height, isAutoRequestFocus, pageId, pageName).apply {
|
||||
val data = viewModel.directoriesLiveData.value ?: listOf()
|
||||
setData(data)
|
||||
val selectedList = viewModel.selectedSubCategories.value
|
||||
updateCategorySelected(selectedList)
|
||||
setOnSearchCategoryListener(object : SearchCategoryPop.OnSearchCategoryListener {
|
||||
override fun isEnableSelected(): Boolean {
|
||||
val size = viewModel.selectedSubCategories.value?.size ?: 0
|
||||
return size < 5
|
||||
}
|
||||
|
||||
override fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags) {
|
||||
viewModel.addSubCategorySelected(selected)
|
||||
}
|
||||
|
||||
override fun onItemRemoved(parentId: String, subCategoryId: String) {
|
||||
viewModel.removeSubCategorySelected(parentId, subCategoryId, "全部游戏")
|
||||
}
|
||||
|
||||
override fun onResetSelected() {
|
||||
viewModel.clearSelectedTag()
|
||||
viewModel.updateGameFiltered()
|
||||
}
|
||||
|
||||
override fun onSubmit() {
|
||||
viewModel.logClickDetermine()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fun removeGuide() {
|
||||
@ -181,15 +193,7 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
|
||||
private fun showGuide() {
|
||||
if (!isAdded) return
|
||||
mBinding?.run {
|
||||
val isShow = SPUtils.getBoolean(Constants.SP_SHOW_CATEGORY_GUIDE)
|
||||
if (isShow) return
|
||||
|
||||
guideContainer.layoutParams = (guideContainer.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
val screenWidth = resources.displayMetrics.widthPixels
|
||||
leftMargin = screenWidth * 66F.dip2px() / 360F.dip2px()
|
||||
}
|
||||
guideContainer.visibility = View.VISIBLE
|
||||
|
||||
postDelayedRunnable({
|
||||
@ -204,7 +208,7 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
override fun onMenuItemClick(menuItem: MenuItem?) {
|
||||
menuItem?.run {
|
||||
if (itemId == R.id.menu_search) {
|
||||
LogUtils.uploadSearchGame("access_to_search", mCategoryTitle, "", "")
|
||||
LogUtils.uploadSearchGame("access_to_search", pageName, "", "")
|
||||
val intent = SearchActivity.getIntent(
|
||||
requireContext(),
|
||||
false,
|
||||
@ -217,63 +221,64 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initDirectoryView(list: List<CategoryEntity>) {
|
||||
mBinding?.run {
|
||||
mViewModel?.run {
|
||||
if (directoryRv.adapter != null) {
|
||||
(directoryRv.adapter as? CategoryDirectoryAdapter)?.setListData(list)
|
||||
} else {
|
||||
directoryRv.layoutManager = FixLinearLayoutManager(requireContext())
|
||||
directoryRv.adapter = CategoryDirectoryAdapter(
|
||||
requireContext(),
|
||||
this,
|
||||
list
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
resetTv.setOnClickListener {
|
||||
mViewModel?.logClickReset("全部类别")
|
||||
confirmTv.text = "确定"
|
||||
mViewModel?.resetDirectoryList()
|
||||
mCategoryV2ListFragment?.changeCategoryTab()
|
||||
}
|
||||
|
||||
confirmTv.setOnClickListener {
|
||||
mViewModel?.logClickDetermine()
|
||||
drawerLayout.closeDrawer(GravityCompat.START)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
if (mEntity?.sidebars?.isNullOrEmpty() == true || mViewModel == null) return
|
||||
if (mEntity?.sidebars.isNullOrEmpty()) return
|
||||
initSelectedCategory()
|
||||
initCategoryRv()
|
||||
initContentFragment()
|
||||
|
||||
mBinding?.run {
|
||||
|
||||
vSearchCategory.setOnClickListener {
|
||||
SensorsBridge.logClassificationSearch(pageId, pageName)
|
||||
removeGuide()
|
||||
showSearchPop(true)
|
||||
}
|
||||
|
||||
vMoreCategory.setOnClickListener {
|
||||
removeGuide()
|
||||
showSearchPop(false)
|
||||
}
|
||||
}
|
||||
|
||||
val isShow = SPUtils.getBoolean(Constants.SP_SHOW_CATEGORY_GUIDE)
|
||||
if (!isShow) {
|
||||
postDelayedRunnable({
|
||||
showSearchPop(false)
|
||||
searchCategoryPop?.setOnDismissListener {
|
||||
showGuide()
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun showSearchPop(isAutoRequestFocus: Boolean) {
|
||||
mBinding?.run {
|
||||
val location = IntArray(2)
|
||||
vSearchCategory.getLocationOnScreen(location)
|
||||
|
||||
val popTop = location[1] - 8F.dip2px()
|
||||
searchCategoryPop = createSearchPop(isAutoRequestFocus)
|
||||
searchCategoryPop?.showAtLocation(vSearchCategory, Gravity.TOP, 0, popTop)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSelectedCategory() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
if (mLastSelectedPosition != -1) {
|
||||
selectedCategoryPosition = mLastSelectedPosition
|
||||
selectedCategoryName = sidebars[mLastSelectedPosition].name
|
||||
} else {
|
||||
selectedCategoryPosition = 0
|
||||
selectedCategoryName = sidebars[0].name
|
||||
}
|
||||
}
|
||||
if (mLastSelectedPosition != -1) {
|
||||
viewModel.selectSidebarsPosition(mLastSelectedPosition)
|
||||
} else {
|
||||
// 默认选中第 0 个 位置
|
||||
viewModel.selectSidebarsPosition(0)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private fun initCategoryRv() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
viewModel.run {
|
||||
mBinding?.categoryRv?.layoutManager = FixLinearLayoutManager(requireContext())
|
||||
mBinding?.categoryRv?.adapter = CategoryV2Adapter(
|
||||
requireContext(),
|
||||
this@CategoryV2Fragment,
|
||||
this,
|
||||
sidebars
|
||||
)
|
||||
@ -281,226 +286,93 @@ class CategoryV2Fragment : LazyFragment() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun initContentFragment() {
|
||||
mEntity?.apply {
|
||||
mViewModel?.apply {
|
||||
if (hasSpecial && selectedCategoryPosition == 0) {
|
||||
initSpecialCatalogFragment()
|
||||
} else {
|
||||
initCategoryV2ListFragment()
|
||||
private fun onSelectedPositionChanged(position: Int) {
|
||||
mEntity?.run {
|
||||
viewModel.run {
|
||||
clearSelectedTag()
|
||||
childFragmentManager.fragments.find { it.isAdded }
|
||||
val targetFragment =
|
||||
if (hasSpecial && position == 1) {
|
||||
val fragment = childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
?: SpecialCatalogFragment()
|
||||
fragment.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to pageId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to pageName,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
fragment
|
||||
|
||||
// 第一次点"全部"tab展开全部类别选择框
|
||||
// 加延迟是为了防止卡顿
|
||||
if (SPUtils.getBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, true)) {
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, false)
|
||||
mBinding?.drawerLayout?.postDelayed({
|
||||
tryCatchInRelease { openDrawer() }
|
||||
}, 500L)
|
||||
} else {
|
||||
val fragment = (childFragmentManager.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
?: CategoryV2ListFragment())
|
||||
fragment.arguments = bundleOf(
|
||||
EntranceConsts.KEY_PAGE_ID to id,
|
||||
EntranceConsts.KEY_PAGE_NAME to pageName,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
fragment
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initSpecialCatalogFragment() {
|
||||
mEntity?.run {
|
||||
mSpecialCatalogFragment = childFragmentManager
|
||||
.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
as? SpecialCatalogFragment ?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initCategoryV2ListFragment() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
mCategoryV2ListFragment = childFragmentManager
|
||||
.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
as? CategoryV2ListFragment ?: CategoryV2ListFragment()
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[selectedCategoryPosition].categoryId,
|
||||
EntranceConsts.KEY_CATEGORY_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mCategoryV2ListFragment!!,
|
||||
CategoryV2ListFragment::class.java.name
|
||||
)
|
||||
.replace(R.id.gamesContainer, targetFragment, targetFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCategory(position: Int) {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
resetDirectoryList()
|
||||
|
||||
if (hasSpecial) {
|
||||
if (selectedCategoryPosition == 0) {
|
||||
mCategoryV2ListFragment = childFragmentManager
|
||||
.findFragmentByTag(CategoryV2ListFragment::class.java.name)
|
||||
as? CategoryV2ListFragment ?: CategoryV2ListFragment()
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mCategoryV2ListFragment!!,
|
||||
CategoryV2ListFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
} else {
|
||||
if (position == 0) {
|
||||
removeGuide()
|
||||
|
||||
mSpecialCatalogFragment = childFragmentManager
|
||||
.findFragmentByTag(SpecialCatalogFragment::class.java.name)
|
||||
as? SpecialCatalogFragment ?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to true,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
childFragmentManager
|
||||
.beginTransaction()
|
||||
.replace(
|
||||
R.id.gamesContainer,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
)
|
||||
.commitAllowingStateLoss()
|
||||
} else {
|
||||
if (mCategoryV2ListFragment?.isStateSaved == false) {
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
}
|
||||
mCategoryV2ListFragment?.changeCategoryTab(sidebars[position].categoryId)
|
||||
}
|
||||
}
|
||||
|
||||
// 第一次点"全部"tab展开全部类别选择框
|
||||
// 加延迟是为了防止卡顿
|
||||
if (position == 1 && SPUtils.getBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, true)) {
|
||||
SPUtils.setBoolean(Constants.SP_FIRST_ENTER_CATEGORY_V2, false)
|
||||
mBinding?.drawerLayout?.postDelayed({
|
||||
tryCatchInRelease { openDrawer() }
|
||||
}, 200L)
|
||||
}
|
||||
} else {
|
||||
if (mCategoryV2ListFragment?.isStateSaved == false) {
|
||||
mCategoryV2ListFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATEGORY_ID to id,
|
||||
EntranceConsts.KEY_SUB_CATEGORY_ID to sidebars[position].categoryId,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCategoryTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST to arguments?.getParcelableArrayList<ExposureSource>(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE_LIST
|
||||
),
|
||||
EntranceConsts.KEY_LAST_PAGE_DATA to mLastPageDataMap
|
||||
)
|
||||
}
|
||||
mCategoryV2ListFragment?.changeCategoryTab(sidebars[position].categoryId)
|
||||
}
|
||||
selectedCategoryPosition = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun openDirectoryLayout() {
|
||||
private fun updateMoreCategory(size: Int) {
|
||||
mBinding?.run {
|
||||
var i = 0
|
||||
mViewModel?.run {
|
||||
mEntity?.run {
|
||||
val sidebar = sidebars[selectedCategoryPosition]
|
||||
if (sidebar.name != "全部") {
|
||||
directories.forEachIndexed { index, entity ->
|
||||
if (sidebar.type == "level_one") {
|
||||
if (sidebar.categoryId == entity.id) {
|
||||
i = index
|
||||
return@run
|
||||
}
|
||||
} else {
|
||||
if (sidebar.parentId == entity.id) {
|
||||
i = index
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if (sidebar.name == "全部" && selectedCategoryList.isNotEmpty()) {
|
||||
i = selectedCategoryList[0].primaryIndex
|
||||
}
|
||||
if (size > 0) {
|
||||
vMoreCategory.setBackgroundResource(R.drawable.bg_more_category_filtered)
|
||||
TextViewCompat.setCompoundDrawableTintList(
|
||||
tvMoreCategory,
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
|
||||
)
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(requireContext()))
|
||||
tvTagNumber.goneIf(false) {
|
||||
tvTagNumber.text = "$size"
|
||||
}
|
||||
} else {
|
||||
vMoreCategory.setBackgroundResource(R.drawable.bg_more_category_default)
|
||||
TextViewCompat.setCompoundDrawableTintList(
|
||||
tvMoreCategory,
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
)
|
||||
tvMoreCategory.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(requireContext()))
|
||||
tvTagNumber.goneIf(true)
|
||||
}
|
||||
openDrawer()
|
||||
(directoryRv.layoutManager as? LinearLayoutManager)?.scrollToPositionWithOffset(i, 0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDrawer() {
|
||||
mBinding?.drawerLayout?.openDrawer(GravityCompat.START)
|
||||
|
||||
mHomeViewModel?.let {
|
||||
mBinding?.directoryContainer?.setPadding(
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
requireContext().resources.getDimension(com.gh.gamecenter.common.R.dimen.main_bottom_tab_height).toInt() - it.appBarOffset
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
getItemMenu(R.id.menu_search)?.setIcon(R.drawable.ic_column_search)
|
||||
mBinding?.categoryRv?.adapter?.run {
|
||||
mBinding?.categoryRv?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
mBinding?.directoryRv?.adapter?.run {
|
||||
mBinding?.directoryRv?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
mBinding?.run {
|
||||
categoryRv.adapter?.run {
|
||||
categoryRv.recycledViewPool.clear()
|
||||
notifyItemRangeChanged(0, itemCount, "")
|
||||
}
|
||||
|
||||
val selectedTagsSize = viewModel.selectedSubCategories.value?.size ?: 0
|
||||
updateMoreCategory(selectedTagsSize)
|
||||
|
||||
context?.let {
|
||||
ivSearchCategory.imageTintList =
|
||||
ColorStateList.valueOf(com.gh.gamecenter.common.R.color.text_instance.toColor(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SPECIAL_CATEGORY_POSITION = 1
|
||||
}
|
||||
}
|
||||
@ -76,8 +76,15 @@ class CategoryV2ListAdapter(
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
CategoryGameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
FooterViewHolder(
|
||||
mLayoutInflater.inflate(
|
||||
com.gh.gamecenter.common.R.layout.refresh_footerview,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -94,20 +101,12 @@ class CategoryV2ListAdapter(
|
||||
holder.bindGameItem(gameEntity)
|
||||
holder.initServerType(gameEntity)
|
||||
|
||||
val categoryTitle = mCategoryViewModel.categoryTitle
|
||||
val selectedCategoryName = mCategoryViewModel.selectedCategoryName
|
||||
val builder = StringBuilder()
|
||||
mCategoryViewModel.selectedCategoryList.run {
|
||||
forEachIndexed { index, entity ->
|
||||
builder.append(entity.name)
|
||||
if (index != size - 1) {
|
||||
builder.append("_")
|
||||
}
|
||||
}
|
||||
}
|
||||
val selectedSubCatalogName = builder.toString()
|
||||
val sortType = mViewModel.sortType.value
|
||||
val sortSize = mViewModel.sortSize.text
|
||||
val categoryTitle = mCategoryViewModel.pageName
|
||||
val selectedCategoryName = mCategoryViewModel.selectedSidebarsName
|
||||
val selectedSubCatalogName =
|
||||
mCategoryViewModel.selectedSubCategories.value?.joinToString("-") { it.category.name ?: "" }
|
||||
val sortType = mViewModel.gameFiltered.sortType.value
|
||||
val sortSize = mViewModel.gameFiltered.size.text
|
||||
|
||||
val exposureSources = ArrayList<ExposureSource>()
|
||||
if (!mViewModel.exposureSourceList.isNullOrEmpty()) {
|
||||
@ -172,8 +171,13 @@ class CategoryV2ListAdapter(
|
||||
) {
|
||||
val trackEvent = JSONObject()
|
||||
try {
|
||||
trackEvent.put("navigation_bar_name", mCategoryViewModel.selectedCategoryName)
|
||||
trackEvent.put("game_tag", mCategoryViewModel.selectedCategoryList.map { it.name })
|
||||
trackEvent.put("navigation_bar_name", mCategoryViewModel.selectedSidebarsName)
|
||||
trackEvent.put(
|
||||
"game_tag",
|
||||
(mCategoryViewModel.selectedSubCategories.value ?: listOf())
|
||||
.map {
|
||||
it.category.name
|
||||
})
|
||||
trackEvent.put("game_status", gameEntity.category)
|
||||
trackEvent.put(
|
||||
"inclusion_size",
|
||||
@ -242,7 +246,7 @@ class CategoryV2ListAdapter(
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F
|
||||
BindingAdapters.setGameName(gameName, gameEntity, false)
|
||||
BindingAdapters.setGameTags(labelList, gameEntity)
|
||||
BindingAdapters.setGameTags(labelList, gameEntity, "")
|
||||
gameRating.setDrawableStart(if (gameEntity.commentCount > 3) com.gh.gamecenter.feature.R.drawable.game_horizontal_rating.toDrawable() else null)
|
||||
gameRating.text = if (gameEntity.commentCount > 3) {
|
||||
if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString()
|
||||
@ -266,18 +270,24 @@ class CategoryV2ListAdapter(
|
||||
binding.gameKaifuType.visibility = View.GONE
|
||||
binding.gameKaifuType.text = ""
|
||||
}
|
||||
|
||||
serverLabel != null -> {
|
||||
binding.gameKaifuType.visibility = View.VISIBLE
|
||||
binding.gameKaifuType.text = serverLabel.value
|
||||
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)
|
||||
binding.gameKaifuType.setTextColor(com.gh.gamecenter.common.R.color.white.toColor(binding.root.context))
|
||||
}
|
||||
}
|
||||
|
||||
else -> binding.gameKaifuType.visibility = View.GONE
|
||||
}
|
||||
|
||||
|
||||
@ -2,9 +2,10 @@ package com.gh.gamecenter.category2
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
@ -13,16 +14,17 @@ import com.gh.common.xapk.XapkUnzipStatus
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.observeNonNull
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.databinding.FragmentCategoryListBinding
|
||||
import com.gh.gamecenter.databinding.LayoutSelectedCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
@ -30,13 +32,18 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>() {
|
||||
|
||||
private var mCategoryId: String = ""
|
||||
private var mSubCategoryId: String = ""
|
||||
private var mCategoryTitle: String = ""
|
||||
private var pageId: String = ""
|
||||
private var pageName: String = ""
|
||||
private val parentViewModel by viewModels<CategoryV2ViewModel>(
|
||||
ownerProducer = { parentFragment ?: this }
|
||||
)
|
||||
|
||||
override fun isAutomaticLoad(): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
private var mAdapter: CategoryV2ListAdapter? = null
|
||||
private var mSelectedViewList = ArrayList<View>()
|
||||
private var mBinding: FragmentCategoryListBinding? = null
|
||||
private var mCategoryViewModel: CategoryV2ViewModel? = null
|
||||
private var mLastPageDataMap: HashMap<String, String>? = null
|
||||
private val mDataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
@ -52,6 +59,12 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedTagsAdapter by lazy {
|
||||
SelectedTagsAdapter {
|
||||
parentViewModel.removeSubCategorySelected(it.parentId, it.category.id, "游戏列表")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
@ -60,8 +73,7 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
override fun provideListViewModel() =
|
||||
viewModelProvider<CategoryV2ListViewModel>(
|
||||
CategoryV2ListViewModel.Factory(
|
||||
mCategoryId,
|
||||
mSubCategoryId,
|
||||
pageId,
|
||||
arguments?.getParcelableArrayList(EntranceConsts.KEY_EXPOSURE_SOURCE_LIST)
|
||||
)
|
||||
)
|
||||
@ -70,12 +82,7 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
?: CategoryV2ListAdapter(
|
||||
requireContext(),
|
||||
mListViewModel ?: provideListViewModel(),
|
||||
mCategoryViewModel ?: viewModelProviderFromParent(
|
||||
CategoryV2ViewModel.Factory(
|
||||
mCategoryId,
|
||||
mCategoryTitle
|
||||
), mCategoryId
|
||||
),
|
||||
parentViewModel,
|
||||
mEntrance,
|
||||
mLastPageDataMap
|
||||
).apply { mAdapter = this }
|
||||
@ -83,12 +90,10 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
override fun getItemDecoration() = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mCategoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
|
||||
mSubCategoryId = arguments?.getString(EntranceConsts.KEY_SUB_CATEGORY_ID) ?: ""
|
||||
mCategoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
|
||||
pageId = arguments?.getString(EntranceConsts.KEY_PAGE_ID) ?: ""
|
||||
pageName = arguments?.getString(EntranceConsts.KEY_PAGE_NAME) ?: ""
|
||||
mLastPageDataMap = arguments?.getSerializable(EntranceConsts.KEY_LAST_PAGE_DATA) as? HashMap<String, String>
|
||||
mCategoryViewModel =
|
||||
viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId)
|
||||
|
||||
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
@ -104,19 +109,8 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
|
||||
initFilterView()
|
||||
|
||||
mListViewModel?.refresh?.observeNonNull(viewLifecycleOwner) { onRefresh() }
|
||||
mCategoryViewModel?.run {
|
||||
categoryPositionLiveData.observeNonNull(viewLifecycleOwner) {
|
||||
directories[it.first].data?.get(it.second)?.run {
|
||||
mBinding?.selectedCategoryContainer?.visibility = View.VISIBLE
|
||||
|
||||
if (selected) {
|
||||
addCategory(this)
|
||||
} else {
|
||||
removeCategory(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
mListViewModel?.refresh?.observeNonNull(viewLifecycleOwner) {
|
||||
onRefresh()
|
||||
}
|
||||
|
||||
mListRv.addOnScrollListener(ExposureListener(this, provideListAdapter()))
|
||||
@ -132,19 +126,40 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
.show()
|
||||
|
||||
mBinding?.reuseNoneData?.reuseResetLoadTv?.setOnClickListener {
|
||||
mCategoryViewModel?.run {
|
||||
// 重试时,
|
||||
// 清空所有筛选条件
|
||||
with(parentViewModel) {
|
||||
logClickReset("游戏列表")
|
||||
resetDirectoryList()
|
||||
clearSelectedTag()
|
||||
// 移除大小限制
|
||||
mBinding?.filterContainer?.resetSortSize()
|
||||
val size = SubjectSettingEntity.Size()
|
||||
updateGameFiltered(size)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
with(parentViewModel) {
|
||||
gameFiltered.observe(viewLifecycleOwner) {
|
||||
mListViewModel.updateSortConfig(it)
|
||||
}
|
||||
|
||||
selectedSubCategories.observe(viewLifecycleOwner) {
|
||||
updateSelectedTags(it)
|
||||
}
|
||||
resetSortSize()
|
||||
changeCategoryTab()
|
||||
// openDirectoryLayout()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetSortSize() {
|
||||
mBinding?.filterContainer?.resetSortSize()
|
||||
mListViewModel?.sortSize = SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小")
|
||||
private fun updateSelectedTags(selectedTags: List<CategoryV2ViewModel.SelectedTags>) {
|
||||
mBinding?.run {
|
||||
flTagsContainer.goneIf(selectedTags.isEmpty()) {
|
||||
if (rvTags.adapter == null) {
|
||||
rvTags.layoutManager = LinearLayoutManager(requireContext(), RecyclerView.HORIZONTAL, false)
|
||||
rvTags.adapter = selectedTagsAdapter
|
||||
}
|
||||
selectedTagsAdapter.submitList(selectedTags)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -154,22 +169,19 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
|
||||
setOnConfigSetupListener(object : CategoryFilterView.OnCategoryFilterSetupListener {
|
||||
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
|
||||
mListViewModel?.updateSortConfig(sortSize = sortSize)
|
||||
parentViewModel.updateGameFiltered(size = sortSize)
|
||||
}
|
||||
|
||||
override fun onSetupSortType(sortType: CategoryFilterView.SortType) {
|
||||
mListViewModel?.updateSortConfig(sortType = sortType)
|
||||
parentViewModel.updateGameFiltered(sortType = sortType)
|
||||
}
|
||||
|
||||
override fun onSetupSortCategory() {
|
||||
openDirectoryLayout()
|
||||
override fun getPopHeight(): Int {
|
||||
return (mBinding?.root?.height ?: 0) - (mBinding?.flTagsContainer?.height ?: 0)
|
||||
}
|
||||
})
|
||||
|
||||
setOnFilterClickListener(object : CategoryFilterView.OnFilterClickListener {
|
||||
override fun onCategoryClick() {
|
||||
removeGuide()
|
||||
}
|
||||
|
||||
override fun onTypeClick() {
|
||||
removeGuide()
|
||||
@ -183,101 +195,6 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSubCategoryId(id: String) {
|
||||
mSubCategoryId = id
|
||||
}
|
||||
|
||||
fun changeCategoryTab(categoryId: String? = null) {
|
||||
mSelectedViewList.clear()
|
||||
mBinding?.selectedCategoryContainer?.run {
|
||||
removeAllViews()
|
||||
visibility = View.GONE
|
||||
}
|
||||
if (categoryId != null) mSubCategoryId = categoryId
|
||||
mListViewModel?.updateSortConfig(categoryIds = mSubCategoryId)
|
||||
}
|
||||
|
||||
private fun addCategory(entity: CategoryEntity) {
|
||||
addCategoryView(entity)
|
||||
mCategoryViewModel?.selectedCategoryList?.add(entity)
|
||||
updateCategoryGame()
|
||||
}
|
||||
|
||||
private fun removeCategory(entity: CategoryEntity) {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
if (isEmpty()) return
|
||||
|
||||
removeCategoryView(entity.name ?: "")
|
||||
remove(entity)
|
||||
if (size == 0) {
|
||||
mBinding?.selectedCategoryContainer?.visibility = View.GONE
|
||||
mListViewModel?.updateSortConfig(categoryIds = mSubCategoryId)
|
||||
} else {
|
||||
updateCategoryGame()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addCategoryView(entity: CategoryEntity) {
|
||||
val binding = LayoutSelectedCategoryBinding.inflate(layoutInflater).apply {
|
||||
val params =
|
||||
FlexboxLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
params.setMargins(0, 8F.dip2px(), 8F.dip2px(), 0)
|
||||
params.height = 24F.dip2px()
|
||||
root.layoutParams = params
|
||||
|
||||
name.text = entity.name
|
||||
root.setOnClickListener {
|
||||
removeCategoryAndNotify(entity)
|
||||
}
|
||||
}
|
||||
|
||||
mSelectedViewList.add(binding.root)
|
||||
mBinding?.selectedCategoryContainer?.addView(binding.root)
|
||||
}
|
||||
|
||||
private fun removeCategoryView(categoryName: String) {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
var i = 0
|
||||
forEachIndexed { index, categoryEntity ->
|
||||
if (categoryName == categoryEntity.name) {
|
||||
i = index
|
||||
}
|
||||
}
|
||||
|
||||
if (i < mSelectedViewList.size) {
|
||||
mBinding?.selectedCategoryContainer?.removeView(mSelectedViewList[i])
|
||||
mSelectedViewList.removeAt(i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeCategoryAndNotify(entity: CategoryEntity) {
|
||||
removeCategory(entity)
|
||||
entity.selected = false
|
||||
mCategoryViewModel?.run {
|
||||
if (selectedCount > 0) {
|
||||
selectedCount--
|
||||
postSelectedCount()
|
||||
postCategoryDirectoryList()
|
||||
logClickClassificationDelete(entity.primaryIndex, entity.name ?: "", "游戏列表")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateCategoryGame() {
|
||||
mCategoryViewModel?.selectedCategoryList?.run {
|
||||
val categoryIds = StringBuilder()
|
||||
forEachIndexed { index, s ->
|
||||
categoryIds.append(s.id)
|
||||
if (index != size - 1) {
|
||||
categoryIds.append("-")
|
||||
}
|
||||
}
|
||||
mListViewModel?.updateSortConfig(categoryIds = categoryIds.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun removeGuide() {
|
||||
(parentFragment as? CategoryV2Fragment)?.removeGuide()
|
||||
}
|
||||
@ -328,10 +245,6 @@ class CategoryV2ListFragment : ListFragment<GameEntity, CategoryV2ListViewModel>
|
||||
}
|
||||
}
|
||||
|
||||
fun openDirectoryLayout() {
|
||||
(parentFragment as? CategoryV2Fragment)?.openDirectoryLayout()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.filterContainer?.run {
|
||||
|
||||
@ -4,14 +4,13 @@ import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.common.exposure.ExposureUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
@ -20,14 +19,13 @@ import io.reactivex.Single
|
||||
class CategoryV2ListViewModel(
|
||||
application: Application,
|
||||
val categoryId: String,
|
||||
var categoryIds: String,
|
||||
var exposureSourceList: List<ExposureSource>?
|
||||
) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
val refresh = MutableLiveData<Boolean>()
|
||||
|
||||
var sortType = CategoryFilterView.SortType.RECOMMENDED
|
||||
var sortSize = SubjectSettingEntity.Size()
|
||||
var gameFiltered = CategoryV2ViewModel.GameFiltered()
|
||||
private set
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
|
||||
|
||||
@ -51,52 +49,36 @@ class CategoryV2ListViewModel(
|
||||
}
|
||||
|
||||
fun updateSortConfig(
|
||||
sortSize: SubjectSettingEntity.Size? = null,
|
||||
sortType: CategoryFilterView.SortType? = null,
|
||||
categoryIds: String? = null
|
||||
gameFiltered: CategoryV2ViewModel.GameFiltered
|
||||
) {
|
||||
when {
|
||||
sortSize != null && sortSize != this.sortSize -> {
|
||||
this.sortSize = sortSize
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
sortType != null && sortType != this.sortType -> {
|
||||
this.sortType = sortType
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
categoryIds != null && categoryIds != this.categoryIds -> {
|
||||
this.categoryIds = categoryIds
|
||||
refresh.postValue(true)
|
||||
}
|
||||
}
|
||||
this.gameFiltered = gameFiltered
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
private fun getFilter(): String? {
|
||||
|
||||
return UrlFilterUtils.getFilterQuery(
|
||||
"category_ids", categoryIds,
|
||||
"min_size", sortSize.min.toString(),
|
||||
"max_size", sortSize.max.toString()
|
||||
"category_ids", gameFiltered.categoryIds.ifBlank { gameFiltered.sidebarCategoryId },
|
||||
"min_size", gameFiltered.size.min.toString(),
|
||||
"max_size", gameFiltered.size.max.toString()
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
private fun getSortType(): String? {
|
||||
return when (sortType) {
|
||||
return when (gameFiltered.sortType) {
|
||||
CategoryFilterView.SortType.RECOMMENDED -> "download:-1"
|
||||
CategoryFilterView.SortType.NEWEST -> "publish:-1"
|
||||
CategoryFilterView.SortType.RATING -> "star:-1"
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(val categoryId: String, val categoryIds: String, val exposureSourceList: List<ExposureSource>?) :
|
||||
class Factory(val categoryId: String, val exposureSourceList: List<ExposureSource>?) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CategoryV2ListViewModel(
|
||||
HaloApp.getInstance().application,
|
||||
categoryId,
|
||||
categoryIds,
|
||||
exposureSourceList
|
||||
) as T
|
||||
}
|
||||
|
||||
@ -1,160 +1,214 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.common.view.CategoryFilterView
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.entity.SidebarsEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
|
||||
class CategoryV2ViewModel(
|
||||
application: Application,
|
||||
private val mCategoryId: String,
|
||||
val categoryTitle: String
|
||||
) : AndroidViewModel(application) {
|
||||
class CategoryV2ViewModel : ViewModel() {
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
var sidebarsLiveData = MutableLiveData<SidebarsEntity>()
|
||||
var directories = ArrayList<CategoryEntity>()
|
||||
var sidebarsLiveData = MutableLiveData<SidebarsEntity?>()
|
||||
var directoriesLiveData = MutableLiveData<List<CategoryEntity>>()
|
||||
var selectedCount = 0
|
||||
var selectedCountLiveData = MutableLiveData<Int>()
|
||||
var categoryPositionLiveData = MutableLiveData<Pair<Int, Int>>()
|
||||
|
||||
var selectedCategoryName: String = ""
|
||||
var selectedCategoryPosition: Int = 0
|
||||
var selectedCategoryList = ArrayList<CategoryEntity>()
|
||||
var entrance: String = ""
|
||||
private var pageId = ""
|
||||
var pageName = ""
|
||||
private set
|
||||
|
||||
init {
|
||||
getSidebars()
|
||||
getCategoryDirectories()
|
||||
fun init(entrance: String) {
|
||||
this.entrance = entrance
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getSidebars() {
|
||||
api.getSidebars(mCategoryId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<SidebarsEntity>() {
|
||||
override fun onSuccess(data: SidebarsEntity) {
|
||||
sidebarsLiveData.postValue(data)
|
||||
fun loadData(pageId: String, pageName: String) {
|
||||
this.pageId = pageId
|
||||
this.pageName = pageName
|
||||
|
||||
val sidebarsObservable = api.getSidebars(pageId)
|
||||
val directoriesObservable = api.getCategoryDirectories(pageId)
|
||||
.onErrorReturnItem(listOf())
|
||||
sidebarsObservable.zipWith(directoriesObservable) { t1, t2 ->
|
||||
t1 to t2
|
||||
}.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<Pair<SidebarsEntity, List<CategoryEntity>>>() {
|
||||
override fun onSuccess(data: Pair<SidebarsEntity, List<CategoryEntity>>) {
|
||||
val (sidebarsData, directories) = data
|
||||
directoriesLiveData.value = directories
|
||||
sidebarsLiveData.value = sidebarsData
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
sidebarsLiveData.postValue(null)
|
||||
}
|
||||
})
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getCategoryDirectories() {
|
||||
api.getCategoryDirectories(mCategoryId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<CategoryEntity>>() {
|
||||
override fun onSuccess(data: List<CategoryEntity>) {
|
||||
directories = ArrayList(data)
|
||||
postCategoryDirectoryList()
|
||||
fun clearSelectedTag() {
|
||||
logClickReset("全部类别")
|
||||
val filteredList = _selectedSubCategories.value
|
||||
if (!filteredList.isNullOrEmpty()) {
|
||||
_selectedSubCategories.value = mutableListOf()
|
||||
|
||||
val directories = directoriesLiveData.value ?: return
|
||||
directories.forEach {
|
||||
it.data?.forEach { child ->
|
||||
child.selected = false
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun postSelectedCount() {
|
||||
selectedCountLiveData.postValue(selectedCount)
|
||||
}
|
||||
|
||||
fun postCategoryPosition(primaryIndex: Int, subIndex: Int) {
|
||||
categoryPositionLiveData.postValue(Pair(primaryIndex, subIndex))
|
||||
}
|
||||
|
||||
fun postCategoryDirectoryList() {
|
||||
directoriesLiveData.postValue(directories)
|
||||
}
|
||||
|
||||
fun resetDirectoryList() {
|
||||
selectedCount = 0
|
||||
selectedCategoryList.clear()
|
||||
directories.forEach {
|
||||
it.data?.forEach { entity ->
|
||||
entity.selected = false
|
||||
}
|
||||
directoriesLiveData.value = directories
|
||||
}
|
||||
postSelectedCount()
|
||||
postCategoryDirectoryList()
|
||||
}
|
||||
|
||||
fun logAppearance() {
|
||||
LogUtils.logCategoryV2AppearanceEvent(entrance, categoryTitle)
|
||||
LogUtils.logCategoryV2AppearanceEvent(entrance, pageName)
|
||||
}
|
||||
|
||||
fun logClickSide() {
|
||||
LogUtils.logCategoryV2ClickSideEvent(entrance, categoryTitle, selectedCategoryName, selectedCategoryPosition)
|
||||
val sidebars = sidebarsLiveData.value?.sidebars
|
||||
val selectedPosition = selectedSidebarsPosition.value ?: 0
|
||||
val selectedCategoryName = sidebars?.getOrNull(selectedPosition)?.name ?: ""
|
||||
LogUtils.logCategoryV2ClickSideEvent(entrance, pageName, selectedCategoryName, selectedPosition)
|
||||
}
|
||||
|
||||
fun logClickClassification(primaryIndex: Int, categoryName: String, position: Int) {
|
||||
private fun logClickClassification(selected: SelectedTags) {
|
||||
LogUtils.logCategoryV2ClickClassificationEvent(
|
||||
entrance,
|
||||
categoryTitle,
|
||||
selectedCategoryName,
|
||||
directories[primaryIndex].name,
|
||||
categoryName,
|
||||
primaryIndex,
|
||||
position
|
||||
pageName,
|
||||
selectedSidebarsName,
|
||||
selected.parentName,
|
||||
selected.category.name ?: "",
|
||||
selected.parentPosition,
|
||||
selected.position
|
||||
)
|
||||
}
|
||||
|
||||
fun logClickClassificationDelete(primaryIndex: Int, categoryName: String, location: String) {
|
||||
private fun logClickClassificationDelete(directoryName: String, categoryName: String, location: String) {
|
||||
LogUtils.logCategoryV2ClickClassificationDeleteEvent(
|
||||
entrance,
|
||||
categoryTitle,
|
||||
directories[primaryIndex].name,
|
||||
pageName,
|
||||
directoryName,
|
||||
categoryName,
|
||||
location
|
||||
)
|
||||
}
|
||||
|
||||
fun logClickDetermine() {
|
||||
val categoryName = StringBuilder()
|
||||
selectedCategoryList.forEachIndexed { index, s ->
|
||||
categoryName.append(s.name)
|
||||
if (index != selectedCategoryList.size - 1) {
|
||||
categoryName.append("+")
|
||||
}
|
||||
}
|
||||
LogUtils.logCategoryV2ClickDetermineEvent(entrance, categoryTitle, categoryName.toString())
|
||||
val categoryNames = selectedSubCategories.value?.joinToString("+") { it.category.name ?: "" }
|
||||
LogUtils.logCategoryV2ClickDetermineEvent(entrance, pageName, categoryNames ?: "")
|
||||
}
|
||||
|
||||
fun logClickReset(location: String) {
|
||||
val categoryName = StringBuilder()
|
||||
selectedCategoryList.forEachIndexed { index, s ->
|
||||
categoryName.append(s.name)
|
||||
if (index != selectedCategoryList.size - 1) {
|
||||
categoryName.append("+")
|
||||
}
|
||||
}
|
||||
LogUtils.logCategoryV2ClickResetEvent(entrance, categoryTitle, categoryName.toString(), location)
|
||||
val categoryName = selectedSubCategories.value?.joinToString("+") { it.category.name ?: "" }
|
||||
LogUtils.logCategoryV2ClickResetEvent(entrance, pageName, categoryName ?: "", location)
|
||||
}
|
||||
|
||||
class Factory(
|
||||
private val categoryId: String,
|
||||
private val categoryTitle: String
|
||||
) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CategoryV2ViewModel(
|
||||
HaloApp.getInstance().application,
|
||||
categoryId,
|
||||
categoryTitle
|
||||
) as T
|
||||
private val _notifySubCategorySelected = MutableLiveData<Event<String>>()
|
||||
val notifySubCategorySelected: LiveData<Event<String>> = _notifySubCategorySelected
|
||||
|
||||
private val _selectedSubCategories = MutableLiveData<List<SelectedTags>>()
|
||||
val selectedSubCategories: LiveData<List<SelectedTags>> = _selectedSubCategories
|
||||
fun addSubCategorySelected(selected: SelectedTags) {
|
||||
val list = _selectedSubCategories.value
|
||||
val newData = if (list == null) {
|
||||
mutableListOf(selected)
|
||||
} else {
|
||||
list + selected
|
||||
}
|
||||
selected.category.selected = true
|
||||
_selectedSubCategories.value = newData
|
||||
|
||||
// 当搜索条件发生变化时,侧边栏默认选中 “全部”
|
||||
selectSidebarsPosition(0, false)
|
||||
|
||||
updateGameFiltered()
|
||||
_notifySubCategorySelected.value = Event(selected.parentId)
|
||||
logClickClassification(selected)
|
||||
}
|
||||
|
||||
fun removeSubCategorySelected(parentId: String, categoryId: String, location: String) {
|
||||
val list = _selectedSubCategories.value ?: return
|
||||
val position = list.indexOfFirst {
|
||||
it.parentId == parentId && categoryId == it.category.id
|
||||
}
|
||||
if (position != -1) {
|
||||
val item = list[position]
|
||||
|
||||
item.category.selected = false
|
||||
_selectedSubCategories.value = list - item
|
||||
|
||||
updateGameFiltered()
|
||||
_notifySubCategorySelected.value = Event(parentId)
|
||||
|
||||
logClickClassificationDelete(item.parentName, item.category.name ?: "", location)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private val _selectedSidebarsPosition = MutableLiveData<Int>()
|
||||
val selectedSidebarsPosition: LiveData<Int> = _selectedSidebarsPosition
|
||||
val selectedSidebarsName: String
|
||||
get() = sidebarsLiveData.value?.sidebars?.getOrNull(selectedSidebarsPosition.value ?: 0)?.name ?: ""
|
||||
|
||||
fun selectSidebarsPosition(position: Int, triggerSearch: Boolean = true) {
|
||||
val oldPosition = _selectedSidebarsPosition.value ?: INVALID_POSITION
|
||||
if (position != oldPosition) {
|
||||
_selectedSidebarsPosition.value = position
|
||||
// 如果是点击搜索而被动切换到 “全部” tab,则这里不需要更新筛选条件
|
||||
if (triggerSearch && position != 1) {
|
||||
updateGameFiltered()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val _gameFiltered = MutableLiveData<GameFiltered>()
|
||||
val gameFiltered: LiveData<GameFiltered> = _gameFiltered
|
||||
fun updateGameFiltered(
|
||||
size: SubjectSettingEntity.Size? = null,
|
||||
sortType: CategoryFilterView.SortType? = null
|
||||
) {
|
||||
val oldFiltered = _gameFiltered.value
|
||||
val newSize = size ?: oldFiltered?.size ?: SubjectSettingEntity.Size()
|
||||
val newSortType = sortType ?: oldFiltered?.sortType ?: CategoryFilterView.SortType.RECOMMENDED
|
||||
val selectedSidebarPosition = selectedSidebarsPosition.value ?: 0
|
||||
val categoryIds = selectedSubCategories.value?.joinToString("-") { it.category.id } ?: ""
|
||||
val sidebarCategoryId =
|
||||
sidebarsLiveData.value?.sidebars?.getOrNull(selectedSidebarPosition)?.categoryId ?: "all"
|
||||
_gameFiltered.value = GameFiltered(newSize, newSortType, categoryIds, sidebarCategoryId)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val INVALID_POSITION = -1
|
||||
}
|
||||
|
||||
data class SelectedTags(
|
||||
val parentId: String,
|
||||
val parentName: String,
|
||||
val parentPosition: Int,
|
||||
val category: CategoryEntity,
|
||||
val position: Int
|
||||
)
|
||||
|
||||
data class GameFiltered(
|
||||
val size: SubjectSettingEntity.Size = SubjectSettingEntity.Size(),
|
||||
val sortType: CategoryFilterView.SortType = CategoryFilterView.SortType.RECOMMENDED,
|
||||
val categoryIds: String = "",
|
||||
val sidebarCategoryId: String = "全部"
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,292 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.WindowManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.blankj.utilcode.util.KeyboardUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.common.view.BugFixedPopupWindow
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.PopSearchCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import me.xdrop.fuzzywuzzy.FuzzySearch
|
||||
|
||||
class SearchCategoryPop(
|
||||
height: Int,
|
||||
private val isAutoRequestFocus: Boolean,
|
||||
private val pageId: String,
|
||||
private val pageName: String,
|
||||
private val binding: PopSearchCategoryBinding
|
||||
) : BugFixedPopupWindow(binding.root, ViewGroup.LayoutParams.MATCH_PARENT, height) {
|
||||
|
||||
private var listener: OnSearchCategoryListener? = null
|
||||
private val handler = Handler(Looper.getMainLooper())
|
||||
private var searchDataList: List<CategoryV2ViewModel.SelectedTags>? = null
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
private var isSearching = false
|
||||
|
||||
private val adapter by lazy {
|
||||
CategoryDirectoryAdapter(object : OnSearchCategoryListener {
|
||||
override fun isEnableSelected(): Boolean {
|
||||
return listener?.isEnableSelected() ?: false
|
||||
}
|
||||
|
||||
override fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags) {
|
||||
listener?.onItemSelected(selected)
|
||||
|
||||
}
|
||||
|
||||
override fun onItemRemoved(parentId: String, subCategoryId: String) {
|
||||
listener?.onItemRemoved(parentId, subCategoryId)
|
||||
}
|
||||
|
||||
override fun onResetSelected() {
|
||||
listener?.onResetSelected()
|
||||
}
|
||||
|
||||
override fun onSubmit() {
|
||||
listener?.onSubmit()
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
private val resultAdapter by lazy {
|
||||
SearchCategoryResultsAdapter {
|
||||
SensorsBridge.logClassificationSearchReturnClick(
|
||||
pageId,
|
||||
pageName,
|
||||
binding.searchView.searchKey,
|
||||
it.category.name ?: ""
|
||||
)
|
||||
if (listener?.isEnableSelected() == true) {
|
||||
clearSearchKey()
|
||||
listener?.onItemSelected(it)
|
||||
} else {
|
||||
ToastUtils.toast(R.string.selected_category_tags_max_toast.toResString())
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedTagAdapter by lazy {
|
||||
SelectedTagsAdapter {
|
||||
listener?.onItemRemoved(it.parentId, it.category.id)
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
isOutsideTouchable = true
|
||||
setBackgroundDrawable(ColorDrawable(Color.TRANSPARENT))
|
||||
isFocusable = true
|
||||
inputMethodMode = INPUT_METHOD_NEEDED
|
||||
softInputMode = WindowManager.LayoutParams.SOFT_INPUT_ADJUST_NOTHING
|
||||
|
||||
initView()
|
||||
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
binding.rvCategory.layoutManager = LinearLayoutManager(binding.root.context)
|
||||
binding.rvCategory.adapter = adapter
|
||||
|
||||
binding.tvSelectedNumber.typeface =
|
||||
Typeface.createFromAsset(binding.root.context.assets, Constants.DIN_FONT_PATH)
|
||||
|
||||
binding.root.setOnClickListener {
|
||||
dismiss()
|
||||
}
|
||||
|
||||
binding.clContent.setOnClickListener {
|
||||
// 不需要具体实现,只是为了拦截 root 的点击事件
|
||||
}
|
||||
|
||||
binding.tvReset.setOnClickListener {
|
||||
listener?.onResetSelected()
|
||||
}
|
||||
|
||||
binding.vSubmit.setOnClickListener {
|
||||
dismiss()
|
||||
listener?.onSubmit()
|
||||
}
|
||||
|
||||
binding.searchView.addTextChangedListener {
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
val key = it?.toString() ?: ""
|
||||
if (key.isEmpty()) {
|
||||
changeToSearching(false)
|
||||
} else {
|
||||
handler.postDelayed({
|
||||
search(it?.toString() ?: "")
|
||||
}, SEARCH_DELAY_DURATION)
|
||||
}
|
||||
}
|
||||
|
||||
if (!isAutoRequestFocus) {
|
||||
binding.searchView.setEditTextOnFocusChangeListener { _, hasFocus ->
|
||||
if (hasFocus) {
|
||||
SensorsBridge.logClassificationSearch(pageId, pageName)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
fun setData(data: List<CategoryEntity>) {
|
||||
searchDataList = data
|
||||
.asSequence()
|
||||
.mapIndexed { index, parent ->
|
||||
parent.data?.mapIndexed { childIndex, child ->
|
||||
CategoryV2ViewModel.SelectedTags(parent.id, parent.name ?: "", index, child, childIndex)
|
||||
} ?: listOf()
|
||||
}
|
||||
.flatten()
|
||||
.toList()
|
||||
|
||||
adapter.setListData(data)
|
||||
}
|
||||
|
||||
fun updateCategorySelected(selectedList: List<CategoryV2ViewModel.SelectedTags>?) {
|
||||
val size = selectedList?.size ?: 0
|
||||
binding.tvSelectedNumber.goneIf(size == 0) {
|
||||
binding.tvSelectedNumber.text = "$size"
|
||||
}
|
||||
|
||||
if (binding.rvSelected.adapter == null) {
|
||||
binding.rvSelected.layoutManager =
|
||||
LinearLayoutManager(binding.root.context, RecyclerView.HORIZONTAL, false)
|
||||
binding.rvSelected.adapter = selectedTagAdapter
|
||||
}
|
||||
selectedTagAdapter.submitList(selectedList)
|
||||
binding.rvSelected.goneIf(selectedList.isNullOrEmpty())
|
||||
|
||||
if (isSearching) {
|
||||
search(binding.searchView.searchKey)
|
||||
}
|
||||
}
|
||||
|
||||
private fun search(key: String) {
|
||||
Single.create {
|
||||
val data = searchDataList?.filterNot { item -> item.category.selected } ?: emptyList()
|
||||
val resultList = FuzzySearch.extractSorted(key, data, { item -> item.category.name ?: "" }, 1)
|
||||
.map { item -> item.referent }
|
||||
it.onSuccess(resultList)
|
||||
}.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<CategoryV2ViewModel.SelectedTags>>() {
|
||||
override fun onSuccess(data: List<CategoryV2ViewModel.SelectedTags>) {
|
||||
val hasResult = data.isNotEmpty()
|
||||
SensorsBridge.logClassificationSearchReturn(pageId, pageName, key, hasResult)
|
||||
changeToSearching(true, key, data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
SensorsBridge.logClassificationSearchReturn(pageId, pageName, key, false)
|
||||
changeToSearching(true, key)
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun clearSearchKey() {
|
||||
binding.searchView.clear()
|
||||
}
|
||||
|
||||
private fun changeToSearching(
|
||||
isSearching: Boolean,
|
||||
key: String = "",
|
||||
results: List<CategoryV2ViewModel.SelectedTags>? = null
|
||||
) {
|
||||
this.isSearching = isSearching
|
||||
binding.rvCategory.goneIf(isSearching)
|
||||
binding.rvResults.goneIf(results.isNullOrEmpty()) {
|
||||
if (binding.rvResults.adapter == null) {
|
||||
binding.rvResults.layoutManager = LinearLayoutManager(binding.rvResults.context)
|
||||
binding.rvResults.adapter = resultAdapter
|
||||
}
|
||||
}
|
||||
resultAdapter.setData(results ?: listOf(), key)
|
||||
binding.reuseNoConnection.root.goneIf(!isSearching || !results.isNullOrEmpty()) {
|
||||
binding.reuseNoConnection.reuseNoneDataTv.text = R.string.no_relevant_content_found.toResString()
|
||||
binding.reuseNoConnection.reuseNoneDataDescTv.goneIf(false)
|
||||
binding.reuseNoConnection.reuseNoneDataDescTv.text = R.string.try_a_different_search_term.toResString()
|
||||
}
|
||||
}
|
||||
|
||||
fun setOnSearchCategoryListener(listener: OnSearchCategoryListener) {
|
||||
this.listener = listener
|
||||
}
|
||||
|
||||
fun notifyItemSelectedChanged(parentId: String) {
|
||||
adapter.notifyItemSelectedChanged(parentId)
|
||||
}
|
||||
|
||||
override fun showAtLocation(parent: View?, gravity: Int, x: Int, y: Int) {
|
||||
super.showAtLocation(parent, gravity, x, y)
|
||||
if (isAutoRequestFocus) {
|
||||
binding.searchView.requestFocus()
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
// 在某些机型上延时一下才能弹起软键盘
|
||||
handler.postDelayed({
|
||||
KeyboardUtils.showSoftInput()
|
||||
}, SHOW_SOFT_INPUT_DELAY)
|
||||
}
|
||||
}
|
||||
|
||||
override fun dismiss() {
|
||||
super.dismiss()
|
||||
clearSearchKey()
|
||||
handler.removeCallbacksAndMessages(null)
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
private const val SEARCH_DELAY_DURATION = 300L
|
||||
private const val SHOW_SOFT_INPUT_DELAY = 200L
|
||||
|
||||
fun newInstance(
|
||||
context: Context,
|
||||
height: Int,
|
||||
isAutoRequestFocus: Boolean,
|
||||
pageId: String,
|
||||
pageName: String
|
||||
): SearchCategoryPop {
|
||||
val inflater = LayoutInflater.from(context)
|
||||
val binding = PopSearchCategoryBinding.inflate(inflater)
|
||||
return SearchCategoryPop(height, isAutoRequestFocus, pageId, pageName, binding)
|
||||
}
|
||||
}
|
||||
|
||||
interface OnSearchCategoryListener {
|
||||
|
||||
fun isEnableSelected(): Boolean
|
||||
|
||||
fun onItemSelected(selected: CategoryV2ViewModel.SelectedTags)
|
||||
|
||||
fun onItemRemoved(parentId: String, subCategoryId: String)
|
||||
|
||||
fun onResetSelected()
|
||||
|
||||
fun onSubmit()
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.Spannable
|
||||
import android.text.SpannableString
|
||||
import android.text.style.ForegroundColorSpan
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.databinding.RecyclerSearchCategoryResultBinding
|
||||
|
||||
class SearchCategoryResultsAdapter(val clickListener: (CategoryV2ViewModel.SelectedTags) -> Unit) :
|
||||
RecyclerView.Adapter<SearchCategoryResultsAdapter.ResultViewHolder>() {
|
||||
private val dataList = arrayListOf<CategoryV2ViewModel.SelectedTags>()
|
||||
private var key = ""
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(data: List<CategoryV2ViewModel.SelectedTags>, newKey: String) {
|
||||
key = newKey
|
||||
dataList.clear()
|
||||
dataList.addAll(data)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ResultViewHolder {
|
||||
return ResultViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount() = dataList.size
|
||||
|
||||
override fun onBindViewHolder(holder: ResultViewHolder, position: Int) {
|
||||
val item = dataList[position]
|
||||
val text = item.category.name ?: ""
|
||||
val spannableString = SpannableString(text)
|
||||
val highlightColor = com.gh.gamecenter.common.R.color.text_theme.toColor(holder.itemView.context)
|
||||
text.forEachIndexed { index, char ->
|
||||
if (key.contains(char)) {
|
||||
// 需要高亮
|
||||
spannableString.setSpan(
|
||||
ForegroundColorSpan(highlightColor),
|
||||
index,
|
||||
index + 1,
|
||||
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE
|
||||
)
|
||||
}
|
||||
}
|
||||
holder.binding.root.text = spannableString
|
||||
holder.itemView.setOnClickListener {
|
||||
clickListener(item)
|
||||
}
|
||||
}
|
||||
|
||||
class ResultViewHolder(val binding: RecyclerSearchCategoryResultBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.DiffUtil
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.databinding.RecyclerCategorySelectedTagBinding
|
||||
|
||||
class SelectedTagsAdapter(val click: (CategoryV2ViewModel.SelectedTags) -> Unit) :
|
||||
ListAdapter<CategoryV2ViewModel.SelectedTags, SelectedTagsAdapter.TagsViewHolder>(
|
||||
createDiffUtil()
|
||||
) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagsViewHolder {
|
||||
return TagsViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TagsViewHolder, position: Int) {
|
||||
val item = getItem(position)
|
||||
holder.binding.root.setText(item.category.name ?: "")
|
||||
holder.itemView.setOnClickListener {
|
||||
click(item)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private fun createDiffUtil() = object : DiffUtil.ItemCallback<CategoryV2ViewModel.SelectedTags>() {
|
||||
override fun areItemsTheSame(
|
||||
oldItem: CategoryV2ViewModel.SelectedTags,
|
||||
newItem: CategoryV2ViewModel.SelectedTags
|
||||
): Boolean {
|
||||
return oldItem.category.id == newItem.category.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: CategoryV2ViewModel.SelectedTags,
|
||||
newItem: CategoryV2ViewModel.SelectedTags
|
||||
): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
class TagsViewHolder(val binding: RecyclerCategorySelectedTagBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
@ -1,84 +1,94 @@
|
||||
package com.gh.gamecenter.category2
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.ViewHolder
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.SubCategoryItemBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class SubCategoryAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: CategoryV2ViewModel,
|
||||
private val mList: List<CategoryEntity>,
|
||||
private val mPrimaryIndex: Int
|
||||
) : BaseRecyclerAdapter<SubCategoryAdapter.SubCategoryItemViewHolder>(context) {
|
||||
private val listener: SearchCategoryPop.OnSearchCategoryListener,
|
||||
) : RecyclerView.Adapter<SubCategoryAdapter.SubCategoryItemViewHolder>() {
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
private lateinit var itemData: CategoryEntity
|
||||
private val data: List<CategoryEntity>
|
||||
get() = itemData.data ?: emptyList()
|
||||
|
||||
private var directoryPosition = 0
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
fun setData(directoryPosition: Int, newItem: CategoryEntity) {
|
||||
this.directoryPosition = directoryPosition
|
||||
itemData = newItem
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemCount() = data.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
SubCategoryItemViewHolder(SubCategoryItemBinding.inflate(mLayoutInflater))
|
||||
SubCategoryItemViewHolder(parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: SubCategoryItemViewHolder, position: Int, payloads: MutableList<Any>) {
|
||||
if (payloads.isEmpty()) {
|
||||
super.onBindViewHolder(holder, position, payloads)
|
||||
} else {
|
||||
val item = data[position]
|
||||
updateSelectedState(item.selected, holder.binding)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: SubCategoryItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
val categoryEntity = mList[position]
|
||||
name.text = categoryEntity.name
|
||||
val categoryEntity = data[position]
|
||||
tvName.text = categoryEntity.name
|
||||
recommendIv.goneIf(categoryEntity.recommend == false)
|
||||
|
||||
if (categoryEntity.selected) {
|
||||
selectedIv.visibility = View.VISIBLE
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
} else {
|
||||
selectedIv.visibility = View.GONE
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
}
|
||||
updateSelectedState(categoryEntity.selected, this)
|
||||
|
||||
root.setOnClickListener {
|
||||
when {
|
||||
mViewModel.selectedCount >= 5 && !categoryEntity.selected -> ToastUtils.toast("最多只能选择5个类别")
|
||||
!categoryEntity.selected && !listener.isEnableSelected() -> {
|
||||
ToastUtils.toast(R.string.selected_category_tags_max_toast.toResString())
|
||||
}
|
||||
|
||||
categoryEntity.selected -> {
|
||||
categoryEntity.selected = false
|
||||
selectedIv.visibility = View.GONE
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
mViewModel.run {
|
||||
if (selectedCount > 0) {
|
||||
selectedCount--
|
||||
postSelectedCount()
|
||||
postCategoryPosition(mPrimaryIndex, position)
|
||||
logClickClassificationDelete(mPrimaryIndex, categoryEntity.name ?: "", "全部类别")
|
||||
}
|
||||
}
|
||||
|
||||
listener.onItemRemoved(itemData.id, categoryEntity.id)
|
||||
}
|
||||
|
||||
!categoryEntity.selected -> {
|
||||
categoryEntity.selected = true
|
||||
categoryEntity.primaryIndex = mPrimaryIndex
|
||||
selectedIv.visibility = View.VISIBLE
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(mContext)
|
||||
name.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(mContext))
|
||||
mViewModel.run {
|
||||
if (selectedCount < 5) {
|
||||
selectedCount++
|
||||
logClickClassification(mPrimaryIndex, categoryEntity.name ?: "", position)
|
||||
postSelectedCount()
|
||||
postCategoryPosition(mPrimaryIndex, position)
|
||||
}
|
||||
}
|
||||
|
||||
listener.onItemSelected(
|
||||
CategoryV2ViewModel.SelectedTags(
|
||||
itemData.id,
|
||||
itemData.name ?: "",
|
||||
directoryPosition,
|
||||
categoryEntity,
|
||||
position
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubCategoryItemViewHolder(val binding: SubCategoryItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
private fun updateSelectedState(isSelected: Boolean, binding: SubCategoryItemBinding) {
|
||||
with(binding) {
|
||||
val context = root.context
|
||||
if (isSelected) {
|
||||
container.background = R.drawable.bg_category_selected.toDrawable(context)
|
||||
tvName.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
} else {
|
||||
container.background = com.gh.gamecenter.common.R.drawable.bg_shape_space_radius_8.toDrawable(context)
|
||||
tvName.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(context))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubCategoryItemViewHolder(val binding: SubCategoryItemBinding) : ViewHolder(binding.root)
|
||||
}
|
||||
@ -2,17 +2,30 @@ package com.gh.gamecenter.cloudarchive
|
||||
|
||||
import android.app.Application
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.entity.ArchiveEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.*
|
||||
import retrofit2.HttpException
|
||||
import java.io.IOException
|
||||
|
||||
open class BaseCloudArchiveViewModel(application: Application, private val mConfigUrl: String): ListViewModel<ArchiveEntity, ArchiveEntity>(application) {
|
||||
open class BaseCloudArchiveViewModel(
|
||||
application: Application,
|
||||
private val mGameId: String,
|
||||
private var mConfigUrl: String
|
||||
) : ListViewModel<ArchiveEntity, ArchiveEntity>(application) {
|
||||
private var mArchiveConfigStr = ""
|
||||
|
||||
init {
|
||||
getArchiveConfigString()
|
||||
if (mConfigUrl.isEmpty()) {
|
||||
getArchiveConfigUrl()
|
||||
} else {
|
||||
getArchiveConfigString()
|
||||
}
|
||||
}
|
||||
|
||||
// 通过url获取config字符串内容
|
||||
@ -41,6 +54,27 @@ open class BaseCloudArchiveViewModel(application: Application, private val mConf
|
||||
}
|
||||
}
|
||||
|
||||
// 获取游戏存档配置url
|
||||
private fun getArchiveConfigUrl() {
|
||||
val map = mapOf(
|
||||
"game_ids" to listOf(mGameId)
|
||||
)
|
||||
RetrofitManager.getInstance().newApi.getGamesArchiveConfigs(map.toRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : com.gh.gamecenter.common.retrofit.Response<List<ArchiveEntity>>() {
|
||||
override fun onResponse(response: List<ArchiveEntity>?) {
|
||||
mConfigUrl = response?.find { it.gameId == mGameId }?.configUrl ?: return
|
||||
getArchiveConfigString()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
Utils.toast(getApplication(), "获取存档配置失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<ArchiveEntity>>? = null
|
||||
|
||||
override fun mergeResultLiveData() {}
|
||||
|
||||
@ -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, "")
|
||||
}
|
||||
}
|
||||
|
||||
@ -8,7 +8,6 @@ import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.toJson
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.entity.ArchiveEntity
|
||||
@ -22,7 +21,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.*
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
|
||||
class CloudArchiveManagerViewModel(
|
||||
@ -30,7 +29,7 @@ class CloudArchiveManagerViewModel(
|
||||
val gameId: String,
|
||||
val gameName: String,
|
||||
configUrl: String
|
||||
) : BaseCloudArchiveViewModel(application, configUrl) {
|
||||
) : BaseCloudArchiveViewModel(application, gameId, configUrl) {
|
||||
|
||||
companion object {
|
||||
private const val SORT_TYPE_CREATE = "time.create:-1"
|
||||
|
||||
@ -318,7 +318,9 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
downloadEntity.getGameId(),
|
||||
downloadEntity.getName(),
|
||||
"主动安装"
|
||||
"主动安装",
|
||||
false,
|
||||
""
|
||||
);
|
||||
PackageInstaller.install(mContext, downloadEntity);
|
||||
}
|
||||
@ -397,7 +399,9 @@ class GameDownloadFragmentAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
SensorsBridge.trackInstallGameClick(
|
||||
downloadEntity.getGameId(),
|
||||
downloadEntity.getName(),
|
||||
"主动安装"
|
||||
"主动安装",
|
||||
"true".equals(ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_GAME)),
|
||||
ExtensionsKt.getMetaExtra(downloadEntity, Constants.DSP_AD_ID)
|
||||
);
|
||||
PackageInstaller.install(mContext, downloadEntity);
|
||||
}
|
||||
|
||||
@ -15,6 +15,7 @@ import com.gh.gamecenter.common.base.BaseSimpleDao
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager.getCurrentPageEntity
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager.getLastPageEntity
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.GsonUtils.toJson
|
||||
@ -142,6 +143,8 @@ class UpdatableGameViewModel(
|
||||
|
||||
// 按包名分
|
||||
for (update in updatableList) {
|
||||
if (update.id == Constants.GHZS_GAME_ID) continue
|
||||
|
||||
var list = packageNameAndUpdateListMap[update.packageName]
|
||||
if (list == null) {
|
||||
list = arrayListOf()
|
||||
@ -629,6 +632,7 @@ class UpdatableGameViewModel(
|
||||
downloadEntity.addMetaExtra(Constants.APK_MD5, update.md5)
|
||||
downloadEntity.addMetaExtra(Constants.GAME_NAME, update.name)
|
||||
downloadEntity.addMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE, update.categoryChinese)
|
||||
downloadEntity.addMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE, update.downloadStatusChinese)
|
||||
downloadEntity.putGameCategory(update.category ?: "")
|
||||
if (update.isModdedGame) {
|
||||
downloadEntity.addMetaExtra(Constants.EXTRA_IS_MODDED_GAME, "true")
|
||||
@ -685,6 +689,9 @@ class UpdatableGameViewModel(
|
||||
// 收集下载数据
|
||||
DataCollectionUtils.uploadDownload(getApplication(), downloadEntity, "开始")
|
||||
|
||||
val pushMessageId = (HaloApp.get(Constants.PUSH_MESSAGE_ID, false) as? String) ?: ""
|
||||
val pushLinkId = (HaloApp.get(Constants.PUSH_LINK_ENTITY, false) as? LinkEntity)?.link ?: ""
|
||||
val isFromPush = pushMessageId.isNotEmpty()
|
||||
SensorsBridge.trackEventWithExposureSource(
|
||||
"DownloadProcessBegin",
|
||||
event.source,
|
||||
@ -700,6 +707,9 @@ class UpdatableGameViewModel(
|
||||
"last_page_business_id", getLastPageEntity().pageBusinessId,
|
||||
"download_status", update.downloadStatusChinese,
|
||||
"download_type", "本地下载",
|
||||
"is_from_push_notifications", isFromPush,
|
||||
"message_id", pushMessageId,
|
||||
"link_id", pushLinkId,
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
46
app/src/main/java/com/gh/gamecenter/dsp/DspReportHelper.kt
Normal file
46
app/src/main/java/com/gh/gamecenter/dsp/DspReportHelper.kt
Normal file
@ -0,0 +1,46 @@
|
||||
package com.gh.gamecenter.dsp
|
||||
|
||||
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.lightgame.utils.Utils
|
||||
import okhttp3.OkHttpClient
|
||||
import okhttp3.Request
|
||||
|
||||
object DspReportHelper {
|
||||
|
||||
private const val TAG = "DspReportHelper"
|
||||
private val reportCache by lazy { FixedSizeLinkedHashSet<String>(100) }
|
||||
|
||||
fun report(url: String?) {
|
||||
url ?: return
|
||||
|
||||
AppExecutor.logExecutor.execute {
|
||||
if (reportCache.contains(url)) {
|
||||
Utils.log(TAG, "遇到重复上报,自动过滤 ($url)")
|
||||
return@execute
|
||||
}
|
||||
|
||||
try {
|
||||
val client = OkHttpClient()
|
||||
val request = Request.Builder()
|
||||
.url(url)
|
||||
.build()
|
||||
|
||||
Utils.log(TAG, "Report is executing: $url")
|
||||
|
||||
val response = client.newCall(request).execute()
|
||||
if (response.isSuccessful) {
|
||||
// Add the URL to the cache to prevent duplicate reports
|
||||
reportCache.add(url)
|
||||
|
||||
Utils.log(TAG, "Report successful: ${response.body?.string()}")
|
||||
} else {
|
||||
Utils.log(TAG, "Request failed with code: ${response.code}")
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Utils.log(TAG, "Request failed: ${e.message}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,36 @@
|
||||
package com.gh.gamecenter.dsp.data
|
||||
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.DspApiService
|
||||
import io.reactivex.Single
|
||||
|
||||
class GameSubjectDSPRemoteDataSource(private val api: DspApiService = RetrofitManager.getInstance().dspApiService) {
|
||||
|
||||
fun getDspGames(type: String, count: Int): Single<List<GameEntity>> {
|
||||
val meta = MetaUtil.getMeta()
|
||||
val request = mapOf(
|
||||
"device" to mapOf(
|
||||
"oaid" to (meta.oaid ?: ""),
|
||||
"brand" to (meta.manufacturer ?: ""),
|
||||
"model" to (meta.model ?: ""),
|
||||
"osv" to (MetaUtil.getAndroidVersion()),
|
||||
),
|
||||
"count" to count,
|
||||
"scene" to 3,
|
||||
"busid" to "guanghuan1205"
|
||||
)
|
||||
return api.fetch(request.toRequestBody())
|
||||
.map {
|
||||
val gameEntityList = mutableListOf<GameEntity>()
|
||||
for (bidEntity in it.bidList) {
|
||||
val gameEntity = bidEntity.toGameEntity()
|
||||
gameEntityList.add(gameEntity)
|
||||
}
|
||||
gameEntityList
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,16 +2,22 @@ package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class CategoryEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String? = "",
|
||||
private var _id: String? = null,
|
||||
var icon: String? = "",
|
||||
var name: String? = "",
|
||||
var recommend: Boolean? = false,
|
||||
var data: List<CategoryEntity>? = null,
|
||||
var selected: Boolean = false,
|
||||
var primaryIndex: Int = -1
|
||||
) : Parcelable
|
||||
var data: List<CategoryEntity>? = null
|
||||
) : Parcelable {
|
||||
|
||||
val id: String
|
||||
get() = _id ?: ""
|
||||
|
||||
@IgnoredOnParcel
|
||||
var selected: Boolean = false
|
||||
}
|
||||
@ -7,5 +7,8 @@ class GameColumnCollection(
|
||||
val id: String = "",
|
||||
val name: String = "",
|
||||
// 取值为 "1-1" 或 "1-2" 或 "top" 相应地代表 1行1个 或 1行2个 或 排行榜
|
||||
val style: String = ""
|
||||
val style: String = "",
|
||||
val custom: Boolean = false, // 自定义设置
|
||||
@SerializedName("custom_size")
|
||||
val customSize: Int = 0 // 默认显示前X个专题
|
||||
)
|
||||
@ -1,10 +1,10 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import com.gh.gamecenter.feature.entity.SimpleGame
|
||||
import com.gh.gamecenter.feature.entity.Count
|
||||
import com.gh.gamecenter.feature.entity.SimpleGame
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GameDetailRecommendGameEntity(
|
||||
data class GameDetailRecommendGameCollectionEntity(
|
||||
@SerializedName("_id")
|
||||
val id: String = "",
|
||||
val cover: String = "",
|
||||
@ -0,0 +1,11 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
|
||||
@Entity
|
||||
data class HistoryGameDetailEntity(
|
||||
@PrimaryKey
|
||||
var id: String = "",
|
||||
var name: String? = "",
|
||||
)
|
||||
@ -13,6 +13,14 @@ class LibaoDetailEntity {
|
||||
|
||||
var des: String? = null
|
||||
|
||||
// 领取限制 发表游戏评价 (game_comment)
|
||||
@SerializedName("receive_limit")
|
||||
var receiveLimit: String? = ""
|
||||
|
||||
// 领取条件
|
||||
@SerializedName("receive_condition")
|
||||
var receiveCondition: Condition ? = null
|
||||
|
||||
@SerializedName("new_des")
|
||||
var newDes: String? = null
|
||||
|
||||
@ -24,4 +32,10 @@ class LibaoDetailEntity {
|
||||
@SerializedName("me")
|
||||
var me: MeEntity? = null
|
||||
|
||||
class Condition(
|
||||
// 评分,-1/5/4/3/2/1 => 无限制、5星好评、4星及以上评分、3星及以上评分、2星及以上评分、1星及以上评分
|
||||
val star: Int = 0,
|
||||
// 字数,-1/n => 无限制、数量
|
||||
val words: Int = 0
|
||||
)
|
||||
}
|
||||
|
||||
@ -11,6 +11,8 @@ class ReserveReminderEntity(
|
||||
private var _smsConfig: SmsConfig? = null,
|
||||
@SerializedName("wechat_config")
|
||||
private var _wechatConfig: WechatConfigEntity? = null,
|
||||
@SerializedName("calendar_config")
|
||||
private var _calendarConfig: CalendarConfig? = null,
|
||||
@SerializedName("mirror_type")
|
||||
private val _mirrorType: String? = null,
|
||||
@SerializedName("wifi_auto_download")
|
||||
@ -34,8 +36,14 @@ class ReserveReminderEntity(
|
||||
_wechatConfig = value
|
||||
}
|
||||
|
||||
val onlyShowWechatReminder: Boolean
|
||||
get() = _smsConfig == null
|
||||
val hasCalendarConfig: Boolean
|
||||
get() = _calendarConfig != null
|
||||
|
||||
var calendarConfig: CalendarConfig
|
||||
get() = _calendarConfig ?: CalendarConfig()
|
||||
set(value) {
|
||||
_calendarConfig = value
|
||||
}
|
||||
|
||||
val mirrorType: String
|
||||
get() = _mirrorType ?: ""
|
||||
@ -81,4 +89,39 @@ class ReserveReminderEntity(
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
class CalendarConfig(
|
||||
@SerializedName("notice")
|
||||
private val _notice: Boolean? = null,
|
||||
@SerializedName("title")
|
||||
private val _title: String? = null,
|
||||
@SerializedName("time_start")
|
||||
private val _timeStart: Long? = null,
|
||||
@SerializedName("time_end")
|
||||
private val _timeEnd: Long? = null,
|
||||
@SerializedName("advance_seconds")
|
||||
private val _advanceSeconds: Long? = null,
|
||||
@SerializedName("remark")
|
||||
private val _remark: String? = null
|
||||
) : Parcelable {
|
||||
|
||||
val notice: Boolean
|
||||
get() = _notice ?: false
|
||||
|
||||
val title: String
|
||||
get() = _title ?: ""
|
||||
|
||||
val timeStart: Long
|
||||
get() = _timeStart ?: 0L
|
||||
|
||||
val timeEnd: Long
|
||||
get() = _timeEnd ?: 0L
|
||||
|
||||
val advanceSeconds: Long
|
||||
get() = _advanceSeconds ?: 0L
|
||||
|
||||
val remark: String
|
||||
get() = _remark ?: ""
|
||||
}
|
||||
}
|
||||
@ -3,6 +3,7 @@ package com.gh.gamecenter.entity
|
||||
import android.os.Parcelable
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ -19,11 +20,20 @@ data class SearchSubjectEntity(
|
||||
val adIconActive: Boolean = false,
|
||||
// 本地字段,标记是否为微信小游戏CPM专题
|
||||
var isWGameSubjectCPM: Boolean = false,
|
||||
val type: String = ""
|
||||
// 本地字段,标记是否为DSP专题
|
||||
var isDspSubject: Boolean = false,
|
||||
val type: String = "",
|
||||
|
||||
@SerializedName("column_type")
|
||||
val columnType: String = "", // DSP 专题类型 (DSP专题类型(apk:下载应用、mini_game:微信小程序/小游戏)
|
||||
@SerializedName("show_download") // 下载/秒玩按钮(true:显示、false:不显示)(专题类型为apk,则为下载按钮;专题类型为mini_game,则为秒完按钮)
|
||||
val showDownload: Boolean = false,
|
||||
val size: Int = -1, // 专题游戏数量
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
const val TYPE_WECHAT_GAME_CPM_COLUMN = "wechat_game_cpm_column"
|
||||
const val TYPE_DSP_GAME_COLUMN = "dsp_game_column"
|
||||
}
|
||||
|
||||
fun getFilterGame() = RegionSettingHelper.filterGame(games)
|
||||
|
||||
@ -7,7 +7,7 @@ import kotlinx.parcelize.IgnoredOnParcel
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class SubjectData(
|
||||
data class SubjectData(
|
||||
// 入口必填
|
||||
var subjectId: String?,
|
||||
var subjectName: String?,
|
||||
@ -21,13 +21,28 @@ class SubjectData(
|
||||
var subjectStyle: String = "",
|
||||
var showDetailSubtitle: Boolean = false,
|
||||
var showDetailIconSubscript: Boolean = false,
|
||||
var customLimit: String = "", // unlimited(无限制)、forbidden(禁止移出)
|
||||
|
||||
var requireUpdateSetting: Boolean = false, // 多专题页面需要专题页面自行获取专题配置
|
||||
var isAdData: Boolean = false,
|
||||
var adId: String = "", // 广告ID(本地字段),不为空时为广告专题
|
||||
var codeId: String = "" // 广告CODE_ID(本地字段),不为空时为广告专题
|
||||
var codeId: String = "", // 广告CODE_ID(本地字段),不为空时为广告专题
|
||||
var tag: String = "" // 分类标签,埋点用
|
||||
) : Parcelable, Cloneable {
|
||||
|
||||
@IgnoredOnParcel
|
||||
val isForbidden
|
||||
get() = customLimit == "forbidden"
|
||||
|
||||
@IgnoredOnParcel
|
||||
val sortChinese
|
||||
get() = when {
|
||||
sort.contains("publish") -> "最新"
|
||||
sort.contains("star") -> "评分"
|
||||
sort.contains("update") -> "更新"
|
||||
else -> "推荐"
|
||||
}
|
||||
|
||||
@IgnoredOnParcel
|
||||
val subjectStyleChinese: String
|
||||
get() = CustomPageItem.subjectTypeToComponentStyle[subjectStyle] ?: ""
|
||||
|
||||
@ -117,7 +117,12 @@ data class SubjectEntity(
|
||||
@SerializedName("show_index_icon_subscript")
|
||||
val showIndexIconSubscript: Boolean = false,
|
||||
@SerializedName("welfare_info")
|
||||
val welfareInfo: WelfareInfo? = null
|
||||
val welfareInfo: WelfareInfo? = null,
|
||||
|
||||
@SerializedName("column_type")
|
||||
private val _columnType: String? = null,
|
||||
@SerializedName("size")
|
||||
private val _size: Size? = null
|
||||
) : Parcelable {
|
||||
|
||||
@IgnoredOnParcel
|
||||
@ -130,12 +135,13 @@ data class SubjectEntity(
|
||||
@IgnoredOnParcel
|
||||
private var filteredData: MutableList<GameEntity>? = null
|
||||
|
||||
val subjectType: SubjectData.SubjectType get() = when {
|
||||
isQQColumn -> SubjectData.SubjectType.QQ_GAME
|
||||
isWechatColumnCPM -> SubjectData.SubjectType.WECHAT_GAME_CPM
|
||||
isWechatColumn -> SubjectData.SubjectType.WECHAT_GAME
|
||||
else -> SubjectData.SubjectType.NORMAL
|
||||
}
|
||||
val subjectType: SubjectData.SubjectType
|
||||
get() = when {
|
||||
isQQColumn -> SubjectData.SubjectType.QQ_GAME
|
||||
isWechatColumnCPM -> SubjectData.SubjectType.WECHAT_GAME_CPM
|
||||
isWechatColumn -> SubjectData.SubjectType.WECHAT_GAME
|
||||
else -> SubjectData.SubjectType.NORMAL
|
||||
}
|
||||
|
||||
val isMiniGame: Boolean get() = isQQColumn || isWechatColumn
|
||||
|
||||
@ -148,4 +154,28 @@ data class SubjectEntity(
|
||||
|
||||
val list: Int
|
||||
get() = max(min(_list ?: 0, data?.size ?: 0), 1)
|
||||
|
||||
val columnType: String
|
||||
get() = _columnType ?: ""
|
||||
|
||||
val size: Size
|
||||
get() = _size ?: Size()
|
||||
|
||||
var isDspSubject: Boolean = false
|
||||
|
||||
companion object {
|
||||
const val SUBJECT_TAG_UPDATE = "update" // 更新时间
|
||||
const val SUBJECT_TAG_TYPE = "type" // 游戏标签
|
||||
const val SUBJECT_TAG_TEST = "test" // 开测时间
|
||||
const val SUBJECT_TAG_SELLING_POINT = "selling_points&type" // 卖点文案+游戏标签
|
||||
}
|
||||
|
||||
@Parcelize
|
||||
data class Size(
|
||||
@SerializedName("index")
|
||||
private val _index: Int? = null
|
||||
) : Parcelable {
|
||||
val index: Int
|
||||
get() = _index ?: 0
|
||||
}
|
||||
}
|
||||
@ -13,7 +13,7 @@ class SubjectSettingEntity(
|
||||
@SerializedName("type")
|
||||
var typeEntity: TypeEntity = TypeEntity(),
|
||||
var tag: String = "",
|
||||
var filter: String = "", // rows: off/on
|
||||
var filter: String = "", // off/on
|
||||
var order: Boolean = false, // 是否显示序号
|
||||
|
||||
@SerializedName("brief_style")
|
||||
@ -34,6 +34,9 @@ class SubjectSettingEntity(
|
||||
private val _showDetailIconSubscript: Boolean? = null
|
||||
) : Parcelable {
|
||||
|
||||
val isFilterEnabled: Boolean
|
||||
get() = filter == "on"
|
||||
|
||||
val showDetailSubtitle: Boolean
|
||||
get() = _showDetailSubtitle ?: false
|
||||
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
package com.gh.gamecenter.eventbus
|
||||
|
||||
import com.gh.gamecenter.feature.entity.AcctGameInfo
|
||||
|
||||
class EBStartupAcceleration(val acctGameInfo: AcctGameInfo)
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user