Compare commits
33 Commits
release
...
feat/GHZSC
| Author | SHA1 | Date | |
|---|---|---|---|
| 853aeffe1e | |||
| e562ea0d35 | |||
| ff519a0da1 | |||
| 40147ac09d | |||
| c47883f176 | |||
| d6771cf5ea | |||
| ea742707fe | |||
| 287f412402 | |||
| 14bd1a5153 | |||
| 00eaeac888 | |||
| d370c33d12 | |||
| 35c1a3487b | |||
| 9b480b89c7 | |||
| ab34c7b56a | |||
| 4bb3208d59 | |||
| e875ff9c03 | |||
| 9eac3d64e2 | |||
| 8f4a7dfd16 | |||
| b611138496 | |||
| 40eebbc93c | |||
| 94b9f554b4 | |||
| 2bbf2dce3c | |||
| c944e529ba | |||
| eaa1a3609f | |||
| 2b9478a1b9 | |||
| dbd6e7bb43 | |||
| 09a823d4da | |||
| 0b49c58884 | |||
| 2e93bcc173 | |||
| 03f6906b46 | |||
| 0f382baec1 | |||
| 62f80bbfdd | |||
| 63be9ac221 |
@ -72,6 +72,7 @@ android_build:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-7835
|
||||
|
||||
# 代码检查
|
||||
sonarqube_analysis:
|
||||
@ -157,4 +158,5 @@ oss-upload&send-email:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-7835
|
||||
|
||||
|
||||
@ -824,6 +824,14 @@
|
||||
android:name="com.halo.assistant.accelerator.MyAssetsActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".personal.playtime.PlayTimeActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".playedgame.PlayedGameActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<!-- <activity-->
|
||||
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->
|
||||
<!-- android:launchMode="singleTask"-->
|
||||
|
||||
9
app/src/main/assets/myhalo/ic_announcement.svg
Normal file
9
app/src/main/assets/myhalo/ic_announcement.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 18 KiB |
9
app/src/main/assets/myhalo/ic_game_event.svg
Normal file
9
app/src/main/assets/myhalo/ic_game_event.svg
Normal file
File diff suppressed because one or more lines are too long
|
After Width: | Height: | Size: 15 KiB |
@ -18,7 +18,6 @@ import com.therouter.TheRouter
|
||||
import com.facebook.drawee.controller.BaseControllerListener
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.facebook.imagepipeline.image.ImageInfo
|
||||
import com.g00fy2.versioncompare.Version
|
||||
import com.gh.common.exposure.ExposureManager
|
||||
import com.gh.common.util.DirectUtils.directToLinkPage
|
||||
import com.gh.common.util.LogUtils
|
||||
@ -187,11 +186,11 @@ object AdDelegateHelper {
|
||||
"halo_launch" -> {
|
||||
config.ownerAd?.startAd?.let { it.id = config.ownerAd.id }
|
||||
|
||||
// HarmonyOS 4.2.0 版本不展示第三方开屏广告 (因为会引起奇怪的闪退)
|
||||
if (MetaUtil.getRom().romName == "HarmonyOS"
|
||||
&& Version(MetaUtil.getRom().romVersion).isLowerThan(Version("4.2.0"))
|
||||
&& config.displayRule.adSource == AD_TYPE_SDK
|
||||
) {
|
||||
// HarmonyOS 2.2.0 版本不展示第三方开屏广告 (因为会引起奇怪的闪退)
|
||||
if (MetaUtil.getRom().name == "HarmonyOS"
|
||||
&& MetaUtil.getRom().versionName == "2.2.0"
|
||||
&& config.displayRule.adSource == AD_TYPE_SDK) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@ -390,7 +390,7 @@ public class Config {
|
||||
"manufacturer", Build.MANUFACTURER,
|
||||
"model", Build.MODEL,
|
||||
"android_sdk_version", String.valueOf(Build.VERSION.SDK_INT),
|
||||
"rom", MetaUtil.INSTANCE.getRom().getRomName() + " " + MetaUtil.INSTANCE.getRom().getRomVersion()
|
||||
"rom", MetaUtil.INSTANCE.getRom().name() + " " + MetaUtil.INSTANCE.getRom().getVersionName()
|
||||
);
|
||||
|
||||
RetrofitManager.getInstance()
|
||||
|
||||
@ -12,7 +12,6 @@ import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.view.CustomLinkMovementMethod
|
||||
@ -82,9 +81,9 @@ class NewPrivacyPolicyDialogFragment : BaseDialogFragment() {
|
||||
|
||||
override fun onClick(widget: View) {
|
||||
val privacyPolicyUrl = requireContext().getString(com.gh.gamecenter.common.R.string.privacy_policy_url)
|
||||
val childrenPrivacyPolicyUrl = requireContext().getString(R.string.children_policy_url)
|
||||
val thirdPartySdkUrl = requireContext().getString(R.string.sdk_list_url)
|
||||
val permissionListUrl = requireContext().getString(R.string.permission_and_usage_url)
|
||||
val childrenPrivacyPolicyUrl = requireContext().getString(com.gh.gamecenter.common.R.string.children_policy_url)
|
||||
val thirdPartySdkUrl = requireContext().getString(com.gh.gamecenter.common.R.string.sdk_list_url)
|
||||
val permissionListUrl = requireContext().getString(com.gh.gamecenter.common.R.string.permission_and_usage_url)
|
||||
|
||||
val selectedUrl =
|
||||
when (contentText.substring(hypertextPositionPair.first, hypertextPositionPair.second)) {
|
||||
|
||||
@ -16,7 +16,6 @@ class DefaultExposureStateChangeListener : IExposureStateChangeListener {
|
||||
val exposureStatus = if (inExposure) "曝光中" else "结束曝光"
|
||||
|
||||
val isCPMExposureEvent = exposureEvent.payload.miniGameType == Constants.WECHAT_MINI_GAME_CPM
|
||||
val isDSPExposureEvent = exposureEvent.payload.miniGameType == Constants.DSP_GAME
|
||||
|
||||
Utils.log(
|
||||
RecyclerViewExposureHelper.TAG,
|
||||
@ -30,12 +29,6 @@ class DefaultExposureStateChangeListener : IExposureStateChangeListener {
|
||||
"上报 CPM 曝光 ${exposureEvent.payload.gameName} ${exposureEvent.id}"
|
||||
)
|
||||
ExposureManager.logCPM(exposureEvent)
|
||||
} else if (isDSPExposureEvent && inExposure) {
|
||||
Utils.log(
|
||||
RecyclerViewExposureHelper.TAG,
|
||||
"上报 DSP 曝光 ${exposureEvent.payload.gameName} ${exposureEvent.id}"
|
||||
)
|
||||
ExposureManager.logDSP(exposureEvent)
|
||||
}
|
||||
|
||||
if (!inExposure
|
||||
|
||||
@ -111,12 +111,6 @@ object ExposureManager {
|
||||
}
|
||||
}
|
||||
|
||||
fun logDSP(event: ExposureEvent) {
|
||||
AppExecutor.logExecutor.execute {
|
||||
DspReportHelper.report(event.payload.showUrl)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param forcedUpload Ignore all restrictions.
|
||||
*/
|
||||
|
||||
@ -73,7 +73,7 @@ object RegionSettingHelper {
|
||||
if (list is ArrayList) return list
|
||||
}
|
||||
|
||||
val listCopy: ArrayList<GameEntity> = ArrayList(list)
|
||||
val listCopy: ArrayList<GameEntity> = if (list is ArrayList) list else ArrayList(list)
|
||||
listCopy.removeAll { mFilterGameIdSet?.contains(it.id) ?: false }
|
||||
return listCopy
|
||||
}
|
||||
|
||||
@ -0,0 +1,5 @@
|
||||
package com.gh.common.iinterface
|
||||
|
||||
interface IBatchManage {
|
||||
fun onManageClick()
|
||||
}
|
||||
@ -0,0 +1,16 @@
|
||||
package com.gh.common.iinterface
|
||||
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
|
||||
interface IInstalledSortType {
|
||||
fun getCurrentSortType(): InstalledSortType
|
||||
fun changeSortType(sortType: InstalledSortType)
|
||||
}
|
||||
|
||||
enum class InstalledSortType(val des: String) {
|
||||
RECENTLY_PLAYED(R.string.order_default.toResString()),
|
||||
LATEST_UPDATED(R.string.order_latest_updated.toResString()),
|
||||
MOST_PLAYED(R.string.order_most_played.toResString()),
|
||||
LEAST_PLAYED(R.string.order_least_played.toResString())
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.gh.common.prioritychain
|
||||
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.personal.MyHaloFragment
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class MyHaloGuideHandler(priority: Int) : PriorityChainHandler(priority) {
|
||||
private var myHaloFragmentWeakRef: WeakReference<MyHaloFragment>? = null
|
||||
|
||||
fun doPreProcess(fragment: MyHaloFragment) {
|
||||
myHaloFragmentWeakRef = WeakReference(fragment)
|
||||
|
||||
val shouldShowGuide = SPUtils.getBoolean(Constants.SP_SHOW_MY_HALO_GUIDE, true)
|
||||
if (getStatus() == STATUS_PENDING) {
|
||||
if (shouldShowGuide) {
|
||||
updateStatus(STATUS_VALID)
|
||||
process()
|
||||
} else {
|
||||
processNext()
|
||||
}
|
||||
} else {
|
||||
if (shouldShowGuide) {
|
||||
updateStatus(STATUS_VALID)
|
||||
} else {
|
||||
updateStatus(STATUS_INVALID)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onProcess() : Boolean {
|
||||
when (getStatus()) {
|
||||
STATUS_VALID -> {
|
||||
myHaloFragmentWeakRef?.get()?.scrollToTop()
|
||||
myHaloFragmentWeakRef?.get()?.showGuide()
|
||||
return true
|
||||
}
|
||||
|
||||
STATUS_INVALID -> {
|
||||
processNext()
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
}
|
||||
@ -45,6 +45,10 @@ class PackageUtilsProviderImpl : IPackageUtilsProvider {
|
||||
return PackageUtils.getInstalledTime(context, packageName)
|
||||
}
|
||||
|
||||
override fun getLastUpdateTime(context: Context, packageName: String): Long {
|
||||
return PackageUtils.getLastUpdateTime(context, packageName)
|
||||
}
|
||||
|
||||
override fun getVersionNameByPackageName(packageName: String): String {
|
||||
return PackageUtils.getVersionNameByPackageName(packageName) ?: ""
|
||||
}
|
||||
|
||||
@ -10,4 +10,7 @@ import com.gh.gamecenter.core.provider.IShellProvider
|
||||
class ShellProviderImpl : IShellProvider {
|
||||
override fun getSwitchInstallMethodIntent(context: Context, extraParcelable: Parcelable?): Intent =
|
||||
ShellActivity.getIntent(context, ShellActivity.Type.SWITCH_INSTALL_METHOD, extraParcelable)
|
||||
|
||||
override fun getRealNameInfoIntent(context: Context, extraParcelable: Parcelable?): Intent =
|
||||
ShellActivity.getIntent(context, ShellActivity.Type.REAL_NAME_INFO, extraParcelable)
|
||||
}
|
||||
@ -3,9 +3,7 @@ package com.gh.common.provider
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import com.therouter.router.Route
|
||||
import com.gh.common.util.UsageStatsHelper
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.provider.IUsageStatsHelperProvider
|
||||
|
||||
@com.therouter.inject.ServiceProvider
|
||||
@ -15,4 +13,5 @@ class UsageStatsHelperProviderImpl : IUsageStatsHelperProvider {
|
||||
override fun skipToUsageStats(context: Context, requestCode: Int) {
|
||||
UsageStatsHelper.skipToUsageStats(context, requestCode)
|
||||
}
|
||||
override fun getLastUsedTimeByPackageName(packageName: String): Long = UsageStatsHelper.getLastUsedTimeByPackageName(packageName)
|
||||
}
|
||||
@ -4,7 +4,6 @@ import android.annotation.SuppressLint
|
||||
import android.content.ActivityNotFoundException
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.database.sqlite.SQLiteDiskIOException
|
||||
import android.database.sqlite.SQLiteException
|
||||
import android.graphics.Bitmap
|
||||
import android.net.Uri
|
||||
@ -14,6 +13,7 @@ import com.gh.common.util.LogUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.callback.BiCallback
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.json.json
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.EmptyResponse
|
||||
@ -202,6 +202,10 @@ object SimulatorGameManager {
|
||||
intent.setClassName(gameEntity.simulator?.apk?.packageName ?: "", destActivity)
|
||||
try {
|
||||
AppManager.getInstance().recentActiveActivity?.startActivity(intent)
|
||||
|
||||
// 记录光环内启动时间
|
||||
val packageLaunchTimeSp = HaloApp.getInstance().getSharedPreferences(Constants.SP_PACKAGE_LAUNCH_TIME, Context.MODE_PRIVATE)
|
||||
SPUtils.setLong(packageLaunchTimeSp, gameEntity.id, System.currentTimeMillis())
|
||||
} catch (e: ActivityNotFoundException) {
|
||||
ToastUtils.showToast("模拟器安装错误")
|
||||
}
|
||||
|
||||
@ -1073,6 +1073,15 @@ object DirectUtils {
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToDownloadManagerUpdate(context: Context, entrance: String? = null) {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_TO, DownloadManagerActivity.TAG)
|
||||
bundle.putInt(BaseFragment_TabLayout.PAGE_INDEX, INDEX_UPDATE)
|
||||
jumpActivityCompat(context, bundle)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun directToDownloadManagerAndStartUpdate(
|
||||
context: Context,
|
||||
|
||||
@ -108,32 +108,21 @@ object GameSubstituteRepositoryHelper {
|
||||
var thisPositionNeedToBeReplaced = false
|
||||
|
||||
// 检查是否已安装该游戏里同包名的 APK
|
||||
if (game.getApk().size == 1) {
|
||||
val apk = game.getApk().firstOrNull()
|
||||
// 若该游戏只有一个 APK,且该 APK 的包名在本地已安装的包名列表中
|
||||
if (PackageHelper.localPackageNameSet.contains(apk?.packageName)) {
|
||||
for (apk in game.getApk()) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfGameToBeReplacedList.add(index)
|
||||
thisPositionNeedToBeReplaced = true
|
||||
}
|
||||
} else {
|
||||
// 检查是否已安装该游戏里同包名的 APK
|
||||
for (apk in game.getApk()) {
|
||||
if (PackageHelper.validLocalPackageNameSet.contains(apk.packageName)) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfGameToBeReplacedList.add(index)
|
||||
thisPositionNeedToBeReplaced = true
|
||||
break
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已安装该游戏 id
|
||||
if (!thisPositionNeedToBeReplaced && PackagesManager.getInstalledDataByGameId(game.id) != null) {
|
||||
if (PackagesManager.getInstalledDataByGameId(game.id) != null) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfGameToBeReplacedList.add(index)
|
||||
thisPositionNeedToBeReplaced = true
|
||||
continue
|
||||
break
|
||||
}
|
||||
|
||||
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
|
||||
@ -163,19 +152,11 @@ object GameSubstituteRepositoryHelper {
|
||||
|
||||
if (mGameCollectionList.isNullOrEmpty()) return
|
||||
|
||||
// 临时的游戏 ID 列表(包含 gameList 的游戏),避免重复替换
|
||||
val tempDisplayingGameIdSet = HashSet(displayingGameIdSet)
|
||||
|
||||
gameList.forEach {
|
||||
tempDisplayingGameIdSet.add(it.id)
|
||||
}
|
||||
|
||||
for (position in positionOfGameToBeReplacedList) {
|
||||
val validGame = getValidGame(relatedCollectionId, tempDisplayingGameIdSet)
|
||||
val validGame = getValidGame(relatedCollectionId, displayingGameIdSet)
|
||||
validGame?.let {
|
||||
gameList[position] = it
|
||||
displayingGameIdSet.add(it.id)
|
||||
tempDisplayingGameIdSet.add(it.id)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -245,7 +245,6 @@ public class LogUtils {
|
||||
object.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
object.put("G_ID", UserManager.getInstance().getDeviceId());
|
||||
object.put("oaid", HaloApp.getInstance().getOAID());
|
||||
object.put("rom", MetaUtil.getMeta().getRom());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
@ -265,7 +264,6 @@ public class LogUtils {
|
||||
object.put("channel", HaloApp.getInstance().getChannel());
|
||||
object.put("dia", MetaUtil.getBase64EncodedAndroidId());
|
||||
object.put("oaid", MetaUtil.INSTANCE.getMeta().getOaid());
|
||||
object.put("rom", MetaUtil.getMeta().getRom());
|
||||
object.put("time", Utils.getTime(context));
|
||||
object.put("network", DeviceUtils.getNetwork(context));
|
||||
object.put("user_id", UserManager.getInstance().getUserId());
|
||||
@ -291,7 +289,6 @@ public class LogUtils {
|
||||
metaObject.put("channel", meta.getChannel());
|
||||
metaObject.put("gid", meta.getGid());
|
||||
metaObject.put("oaid", meta.getOaid());
|
||||
metaObject.put("rom", MetaUtil.getMeta().getRom());
|
||||
metaObject.put("jnfj", MetaUtil.getBase64EncodedIMEI());
|
||||
metaObject.put("mac", meta.getMac());
|
||||
metaObject.put("manufacturer", meta.getManufacturer());
|
||||
@ -498,7 +495,6 @@ public class LogUtils {
|
||||
metaObject.put("os", meta.getOs());
|
||||
metaObject.put("userId", meta.getUserId());
|
||||
metaObject.put("oaid", HaloApp.getInstance().getOAID());
|
||||
metaObject.put("rom", MetaUtil.getMeta().getRom());
|
||||
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@ -33,7 +33,6 @@ import com.lightgame.utils.Utils
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.*
|
||||
import java.util.HashMap
|
||||
import java.util.Locale
|
||||
import java.util.concurrent.Executors
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.HashSet
|
||||
@ -289,22 +288,12 @@ object PackageHelper {
|
||||
* 用户是否已经使用另类方式获取已安装应用列表
|
||||
*/
|
||||
private fun isUseAlternativeWayToGetInstalledPackages(): Boolean {
|
||||
if (isBuggyHuaweiDevice()) {
|
||||
useAlternativeWayToGetInstalledPackages = false
|
||||
return false
|
||||
}
|
||||
|
||||
return useAlternativeWayToGetInstalledPackages
|
||||
|| (SPUtils.getBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API)
|
||||
.also { useAlternativeWayToGetInstalledPackages = it })
|
||||
}
|
||||
|
||||
private fun updateUseAlternativeWayToGetInstalledPackages() {
|
||||
if (isBuggyHuaweiDevice()) {
|
||||
useAlternativeWayToGetInstalledPackages = false
|
||||
return
|
||||
}
|
||||
|
||||
// 启用另类获取已安装应用列表的 API
|
||||
useAlternativeWayToGetInstalledPackages = true
|
||||
SPUtils.setBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API, true)
|
||||
@ -489,12 +478,7 @@ object PackageHelper {
|
||||
/**
|
||||
* 是否支持动态获取已安装应用列表权限
|
||||
*/
|
||||
private fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
|
||||
if (isBuggyHuaweiDevice()) {
|
||||
// 华为系 Android 10 设备存在 bug,调用过多可能会触发 DeadSystemException
|
||||
return false
|
||||
}
|
||||
|
||||
fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
|
||||
if (isUseAlternativeWayToGetInstalledPackages()) {
|
||||
// 已经使用另类获取已安装应用列表形式,强制判定为不支持动态获取已安装应用列表权限
|
||||
return false
|
||||
@ -805,13 +789,4 @@ object PackageHelper {
|
||||
return packageList
|
||||
}
|
||||
|
||||
private fun isBuggyHuaweiDevice(): Boolean {
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.Q) {
|
||||
val manufacturer = Build.MANUFACTURER.lowercase(Locale.CHINA) ?: ""
|
||||
return manufacturer == "huawei" || manufacturer == "honor"
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -3,12 +3,15 @@ package com.gh.common.util
|
||||
import android.content.Context
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.isVGameDownloadInDualDownloadMode
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.GameInstall
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.vspace.VHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
object PackageLauncher {
|
||||
|
||||
@ -90,6 +93,10 @@ object PackageLauncher {
|
||||
val intent = context.applicationContext.packageManager.getLaunchIntentForPackage(packageName)
|
||||
if (intent != null) {
|
||||
context.startActivity(intent)
|
||||
|
||||
// 记录光环内启动时间
|
||||
val packageLaunchTimeSp = HaloApp.getInstance().getSharedPreferences(Constants.SP_PACKAGE_LAUNCH_TIME, Context.MODE_PRIVATE)
|
||||
SPUtils.setLong(packageLaunchTimeSp, packageName, System.currentTimeMillis())
|
||||
} else {
|
||||
ToastUtils.toast("启动失败")
|
||||
}
|
||||
|
||||
@ -588,6 +588,21 @@ public class PackageUtils {
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 获取应用最后更新的时间
|
||||
*/
|
||||
public static long getLastUpdateTime(Context context, String packageName) {
|
||||
try {
|
||||
if (context == null) return 0;
|
||||
PackageInfo packageInfo = context.getApplicationContext().getPackageManager()
|
||||
.getPackageInfo(packageName, 0);
|
||||
return packageInfo.lastUpdateTime;
|
||||
} catch (NameNotFoundException e) {
|
||||
// do nothing
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回光环助手的版本信息
|
||||
*/
|
||||
|
||||
@ -37,7 +37,7 @@ public class PostCommentUtils {
|
||||
device.put("model", Build.MODEL);
|
||||
device.put("manufacturer", Build.MANUFACTURER);
|
||||
device.put("android_version", android.os.Build.VERSION.RELEASE);
|
||||
device.put("rom", MetaUtil.INSTANCE.getRom().getRomName() + " " + MetaUtil.INSTANCE.getRom().getRomVersion());
|
||||
device.put("rom", MetaUtil.INSTANCE.getRom().name() + " " + MetaUtil.INSTANCE.getRom().getVersionName());
|
||||
content.put("device", device);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
@ -9,14 +9,15 @@ import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.DefaultLifecycleObserver
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.DialogHelper
|
||||
import com.gh.gamecenter.entity.NewApiSettingsEntity
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.enlargeTouchArea
|
||||
import com.gh.gamecenter.common.utils.setDrawableEnd
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.entity.NewApiSettingsEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
|
||||
/**
|
||||
* 处理厂商纯净/安全模式的辅助类
|
||||
@ -312,11 +313,9 @@ object PureModeHelper {
|
||||
intent.setPackage("com.huawei.security.privacycenter")
|
||||
intent.setAction("com.huawei.securitycenter.PURE_MODE_ACTIVITY")
|
||||
intent.putExtra("intent_from_settings", true)
|
||||
intent.flags = Intent.FLAG_ACTIVITY_NEW_TASK
|
||||
HaloApp.getInstance().startActivity(intent)
|
||||
context.startActivity(intent)
|
||||
} catch (_: Exception) {
|
||||
toSystemSettings(context)
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -22,19 +22,19 @@ import com.gh.common.util.UsageStatsHelper.getDataByUsageEvents
|
||||
import com.gh.common.util.UsageStatsHelper.getDataByUsageStats
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.debugOnly
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
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.*
|
||||
import java.util.concurrent.ConcurrentHashMap
|
||||
|
||||
/**
|
||||
* 参考资料: https://developer.android.com/reference/android/app/usage/UsageStatsManager
|
||||
@ -52,9 +52,33 @@ object UsageStatsHelper {
|
||||
const val USAGE_STATUS_REQUEST_CODE = 233
|
||||
const val USAGE_STATUS_SP_KEY = "usage_status_sp_key"
|
||||
|
||||
private val usageStatsManager by lazy {
|
||||
HaloApp.getInstance().application.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
|
||||
}
|
||||
private val mApi by lazy { RetrofitManager.getInstance().api }
|
||||
private val mPreference by lazy { PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().application) }
|
||||
|
||||
private val lastUsedTimeMap = ConcurrentHashMap<String, Long>()
|
||||
|
||||
fun getLastUsedTimeByPackageName(packageName: String): Long = lastUsedTimeMap[packageName] ?: 0L
|
||||
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
fun updateLastUsedTimeByUsageStats() {
|
||||
val usageStatsMap = usageStatsManager.queryAndAggregateUsageStats(
|
||||
getDefaultBeginTime(),
|
||||
System.currentTimeMillis()
|
||||
) ?: return
|
||||
|
||||
for (entry in usageStatsMap) {
|
||||
if (entry.value.lastTimeUsed != 0L) {
|
||||
lastUsedTimeMap[entry.key] = entry.value.lastTimeUsed
|
||||
debugOnly {
|
||||
Utils.log("UsageStats getLastUsedTimeByUsageStats ==>" + entry.key + ":" + TimeUtils.getFormatTime(entry.value.lastTimeUsed, "yyyy-MM-dd HH:mm:ss"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 一般用于获取时间跨度较大的数据
|
||||
*
|
||||
@ -63,10 +87,6 @@ object UsageStatsHelper {
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
private fun getDataByUsageStats(lastPostTime: Long): JSONArray? {
|
||||
val usageStatsManager = HaloApp
|
||||
.getInstance().application
|
||||
.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
|
||||
|
||||
val usageStatsMap = usageStatsManager.queryAndAggregateUsageStats(
|
||||
lastPostTime,
|
||||
System.currentTimeMillis()
|
||||
@ -95,13 +115,7 @@ object UsageStatsHelper {
|
||||
*/
|
||||
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
private fun getDataByUsageEvents(startTime: Long): JSONArray? {
|
||||
|
||||
val mUsageStatsManager = HaloApp
|
||||
.getInstance()
|
||||
.application
|
||||
.getSystemService(Context.USAGE_STATS_SERVICE) as UsageStatsManager
|
||||
|
||||
val usageEvents = mUsageStatsManager.queryEvents(startTime, System.currentTimeMillis())
|
||||
val usageEvents = usageStatsManager.queryEvents(startTime, System.currentTimeMillis())
|
||||
val allEvents = ArrayList<UsageEvents.Event>()
|
||||
var currentEvent: UsageEvents.Event
|
||||
|
||||
@ -161,7 +175,7 @@ object UsageStatsHelper {
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
@TargetApi(Build.VERSION_CODES.LOLLIPOP_MR1)
|
||||
private fun postUsageStats(beginTime: Long) {
|
||||
private fun postUsageStats(beginTime: Long, doOnEnd: (() -> Unit)? = null) {
|
||||
debugOnly {
|
||||
Utils.log("UsageStats->beginTime:$beginTime endTime:" + System.currentTimeMillis())
|
||||
}
|
||||
@ -176,6 +190,7 @@ object UsageStatsHelper {
|
||||
debugOnly {
|
||||
Utils.log("UsageStats: 没有可上传的数据")
|
||||
}
|
||||
doOnEnd?.invoke()
|
||||
return
|
||||
}
|
||||
|
||||
@ -186,6 +201,7 @@ object UsageStatsHelper {
|
||||
debugOnly {
|
||||
Utils.log("UsageStats: 数据上传成功")
|
||||
}
|
||||
doOnEnd?.invoke()
|
||||
}
|
||||
})
|
||||
}
|
||||
@ -207,7 +223,7 @@ object UsageStatsHelper {
|
||||
|
||||
@JvmStatic
|
||||
@SuppressLint("CheckResult")
|
||||
fun checkAndPostUsageStats() {
|
||||
fun checkAndPostUsageStats(doOnSuccess: (() -> Unit)? = null) {
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
debugOnly {
|
||||
Utils.log("UsageStats checkAndPostUsageStats Android 版本小于22")
|
||||
@ -227,6 +243,8 @@ object UsageStatsHelper {
|
||||
return
|
||||
}
|
||||
|
||||
updateLastUsedTimeByUsageStats()
|
||||
|
||||
mApi.getUsageStatusUpdateTime(HaloApp.getInstance().gid)
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
@ -239,7 +257,7 @@ object UsageStatsHelper {
|
||||
} else {
|
||||
lastPostTime
|
||||
}
|
||||
postUsageStats(beginTime)
|
||||
postUsageStats(beginTime, doOnSuccess)
|
||||
} catch (e: JSONException) {
|
||||
Utils.log("UsageStats: 获取上次上传时间失败,错误信息:${e.message}")
|
||||
}
|
||||
|
||||
@ -22,7 +22,7 @@ import com.gh.gamecenter.info.InfoWrapperFragment
|
||||
import com.gh.gamecenter.libao.LibaoDetailFragment
|
||||
import com.gh.gamecenter.libao.LibaoFragment
|
||||
import com.gh.gamecenter.newsdetail.NewsDetailFragment
|
||||
import com.gh.gamecenter.personal.HaloPersonalFragment
|
||||
import com.gh.gamecenter.personal.MyHaloFragment
|
||||
import com.gh.gamecenter.qa.article.detail.ArticleDetailFragment
|
||||
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailFragment
|
||||
import com.gh.gamecenter.qa.subject.AskSubjectFragment
|
||||
@ -91,7 +91,7 @@ object ViewPagerFragmentHelper {
|
||||
}
|
||||
// 我的光环
|
||||
TYPE_MY_HALO -> {
|
||||
HaloPersonalFragment().setSuperiorChain(superiorChain).with(bundle)
|
||||
MyHaloFragment().setSuperiorChain(superiorChain).with(bundle)
|
||||
}
|
||||
// 社区首页
|
||||
TYPE_COMMUNITY_HOME -> {
|
||||
|
||||
@ -135,7 +135,7 @@ class ConfigFilterView @JvmOverloads constructor(
|
||||
|
||||
private fun showSelectionPopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
|
||||
sizeTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(sizeTv.context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_auxiliary_arrow_up_primary_8)
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_arrow_up)
|
||||
|
||||
val inflater = LayoutInflater.from(sizeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
|
||||
@ -0,0 +1,47 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import androidx.core.widget.NestedScrollView
|
||||
import kotlin.math.max
|
||||
|
||||
/**
|
||||
* 父容器优先滚动的NestedScrollView
|
||||
*/
|
||||
class ParentPriorityNestedScrollView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
NestedScrollView(context, attrs) {
|
||||
override fun onNestedPreScroll(target: View, dx: Int, dy: Int, consumed: IntArray, type: Int) {
|
||||
if (dy > 0) {
|
||||
super.onNestedPreScroll(target, dx, dy, consumed, type)
|
||||
val remainingDy = dy - consumed[1]
|
||||
if (remainingDy > 0) {
|
||||
val remainingScrollY = getVerticalScrollRange() - height - scrollY
|
||||
val consumedDy = minOf(remainingDy, remainingScrollY)
|
||||
scrollBy(0, consumedDy)
|
||||
consumed[1] += consumedDy
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getVerticalScrollRange(): Int {
|
||||
val count = childCount
|
||||
val parentSpace = height - paddingBottom - paddingTop
|
||||
if (count == 0) {
|
||||
return parentSpace
|
||||
}
|
||||
|
||||
val child = getChildAt(0)
|
||||
val lp = child.layoutParams as LayoutParams
|
||||
var scrollRange = child.bottom + lp.bottomMargin
|
||||
val scrollY = scrollY
|
||||
val overscrollBottom = max(0.0, (scrollRange - parentSpace).toDouble()).toInt()
|
||||
if (scrollY < 0) {
|
||||
scrollRange -= scrollY
|
||||
} else if (scrollY > overscrollBottom) {
|
||||
scrollRange += scrollY - overscrollBottom
|
||||
}
|
||||
|
||||
return scrollRange
|
||||
}
|
||||
}
|
||||
@ -9,11 +9,7 @@ import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil.getMeta
|
||||
import com.gh.gamecenter.common.loghub.LoghubUtils
|
||||
import com.gh.gamecenter.common.utils.DeviceUtils
|
||||
import com.gh.gamecenter.common.utils.asVGame
|
||||
import com.gh.gamecenter.common.utils.getExtension
|
||||
import com.gh.gamecenter.common.utils.getMetaExtra
|
||||
import com.gh.gamecenter.common.utils.isSimulatorGame
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.ndownload.NDataChanger
|
||||
import com.gh.ndownload.NDownloadBridge
|
||||
import com.halo.assistant.HaloApp
|
||||
@ -431,7 +427,6 @@ object DownloadDataHelper {
|
||||
metaObject.put("channel", meta.channel)
|
||||
metaObject.put("gid", meta.gid)
|
||||
metaObject.put("oaid", meta.oaid)
|
||||
metaObject.put("rom", meta.rom)
|
||||
metaObject.put("manufacturer", meta.manufacturer)
|
||||
metaObject.put("model", meta.model)
|
||||
metaObject.put("network", DeviceUtils.getNetwork(context))
|
||||
|
||||
@ -3,6 +3,9 @@ package com.gh.download
|
||||
import android.annotation.SuppressLint
|
||||
import android.text.TextUtils
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DataCollectionUtils
|
||||
import com.gh.common.util.PackageInstaller
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.common.xapk.XapkInstaller
|
||||
import com.gh.download.server.BrowserInstallHelper
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
@ -30,7 +33,9 @@ import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.MediaType
|
||||
import okhttp3.MediaType.Companion.toMediaTypeOrNull
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import org.json.JSONException
|
||||
import org.json.JSONObject
|
||||
|
||||
@ -7,7 +7,6 @@ import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.DefaultUrlHandler
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.loadDataCompat
|
||||
import com.gh.gamecenter.databinding.DownloadDialogInstructionItemBinding
|
||||
|
||||
class DownloadDialogInstructionItemViewHolder(val binding: DownloadDialogInstructionItemBinding) :
|
||||
@ -15,8 +14,11 @@ class DownloadDialogInstructionItemViewHolder(val binding: DownloadDialogInstruc
|
||||
|
||||
fun bindItem(listData: List<DownloadDialogItemData>, position: Int, entrance: String) {
|
||||
val instruction = listData[position].instruction
|
||||
binding.webView.loadDataCompat(
|
||||
binding.webView.loadDataWithBaseURL(
|
||||
null,
|
||||
"<body style='margin:0;padding:0;color:#666666;font-size:12px;line-height:18px;'>$instruction</body>",
|
||||
"text/html",
|
||||
"utf-8", null
|
||||
)
|
||||
binding.webView.settings
|
||||
binding.webView.setBackgroundColor(Color.TRANSPARENT)
|
||||
|
||||
@ -9,7 +9,6 @@ import androidx.fragment.app.FragmentActivity
|
||||
import com.gh.gamecenter.common.base.fragment.BaseDialogFragment
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.common.utils.loadDataCompat
|
||||
import com.gh.gamecenter.databinding.DialogDownloadLinkBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.halo.assistant.HaloApp
|
||||
@ -28,7 +27,10 @@ class DownloadLinkDialog : BaseDialogFragment() {
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View {
|
||||
val binding: DialogDownloadLinkBinding = DialogDownloadLinkBinding.inflate(layoutInflater, container, false)
|
||||
binding.title.text = mLinkEntity?.title
|
||||
binding.webView.loadDataCompat(mLinkEntity?.content ?: "")
|
||||
binding.webView.loadDataWithBaseURL(
|
||||
null,
|
||||
mLinkEntity?.content ?: "", "text/html", "utf-8", null
|
||||
)
|
||||
|
||||
binding.confirm.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
|
||||
@ -14,15 +14,21 @@ import androidx.annotation.NonNull;
|
||||
import com.gh.common.util.MessageShareUtils;
|
||||
import com.gh.common.util.QRCodeUtils;
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity;
|
||||
import com.gh.gamecenter.common.constant.RouteConsts;
|
||||
import com.gh.gamecenter.common.utils.ExtensionsKt;
|
||||
import com.gh.gamecenter.common.utils.ShareUtils;
|
||||
import com.gh.gamecenter.core.utils.MtaHelper;
|
||||
import com.tencent.tauth.Tencent;
|
||||
import com.therouter.router.Route;
|
||||
|
||||
/**
|
||||
* Created by khy on 2017/2/6.
|
||||
* 分享光环
|
||||
*/
|
||||
@Route(
|
||||
path = RouteConsts.activity.shareGhActivity,
|
||||
description = "分享光环"
|
||||
)
|
||||
public class ShareGhActivity extends ToolBarActivity {
|
||||
|
||||
ImageView mGhQrcode;
|
||||
|
||||
@ -2,24 +2,24 @@ package com.gh.gamecenter.collection;
|
||||
|
||||
import android.app.Application;
|
||||
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import com.gh.common.history.HistoryDatabase;
|
||||
import com.gh.common.history.HistoryHelper;
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils;
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel;
|
||||
import com.gh.gamecenter.common.baselist.LoadType;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.gamecenter.core.utils.TimeUtils;
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils;
|
||||
import com.gh.gamecenter.feature.entity.NewsEntity;
|
||||
import com.gh.gamecenter.feature.entity.ViewsEntity;
|
||||
import com.gh.gamecenter.info.NewsViewsRepository;
|
||||
import com.gh.gamecenter.login.user.UserManager;
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.lifecycle.LiveData;
|
||||
|
||||
import io.reactivex.Observable;
|
||||
import io.reactivex.Single;
|
||||
|
||||
@ -83,10 +83,7 @@ public class ArticleViewModel extends ListViewModel<NewsEntity, NewsEntity> {
|
||||
if (ArticleFragment.COLLECTION.equals(type)) {
|
||||
return Single.fromObservable(RetrofitManager.getInstance().getApi().getCollectionArticle(UserManager.getInstance().getUserId(), page));
|
||||
} else {
|
||||
if (page > 5) {
|
||||
return Single.create(emitter -> emitter.onSuccess(new ArrayList<>()));
|
||||
}
|
||||
return HistoryDatabase.Companion.getInstance().newsDao().getNewsWithOffset(20, (page - 1) * 20);
|
||||
return HistoryDatabase.Companion.getInstance().newsDao().getNewsWithOffset(20, (page - 1) * 20, TimeUtils.getTimestampMonthsAgo(3));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -2,17 +2,18 @@ package com.gh.gamecenter.collection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.common.history.HistoryDatabase
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.feature.entity.AnswerEntity
|
||||
import com.gh.gamecenter.feature.entity.ArticleEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
@ -45,11 +46,7 @@ class CommunityArticleViewModel(application: Application) : ListViewModel<Articl
|
||||
})
|
||||
)
|
||||
} else {
|
||||
if (page > 5) {
|
||||
Single.create { it.onSuccess(arrayListOf()) }
|
||||
} else {
|
||||
HistoryDatabase.instance.articleDao().getArticleWithOffset(20, (page - 1) * 20)
|
||||
}
|
||||
HistoryDatabase.instance.articleDao().getArticleWithOffset(20, (page - 1) * 20, TimeUtils.getTimestampMonthsAgo(3))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.observableToMain
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
@ -45,11 +46,8 @@ class GamesCollectionViewModel(
|
||||
TYPE_COLLECT -> mApi.getFavoriteGameCollectionList(userId, page)
|
||||
|
||||
TYPE_HISTORY -> {
|
||||
if (page > 5) {
|
||||
Single.create { it.onSuccess(arrayListOf()) }
|
||||
} else {
|
||||
HistoryDatabase.instance.gamesCollectionDao().getGamesCollectionWithOffset(20, (page - 1) * 20)
|
||||
}
|
||||
HistoryDatabase.instance.gamesCollectionDao()
|
||||
.getGamesCollectionWithOffset(20, (page - 1) * 20, TimeUtils.getTimestampMonthsAgo(3))
|
||||
}
|
||||
|
||||
else -> {
|
||||
|
||||
@ -1,13 +1,14 @@
|
||||
package com.gh.gamecenter.collection
|
||||
|
||||
import android.app.Application
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.history.HistoryDatabase
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.entity.PersonalHistoryEntity
|
||||
import com.gh.gamecenter.feature.entity.User
|
||||
@ -71,12 +72,7 @@ class VideoViewModel(application: Application) : ListViewModel<MyVideoEntity, My
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<MyVideoEntity>>? {
|
||||
if (type == VideoFragment.VideoStyle.BROWSING_HISTORY.value) {
|
||||
if (page > 5) {
|
||||
return Single.create { it.onSuccess(arrayListOf()) }
|
||||
}
|
||||
if (type == VideoFragment.VideoStyle.BROWSING_HISTORY.value) {
|
||||
return HistoryDatabase.instance.videoHistoryDao().getVideoWithOffset(20, (page - 1) * 20)
|
||||
}
|
||||
return HistoryDatabase.instance.videoHistoryDao().getVideoWithOffset(20, (page - 1) * 20, TimeUtils.getTimestampMonthsAgo(3))
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
@ -48,6 +48,7 @@ class UpdatableGameViewModel(
|
||||
|
||||
private var mPackageUpdateList = ArrayList<PackageUpdate>() // 包名更新列表,包括了我的版本和其它版本 (如果存在的话)
|
||||
|
||||
private var mValidGameUpdateListLiveData = MutableLiveData<List<GameUpdateEntity>>()
|
||||
private var mUpdatableListLiveData = MutableLiveData<ArrayList<UpdatableDataItem>>()
|
||||
|
||||
private var mHasLandPageAddressDialog = false // 是否存在由第三方提供的游戏下载
|
||||
@ -65,6 +66,7 @@ class UpdatableGameViewModel(
|
||||
private val mDownloadManager by lazy { DownloadManager.getInstance() }
|
||||
|
||||
var updatableData: LiveData<ArrayList<UpdatableDataItem>> = mUpdatableListLiveData
|
||||
var validGameUpdateData: LiveData<List<GameUpdateEntity>> = mValidGameUpdateListLiveData
|
||||
|
||||
/**
|
||||
* 更新可更新数据列表
|
||||
@ -319,6 +321,8 @@ class UpdatableGameViewModel(
|
||||
|
||||
mHasLandPageAddressDialog = landPageAddressUpdateList.isNotEmpty()
|
||||
|
||||
mValidGameUpdateListLiveData.postValue(validPackageUpdateList.map { it.matchedVersionUpdate })
|
||||
|
||||
// 构建装饰好的页面列表
|
||||
// 正常的我的版本
|
||||
if (validPackageUpdateList.isNotEmpty()) {
|
||||
|
||||
@ -9,7 +9,7 @@ import io.reactivex.Single
|
||||
|
||||
class GameSubjectDSPRemoteDataSource(private val api: DspApiService = RetrofitManager.getInstance().dspApiService) {
|
||||
|
||||
fun getDspGames(type: String, count: Int): Single<List<GameEntity>> {
|
||||
fun getDspGames(count: Int): Single<List<GameEntity>> {
|
||||
val meta = MetaUtil.getMeta()
|
||||
val request = mapOf(
|
||||
"device" to mapOf(
|
||||
|
||||
@ -0,0 +1,26 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import com.gh.gamecenter.servers.gametest2.GameServerTestV2ViewModel
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class GameServerTestDisplaySetting(
|
||||
@SerializedName("time_text_past")
|
||||
val timeTextPast: String = RECENT,
|
||||
@SerializedName("time_text_present")
|
||||
val timeTextPresent: String = TODAY,
|
||||
@SerializedName("time_text_future")
|
||||
val timeTextFuture: String = FUTURE,
|
||||
@SerializedName("game_category")
|
||||
val gameCategory: List<String> = listOf(
|
||||
GameServerTestV2ViewModel.GameCategory.Local.value,
|
||||
GameServerTestV2ViewModel.GameCategory.Online.value,
|
||||
GameServerTestV2ViewModel.GameCategory.Welfare.value,
|
||||
GameServerTestV2ViewModel.GameCategory.Gjonline.value
|
||||
),
|
||||
) {
|
||||
companion object {
|
||||
const val RECENT = "近期"
|
||||
const val TODAY = "今天"
|
||||
const val FUTURE = "预约"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class MyHaloContentCard(
|
||||
@SerializedName("vip_card")
|
||||
val vipCard: VipCard? = null,
|
||||
val function: Function? = null,
|
||||
) {
|
||||
data class VipCard(
|
||||
@SerializedName("vip_card_style")
|
||||
val style: String = "", // 会员入口卡片-前端展示卡片样式(a_long_card:一个长卡片、two_short_cards:两个短卡片),仅当type=vip时才有此字段
|
||||
val data: List<VipCardData> = listOf()
|
||||
) {
|
||||
data class VipCardData(
|
||||
@SerializedName("vip_type")
|
||||
val vipType: String = "",// 会员类型(accelerator:加速器会员、cloud_game:云游戏会员)
|
||||
@SerializedName("user_type")
|
||||
val userType: String = "",
|
||||
val des: String = "",// 权益说明
|
||||
val button: String = "",// 按钮文案(off:不显示按钮、输入内容:自定义文案)
|
||||
) {
|
||||
companion object {
|
||||
const val TYPE_ACCELERATOR = "accelerator"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
data class Function(
|
||||
@SerializedName("compliance_status")
|
||||
val complianceStatus: Boolean = false, // 合规内容-显示状态【true/false】
|
||||
val data: List<FunctionData> = listOf(),
|
||||
) {
|
||||
data class FunctionData(
|
||||
@SerializedName("_id")
|
||||
val id: String = "",
|
||||
val name: String = "",
|
||||
val icon: String = "",
|
||||
val category: String = "",
|
||||
val link: LinkEntity? = null, // 跳转链接类型(resource:资源类型(以往的通用链接处理)、function:功能类型(链接的type和text都是中文名称功能名))
|
||||
@SerializedName("remind_switch")
|
||||
var remindSwitch: Boolean = false // 红点提醒开关(开启:true, 关闭:false)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
|
||||
data class SearchGameUnionEntity(
|
||||
@SerializedName("type")
|
||||
private val _type: String? = null,
|
||||
@SerializedName("link_game")
|
||||
val linkGame: GameEntity? = null,
|
||||
@SerializedName("link_wechat_game_cpm_column")
|
||||
val linkWechatGameCpmColumn: SearchSubjectEntity? = null,
|
||||
@SerializedName("link_dsp_game_column")
|
||||
val linkDspGameColumn: SearchSubjectEntity? = null,
|
||||
@SerializedName("link_wechat_game")
|
||||
val linkWechatGame: GameEntity? = null,
|
||||
@SerializedName("link_column")
|
||||
val linkColum: SearchSubjectEntity? = null,
|
||||
@SerializedName("link_ad_space")
|
||||
val linkAdSpace: AdConfig? = null
|
||||
) {
|
||||
val type: String
|
||||
get() = _type ?: ""
|
||||
|
||||
companion object {
|
||||
const val TYPE_GAME = "game"
|
||||
const val TYPE_WECHAT_GAME_CPM_COLUMN = "wechat_game_cpm_column"
|
||||
const val TYPE_DSP_GAME_COLUMN = "dsp_game_column"
|
||||
const val TYPE_WECHAT_GAME = "wechat_game"
|
||||
const val TYPE_COLUMN = "column"
|
||||
const val TYPE_AD_SPACE = "ad_space"
|
||||
}
|
||||
}
|
||||
@ -2,8 +2,9 @@ package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.gamecenter.entity.SearchGameUnionEntity.Companion.TYPE_DSP_GAME_COLUMN
|
||||
import com.gh.gamecenter.entity.SearchGameUnionEntity.Companion.TYPE_WECHAT_GAME_CPM_COLUMN
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.personalhome.home.UserHistoryViewModel
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@ -18,10 +19,6 @@ data class SearchSubjectEntity(
|
||||
val codeId: String = "", // 广告CODE_ID(本地字段),不为空时为广告专题
|
||||
@SerializedName("ad_icon_active")
|
||||
val adIconActive: Boolean = false,
|
||||
// 本地字段,标记是否为微信小游戏CPM专题
|
||||
var isWGameSubjectCPM: Boolean = false,
|
||||
// 本地字段,标记是否为DSP专题
|
||||
var isDspSubject: Boolean = false,
|
||||
val type: String = "",
|
||||
|
||||
@SerializedName("column_type")
|
||||
@ -31,10 +28,11 @@ data class SearchSubjectEntity(
|
||||
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"
|
||||
}
|
||||
val isWGameSubjectCPM: Boolean
|
||||
get() = type == TYPE_WECHAT_GAME_CPM_COLUMN
|
||||
|
||||
val isDspSubject: Boolean
|
||||
get() = type == TYPE_DSP_GAME_COLUMN
|
||||
|
||||
fun getFilterGame() = RegionSettingHelper.filterGame(games)
|
||||
}
|
||||
@ -886,7 +886,7 @@ class GameDetailViewModel(
|
||||
params["source"] = HaloApp.getInstance().application.getString(R.string.app_name)
|
||||
params["jnfj"] = MetaUtil.getBase64EncodedIMEI()
|
||||
params["manufacturer"] = Build.MANUFACTURER
|
||||
params["rom"] = MetaUtil.getRom().romName + " " + MetaUtil.getRom().romVersion
|
||||
params["rom"] = MetaUtil.getRom().name + " " + MetaUtil.getRom().versionName
|
||||
|
||||
params["suggestion_type"] = "游戏求更新"
|
||||
params["game_id"] = game?.id ?: ""
|
||||
|
||||
@ -361,6 +361,7 @@ class GameDetailAcceleratorUiHelper(private val binding: DetailDownloadItemBindi
|
||||
const val SOURCE_ENTRANCE_MY_ASSETS = "我的资产"
|
||||
const val SOURCE_ENTRANCE_SEARCH = "搜索页"
|
||||
const val SOURCE_ENTRANCE_RECENTLY_PLAYED = "最近在玩"
|
||||
const val SOURCE_ENTRANCE_MY_HALO_INSTALLED = "我的光环-已安装"
|
||||
const val BUTTON_NAME_ENTER_GAME = "进入游戏"
|
||||
const val BUTTON_NAME_STOP_ACCELERATOR = "停止加速"
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.Color
|
||||
import android.graphics.RectF
|
||||
import android.graphics.Typeface
|
||||
import android.graphics.drawable.GradientDrawable
|
||||
import android.graphics.drawable.TransitionDrawable
|
||||
@ -700,7 +701,7 @@ class GameDetailFragment : LazyFragment(), IScrollable {
|
||||
val infoTagLocation = IntArray(2)
|
||||
binding.infoTagContainer.getLocationInWindow(infoTagLocation)
|
||||
val top = (infoTagLocation[1] - 12F.dip2px()).toFloat()
|
||||
root.targetRect.set(8F.dip2px().toFloat(), top, (screenWidth - 8F.dip2px()).toFloat(), top + 53F.dip2px())
|
||||
root.setTargetRect(RectF(8F.dip2px().toFloat(), top, (screenWidth - 8F.dip2px()).toFloat(), top + 53F.dip2px()))
|
||||
guideIv.translationY = top - 56F.dip2px()
|
||||
containerView.translationY = infoTagLocation[1].toFloat()
|
||||
accelerateView.goneIf(viewModel.infoTagLiveData.value?.requestSpeedStatus != "on") {
|
||||
|
||||
@ -7,7 +7,7 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.forEach
|
||||
import androidx.core.view.children
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
@ -117,7 +117,7 @@ class GameLibaoAdapter(
|
||||
|
||||
binding.horizontalScrollView.goneIf(libaoEntity.materials.isEmpty()) {
|
||||
if (binding.imagesContainer.tag == libaoEntity.id) {
|
||||
binding.imagesContainer.forEach { view ->
|
||||
binding.imagesContainer.children.forEach { view ->
|
||||
if (view is SimpleDraweeView) {
|
||||
view.hierarchy.roundingParams = RoundingParams().apply {
|
||||
setCornersRadius(4F.dip2px().toFloat())
|
||||
|
||||
@ -541,7 +541,7 @@ class RatingEditActivity : ToolBarActivity(), KeyboardHeightObserver {
|
||||
jsonObject.put("game_version", gameVersion)
|
||||
jsonObject.put("source", if (mFromAmway) "anliwall" else "game_detail")
|
||||
jsonObject.put("plugin_version", PackageUtils.getMetaData(this, mInstallPackageName, "gh_version"))
|
||||
jsonObject.put("rom", MetaUtil.getRom().romName + " " + MetaUtil.getRom().romVersion)
|
||||
jsonObject.put("rom", MetaUtil.getRom().name + " " + MetaUtil.getRom().versionName)
|
||||
jsonObject.put("again", again)
|
||||
|
||||
val body = jsonObject.toString().toRequestBody("application/json".toMediaTypeOrNull())
|
||||
|
||||
@ -8,20 +8,22 @@ import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import com.gh.gamecenter.common.constant.ItemViewType
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.constant.ItemViewType
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.DrawableView
|
||||
import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.utils.StringUtils
|
||||
import com.gh.gamecenter.databinding.PopupHistoryOptionBinding
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.feature.databinding.GameItemBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
class HistoryGameListAdapter(context: Context, private val mViewModel: HistoryGameListViewModel) :
|
||||
ListAdapter<GameEntity>(context) {
|
||||
@ -30,6 +32,23 @@ class HistoryGameListAdapter(context: Context, private val mViewModel: HistoryGa
|
||||
private var mPopWindow: PopupWindow? = null
|
||||
private var mPopupBinding: PopupHistoryOptionBinding? = null
|
||||
var selectItems = arrayListOf<String>()
|
||||
val positionAndPackageMap = HashMap<String, Int>()
|
||||
|
||||
override fun setListData(updateData: MutableList<GameEntity>?) {
|
||||
positionAndPackageMap.clear()
|
||||
// 记录游戏位置
|
||||
if (updateData != null) {
|
||||
for (i in 0 until updateData.size) {
|
||||
val gameEntity = updateData[i]
|
||||
var packages = gameEntity.id
|
||||
for (apkEntity in gameEntity.getApk()) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
positionAndPackageMap[packages + i] = i
|
||||
}
|
||||
}
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
@ -113,7 +132,7 @@ class HistoryGameListAdapter(context: Context, private val mViewModel: HistoryGa
|
||||
"(浏览记录:游戏)",
|
||||
StringUtils.buildString("浏览记录", ":", gameEntity.name)
|
||||
)
|
||||
DownloadItemUtils.updateItemWithViewOnlyStyle(GameViewHolder(holder.binding))
|
||||
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), false)
|
||||
|
||||
holder.itemView.setOnLongClickListener {
|
||||
consume {
|
||||
@ -184,4 +203,28 @@ class HistoryGameListAdapter(context: Context, private val mViewModel: HistoryGa
|
||||
checkAllCb.isChecked = selectItems.size == mEntityList.size
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemByDownload(download: DownloadEntity) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(download.packageName) && key.contains(download.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && mEntityList != null && position < mEntityList.size) {
|
||||
mEntityList[position].getEntryMap()[download.platform] = download
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(status.packageName) && key.contains(status.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && mEntityList != null && position < mEntityList.size) {
|
||||
mEntityList[position].getEntryMap().remove(status.platform)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -4,20 +4,44 @@ import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.xapk.XapkInstaller
|
||||
import com.gh.common.xapk.XapkUnzipStatus
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.common.view.CustomDividerItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
open class HistoryGameListFragment : ListFragment<GameEntity, HistoryGameListViewModel>(), IBatchDelete {
|
||||
|
||||
private var mAdapter: HistoryGameListAdapter? = null
|
||||
private val mBinding by lazy { FragmentListBaseSkeletonBinding.inflate(layoutInflater) }
|
||||
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
mAdapter?.notifyItemByDownload(downloadEntity)
|
||||
|
||||
if (downloadEntity.meta[XapkInstaller.XAPK_UNZIP_STATUS] == XapkUnzipStatus.FAILURE.name) {
|
||||
showUnzipFailureDialog(downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDataInit(downloadEntity: DownloadEntity) {
|
||||
mAdapter?.notifyItemByDownload(downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayoutId() = R.layout.fragment_list_base_skeleton
|
||||
override fun getInflatedLayout() = mBinding.root
|
||||
|
||||
@ -55,4 +79,43 @@ open class HistoryGameListFragment : ListFragment<GameEntity, HistoryGameListVie
|
||||
}
|
||||
return mItemDecoration
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
private fun showUnzipFailureDialog(downloadEntity: DownloadEntity) {
|
||||
val data = mAdapter?.positionAndPackageMap ?: return
|
||||
for (gameAndPosition in data) {
|
||||
if (gameAndPosition.key.contains(downloadEntity.packageName)) {
|
||||
val targetView = mLayoutManager.findViewByPosition(gameAndPosition.value)
|
||||
if (targetView != null) {
|
||||
DialogUtils.showUnzipFailureDialog(requireContext(), downloadEntity)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 下载被删除事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(status: EBDownloadStatus) {
|
||||
if ("delete" == status.status) {
|
||||
mAdapter?.notifyItemAndRemoveDownload(status)
|
||||
}
|
||||
}
|
||||
|
||||
// 安装/卸载 事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(busFour: EBPackage) {
|
||||
if (!busFour.fromInit && busFour.isInstalledOrUninstalled()) {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -1,32 +1,86 @@
|
||||
package com.gh.gamecenter.history
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.common.history.HistoryDatabase
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.TimeUtils
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.utils.ApkActiveUtils
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.SingleEmitter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import retrofit2.HttpException
|
||||
|
||||
class HistoryGameListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? {
|
||||
return null
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
|
||||
return if (page > 5) {
|
||||
Single.create { it.onSuccess(arrayListOf()) }
|
||||
} else {
|
||||
HistoryDatabase.instance.gameDao().getGamesWithOffset(20, (page - 1) * 20).map {
|
||||
val gameEntityList = arrayListOf<GameEntity>()
|
||||
for (history in it) {
|
||||
gameEntityList.add(history.convertHistoryGameEntityToGameEntity())
|
||||
}
|
||||
gameEntityList
|
||||
return Single.create { emitter ->
|
||||
HistoryDatabase.instance.gameDao().getGamesWithOffset(20, (page - 1) * 20, TimeUtils.getTimestampMonthsAgo(3))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
if (it.isEmpty()) {
|
||||
emitter.onSuccess(emptyList())
|
||||
} else {
|
||||
val ids = it.map { historyGameEntity -> historyGameEntity.id }
|
||||
val result = ArrayList<GameEntity>()
|
||||
val sequences = ArrayList<Observable<GameEntity>>()
|
||||
for (id in ids) {
|
||||
sequences.add(api.getGameDigest(id))
|
||||
}
|
||||
Observable.mergeDelayError(sequences)
|
||||
.map(ApkActiveUtils.filterMapper)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<GameEntity?>() {
|
||||
override fun onComplete() {
|
||||
processingData(emitter, it, result)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
processingData(emitter, it, result)
|
||||
}
|
||||
|
||||
override fun onNext(response: GameEntity) {
|
||||
ApkActiveUtils.filterHideApk(response)
|
||||
result.add(response)
|
||||
}
|
||||
})
|
||||
}
|
||||
}, {
|
||||
emitter.onError(it)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
private fun processingData(
|
||||
emitter: SingleEmitter<List<GameEntity>>,
|
||||
historyGameList: List<HistoryGameEntity>,
|
||||
gameList: List<GameEntity>
|
||||
) {
|
||||
if (gameList.isEmpty()) {
|
||||
val gameEntityList = arrayListOf<GameEntity>()
|
||||
for (history in historyGameList) {
|
||||
gameEntityList.add(history.convertHistoryGameEntityToGameEntity())
|
||||
}
|
||||
emitter.onSuccess(gameEntityList)
|
||||
} else {
|
||||
emitter.onSuccess(gameList)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -5,12 +5,15 @@ import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.collection.ArticleFragment
|
||||
import com.gh.gamecenter.collection.CommunityArticleFragment
|
||||
import com.gh.gamecenter.collection.GamesCollectionFragment
|
||||
import com.gh.gamecenter.collection.VideoFragment
|
||||
import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.collection.*
|
||||
import com.gh.gamecenter.core.utils.MtaHelper
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
|
||||
class HistoryWrapperFragment : BaseFragment_TabLayout() {
|
||||
@ -26,7 +29,6 @@ class HistoryWrapperFragment : BaseFragment_TabLayout() {
|
||||
tabTitleList.add(getString(R.string.main_game))
|
||||
tabTitleList.add(getString(R.string.game_collection))
|
||||
tabTitleList.add(getString(R.string.video))
|
||||
tabTitleList.add(getString(R.string.answer))
|
||||
tabTitleList.add(getString(R.string.collection_article))
|
||||
tabTitleList.add(getString(R.string.collection_info))
|
||||
}
|
||||
@ -79,12 +81,6 @@ class HistoryWrapperFragment : BaseFragment_TabLayout() {
|
||||
fragments.add(VideoFragment().with((arguments?.clone() as? Bundle)?.apply {
|
||||
putString("videoStyle", VideoFragment.VideoStyle.BROWSING_HISTORY.value)
|
||||
}))
|
||||
fragments.add(AnswerFragment().with((arguments?.clone() as? Bundle)?.apply {
|
||||
putString(
|
||||
EntranceConsts.KEY_TYPE,
|
||||
AnswerFragment.HISTORY
|
||||
)
|
||||
}))
|
||||
fragments.add(CommunityArticleFragment().with((arguments?.clone() as? Bundle)?.apply {
|
||||
putString(
|
||||
EntranceConsts.KEY_TYPE,
|
||||
|
||||
@ -384,7 +384,7 @@ class CustomPageRepository private constructor(
|
||||
pageInfo.componentPosition
|
||||
)
|
||||
)
|
||||
dspSubjectUseCase.getDspGames(subject.columnType, subject.size.index)
|
||||
dspSubjectUseCase.getDspGames(subject.size.index)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<List<GameEntity>>() {
|
||||
|
||||
@ -3,23 +3,24 @@ package com.gh.gamecenter.message
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.common.utils.createRequestBody
|
||||
import com.gh.gamecenter.db.GameTrendsDao
|
||||
import com.gh.gamecenter.db.info.GameTrendsInfo
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
|
||||
import com.gh.gamecenter.common.utils.createRequestBody
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.db.GameTrendsDao
|
||||
import com.gh.gamecenter.db.info.GameTrendsInfo
|
||||
import com.gh.gamecenter.entity.AddonsUnreadEntity
|
||||
import com.gh.gamecenter.feature.entity.ConcernEntity
|
||||
import com.gh.gamecenter.feature.entity.MessageDigestEntity
|
||||
import com.gh.gamecenter.feature.entity.MessageUnreadCount
|
||||
import com.gh.gamecenter.feature.entity.MessageUnreadEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.google.gson.reflect.TypeToken
|
||||
@ -69,7 +70,7 @@ object MessageUnreadRepository {
|
||||
@SuppressLint("CheckResult")
|
||||
fun loadMessageUnreadTotal(isRecordData: Boolean = false) {
|
||||
Observable.zip(
|
||||
getMessageUnread(), getDiscoveryData(), getNewAddonsData(), getAddonsUnreadCount()
|
||||
getMessageUnread(), getDiscoveryData(), getFunctionsUnreadCount(), getAddonsUnreadCount()
|
||||
) { t1, t2, t3, t4 ->
|
||||
zixunConcern.postValue(t2 > 0)
|
||||
if (isRecordData) {
|
||||
@ -163,41 +164,19 @@ object MessageUnreadRepository {
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun getNewAddonsData(): Observable<Int> {
|
||||
var count = 0
|
||||
return Observable.create {
|
||||
mApi.newHaloAddons
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ArrayList<HaloAddonEntity>>() {
|
||||
override fun onSuccess(data: ArrayList<HaloAddonEntity>) {
|
||||
val funcHaveReadRecord: HashSet<String> =
|
||||
SPUtils.getStringSet(Constants.SP_ADDONS_FUNCS_HAVE_READ) as HashSet<String>
|
||||
val recommendHaveReadRecord: HashSet<String> =
|
||||
SPUtils.getStringSet(Constants.SP_ADDONS_RECOMMEND_HAVE_READ) as HashSet<String>
|
||||
data.forEach { group ->
|
||||
if (group.groupType == "more_features") {
|
||||
group.addons.forEach { addonLinkEntity ->
|
||||
if (addonLinkEntity.remind && !funcHaveReadRecord.contains(addonLinkEntity.id)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
if (group.groupType == "recommend") {
|
||||
group.addons.forEach { addonLinkEntity ->
|
||||
if (addonLinkEntity.remind && !recommendHaveReadRecord.contains(addonLinkEntity.id)) {
|
||||
count++
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
it.onNext(count)
|
||||
it.onComplete()
|
||||
}
|
||||
})
|
||||
}
|
||||
private fun getFunctionsUnreadCount(): Observable<Int> {
|
||||
return mNewApi.myHaloContentCard
|
||||
.compose(singleToMain())
|
||||
.toObservable()
|
||||
.map { myHaloContentCard ->
|
||||
val haveReadSet = SPUtils.getStringSet(Constants.SP_ADDONS_FUNCS_HAVE_READ)
|
||||
myHaloContentCard.function?.data.orEmpty()
|
||||
.count { it.remindSwitch && !haveReadSet.contains(it.id) }
|
||||
}
|
||||
.onErrorReturnItem(0)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun getAddonsUnreadCount(): Observable<Int> {
|
||||
return Observable.create {
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
|
||||
@ -4,7 +4,7 @@ import com.gh.gamecenter.SearchActivity.Companion.TRACK_SEARCH_TYPE_INPUT
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.search.SearchGameResultFragment
|
||||
import com.gh.gamecenter.search.SearchGameResultViewModel
|
||||
import com.gh.gamecenter.search.viewmodel.SearchGameResultViewModel
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
/**
|
||||
@ -19,7 +19,6 @@ class MiniGameSearchResultFragment : SearchGameResultFragment() {
|
||||
val factory = SearchGameResultViewModel.Factory(
|
||||
HaloApp.getInstance().application,
|
||||
mKey,
|
||||
true,
|
||||
MiniGameSearchResultRepository(),
|
||||
TRACK_SEARCH_TYPE_INPUT,
|
||||
activity?.intent?.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
|
||||
|
||||
@ -1,45 +1,25 @@
|
||||
package com.gh.gamecenter.minigame
|
||||
|
||||
import com.gh.gamecenter.dsp.data.GameSubjectDSPRemoteDataSource
|
||||
import com.gh.gamecenter.entity.SearchSubjectEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.minigame.wechat.WGameSubjectCPMRemoteDataSource
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.retrofit.service.ApiService
|
||||
import com.gh.gamecenter.search.ISearchGameResultRepository
|
||||
import com.gh.gamecenter.search.SearchItemData
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
class MiniGameSearchResultRepository(
|
||||
private val api: ApiService = RetrofitManager.getInstance().newApi,
|
||||
private val mWGameSubjectCPMDataSource: WGameSubjectCPMRemoteDataSource = WGameSubjectCPMRemoteDataSource(),
|
||||
private val mGameSubjectDSPDataSource: GameSubjectDSPRemoteDataSource = GameSubjectDSPRemoteDataSource(RetrofitManager.getInstance().dspApiService)
|
||||
private val api: ApiService = RetrofitManager.getInstance().newApi
|
||||
) : ISearchGameResultRepository {
|
||||
|
||||
override fun getSearchGame(
|
||||
key: String?,
|
||||
page: Int
|
||||
): Observable<List<GameEntity>> {
|
||||
): Observable<List<SearchItemData>> {
|
||||
return api.getSearchMiniGameList(key, page, 20)
|
||||
.map { data ->
|
||||
data.map {
|
||||
SearchItemData(it, type = "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getSearchMiniGameCPM(key: String?): Observable<List<GameEntity>> {
|
||||
return Observable.just(emptyList())
|
||||
}
|
||||
|
||||
override fun getSearchSubject(key: String?, page: Int): Observable<List<SearchSubjectEntity>> {
|
||||
return Observable.just(emptyList())
|
||||
}
|
||||
|
||||
override fun getWGameCPMGameList(size: Int): Single<MutableList<GameEntity>> {
|
||||
return mWGameSubjectCPMDataSource.getUserRecommendCPMList(pageSize = size)
|
||||
}
|
||||
|
||||
override fun getDspGameList(
|
||||
columnType: String,
|
||||
showDownload: Boolean,
|
||||
size: Int,
|
||||
): Single<List<GameEntity>> {
|
||||
return mGameSubjectDSPDataSource.getDspGames(columnType, size)
|
||||
}
|
||||
}
|
||||
@ -1,41 +1,24 @@
|
||||
package com.gh.gamecenter.mygame
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.therouter.TheRouter
|
||||
import com.gh.common.repository.ReservationRepository
|
||||
import com.gh.common.util.ReservationHelper.getReserveRequestBody
|
||||
import com.gh.gamecenter.core.runOnUiThread
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.runOnUiThread
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.provider.IPushProvider
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.personal.reservation.ManageReservationUseCase
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.halo.assistant.fragment.reserve.ReserveReminderRepository
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class MyReservationViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
var positionAndPackageMap = HashMap<String, Int>() // key: packageName + position, value: position
|
||||
|
||||
private val repository = ReserveReminderRepository.newInstance()
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val manageReservationUseCase = ManageReservationUseCase<GameEntity>()
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? {
|
||||
return null
|
||||
@ -68,126 +51,56 @@ class MyReservationViewModel(application: Application) : ListViewModel<GameEntit
|
||||
}
|
||||
|
||||
fun cancelReservation(game: GameEntity) {
|
||||
deleteOrCancelReservation(game, false)
|
||||
}
|
||||
manageReservationUseCase.deleteOrCancelReservation(game, false) {
|
||||
// if (deleteReservation) {
|
||||
//// MtaHelper.onEvent("预约游戏", "取消预约", game.name)
|
||||
// } else {
|
||||
//// MtaHelper.onEvent("预约游戏", "删除预约", game.name)
|
||||
// }
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun deleteOrCancelReservation(game: GameEntity, deleteReservation: Boolean) {
|
||||
val single = if (deleteReservation) {
|
||||
RetrofitManager.getInstance().newApi
|
||||
.deleteGameReservation(game.id, getReserveRequestBody(game, HaloApp.getInstance().application))
|
||||
.subscribeOn(Schedulers.io())
|
||||
} else {
|
||||
RetrofitManager.getInstance().newApi
|
||||
.cancelGameReservation(game.id, getReserveRequestBody(game, HaloApp.getInstance().application))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
val list = mListLiveData.value
|
||||
// 当数据量为1的时候不删了,直接刷新
|
||||
if (list?.size == 1) {
|
||||
runOnUiThread { load(LoadType.REFRESH) }
|
||||
} else {
|
||||
list?.remove(list.find { it.id == game.id })
|
||||
mListLiveData.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
single.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
ReservationRepository.refreshReservations()
|
||||
|
||||
if (deleteReservation) {
|
||||
// MtaHelper.onEvent("预约游戏", "取消预约", game.name)
|
||||
} else {
|
||||
// MtaHelper.onEvent("预约游戏", "删除预约", game.name)
|
||||
}
|
||||
|
||||
val list = mListLiveData.value
|
||||
// 当数据量为1的时候不删了,直接刷新
|
||||
if (list?.size == 1) {
|
||||
runOnUiThread { load(LoadType.REFRESH) }
|
||||
} else {
|
||||
list?.remove(list.find { it.id == game.id })
|
||||
mListLiveData.postValue(list)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
Utils.toast(getApplication(), exception.message)
|
||||
exception.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private val _enableAutoDownloadSuccessfully = MutableLiveData<Event<Boolean>>()
|
||||
val enableAutoDownloadSuccessfully: LiveData<Event<Boolean>> = _enableAutoDownloadSuccessfully
|
||||
val enableAutoDownloadSuccessfully: LiveData<Event<Boolean>> = manageReservationUseCase.enableAutoDownloadSuccessfully
|
||||
fun enableAutoDownload(enable: Boolean, games: Set<GameEntity>) {
|
||||
if (enable) {
|
||||
games.forEach {
|
||||
SensorsBridge.trackOpenAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese)
|
||||
}
|
||||
} else {
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese)
|
||||
}
|
||||
}
|
||||
val params = createParamsInBatch(games).apply {
|
||||
put("wifi_auto_download", enable)
|
||||
}
|
||||
repository.enableAutoDownloadInBatches(params.toRequestBody())
|
||||
.map {
|
||||
val oldList = mResultLiveData.value ?: emptyList()
|
||||
val newList = oldList.map { item ->
|
||||
if (games.any { it.id == item.id }) {
|
||||
item.copy(_wifiAutoDownload = enable)
|
||||
} else {
|
||||
item
|
||||
}
|
||||
|
||||
manageReservationUseCase.enableAutoDownload(enable, games, {
|
||||
val oldList = mResultLiveData.value ?: emptyList()
|
||||
val newList = oldList.map { item ->
|
||||
if (games.any { it.id == item.id }) {
|
||||
item.copy(_wifiAutoDownload = enable)
|
||||
} else {
|
||||
item
|
||||
}
|
||||
newList
|
||||
}
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<GameEntity>>() {
|
||||
override fun onSuccess(data: List<GameEntity>) {
|
||||
mResultLiveData.value = data
|
||||
_enableAutoDownloadSuccessfully.value = Event(enable)
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
newList
|
||||
}) { data ->
|
||||
mResultLiveData.value = data
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelReserveInBatch(games: Set<GameEntity>) {
|
||||
val params = createParamsInBatch(games).apply {
|
||||
put("appointment", false)
|
||||
}
|
||||
repository.cancelReserveInBatch(params.toRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
ReservationRepository.refreshReservations()
|
||||
val oldList = mResultLiveData.value ?: emptyList()
|
||||
val newList = oldList.toMutableList()
|
||||
manageReservationUseCase.cancelReserveInBatch(games) {
|
||||
val oldList = mResultLiveData.value ?: emptyList()
|
||||
val newList = oldList.toMutableList()
|
||||
|
||||
newList.removeAll {
|
||||
games.any { game -> game.id == it.id }
|
||||
}
|
||||
if (newList.isEmpty()) {
|
||||
// 刷新页面
|
||||
runOnUiThread { load(LoadType.REFRESH) }
|
||||
} else {
|
||||
mResultLiveData.postValue(newList)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun createParamsInBatch(games: Set<GameEntity>): HashMap<String, Any> {
|
||||
val jPushId =
|
||||
(TheRouter.get(IPushProvider::class.java)
|
||||
?.getRegistrationId(HaloApp.getInstance().application)) ?: ""
|
||||
val idAndMirrorType = games.map {
|
||||
var mirrorPosition = it.getMirrorPosition()
|
||||
if (mirrorPosition == -1) {
|
||||
mirrorPosition = 0
|
||||
newList.removeAll {
|
||||
games.any { game -> game.id == it.id }
|
||||
}
|
||||
if (newList.isEmpty()) {
|
||||
// 刷新页面
|
||||
runOnUiThread { load(LoadType.REFRESH) }
|
||||
} else {
|
||||
mResultLiveData.postValue(newList)
|
||||
}
|
||||
hashMapOf("_id" to it.id, "mirror_type" to mirrorPosition)
|
||||
}
|
||||
return hashMapOf("jpush_id" to jPushId, "games" to idAndMirrorType)
|
||||
}
|
||||
|
||||
private val _changePageStateAction = MutableLiveData<Event<MyReservationFragment.ReservePageState>>()
|
||||
@ -198,7 +111,7 @@ class MyReservationViewModel(application: Application) : ListViewModel<GameEntit
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
manageReservationUseCase.onCleared()
|
||||
}
|
||||
|
||||
}
|
||||
@ -26,6 +26,10 @@ class DeliveryInfoActivity : ToolBarActivity() {
|
||||
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, DeliveryInfoActivity::class.java, DeliveryInfoFragment::class.java)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context?): Intent? {
|
||||
return getTargetIntent(context, DeliveryInfoActivity::class.java, DeliveryInfoFragment::class.java)
|
||||
|
||||
@ -4,7 +4,10 @@ import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.database.sqlite.SQLiteException
|
||||
import android.graphics.Typeface
|
||||
import android.os.*
|
||||
import android.os.Bundle
|
||||
import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.os.Message
|
||||
import android.text.TextUtils
|
||||
import android.view.MotionEvent
|
||||
import android.view.View
|
||||
@ -14,6 +17,7 @@ import androidx.appcompat.content.res.AppCompatResources
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.PagerSnapHelper
|
||||
@ -210,7 +214,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
linkText = ""
|
||||
)
|
||||
NewLogUtils.logMessageInformBellClick(
|
||||
mStubBinding.loginMessageHint.visibility == View.VISIBLE,
|
||||
mStubBinding.loginMessageHint.isVisible,
|
||||
"我的"
|
||||
)
|
||||
SensorsBridge.trackMessageCenterClick()
|
||||
@ -725,7 +729,7 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
mStubBinding.personalUserName.text = getString(R.string.login_immediately)
|
||||
mStubBinding.userIdTv.text = getString(R.string.login_immediately_hint)
|
||||
|
||||
if (mStubBinding.loginMessageHint.visibility == View.VISIBLE) {
|
||||
if (mStubBinding.loginMessageHint.isVisible) {
|
||||
mStubBinding.loginMessageHint.visibility = View.GONE
|
||||
EventBus.getDefault().post(EBReuse(MESSAGE_READ_OVER))
|
||||
}
|
||||
@ -735,7 +739,6 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
private fun inflateRealView() {
|
||||
mBinding.stub.inflateOrShow()
|
||||
|
||||
mStubBinding.statusBar.goneIf(Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP)
|
||||
mStubBinding.darkModeIv.goneIf(!(Config.getNightModeSetting()?.icon ?: false))
|
||||
mStubBinding.darkModeIv.setImageResource(if (mIsDarkModeOn) R.drawable.ic_personal_light_mode else R.drawable.ic_personal_dark_mode)
|
||||
mStubBinding.loginMessageHint.typeface =
|
||||
@ -755,13 +758,11 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
mStubBinding.historyTv.setOnClickListener(this)
|
||||
mStubBinding.myCollectionTv.setOnClickListener(this)
|
||||
|
||||
val statusBarHeight =
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.KITKAT) 0 else DisplayUtils.getStatusBarHeight(resources)
|
||||
mStubBinding.motionLayout.minimumHeight = statusBarHeight + 48F.dip2px()
|
||||
mStubBinding.motionLayout.minimumHeight = DisplayUtils.getStatusBarHeight(resources) + 48F.dip2px()
|
||||
|
||||
mStubBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _: AppBarLayout?, verticalOffset: Int ->
|
||||
mStubBinding.appbar.addOnOffsetChangedListener { _: AppBarLayout?, verticalOffset: Int ->
|
||||
mStubBinding.listRefresh.isEnabled = abs(verticalOffset) <= 2
|
||||
})
|
||||
}
|
||||
|
||||
mStubBinding.listRefresh.setColorSchemeColors(
|
||||
ContextCompat.getColor(
|
||||
@ -856,13 +857,13 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
root.goneIf(EnvHelper.isGATApp)
|
||||
}
|
||||
mStubBinding.userProtocolItem.run {
|
||||
titleTv.text = getString(R.string.setting_user_protocol)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_user_protocol)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_user_protocol)
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_user_protocol))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_user_protocol))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_user_protocol),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_user_protocol),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -877,15 +878,15 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
}
|
||||
}
|
||||
mStubBinding.privacyPolicyItem.run {
|
||||
titleTv.text = getString(R.string.setting_privacy_policy)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_privacy_policy)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_privacy_policy)
|
||||
//判断隐私政策是否有更新
|
||||
mStubBinding.privacyPolicyItem.redDot.visibility = if (checkPrivacyIsSame()) View.GONE else View.VISIBLE
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_privacy_policy))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_privacy_policy))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_privacy_policy),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_privacy_policy),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -905,13 +906,13 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
}
|
||||
}
|
||||
mStubBinding.infoListItem.run {
|
||||
titleTv.text = getString(R.string.setting_info_list)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_info_list)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_info_list)
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_info_list))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_info_list))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_info_list),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_info_list),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -919,20 +920,20 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
requireContext(),
|
||||
getString(R.string.setting_info_list),
|
||||
requireContext().getString(R.string.info_list_url)
|
||||
getString(com.gh.gamecenter.common.R.string.setting_info_list),
|
||||
requireContext().getString(com.gh.gamecenter.common.R.string.info_list_url)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
mStubBinding.sdkListItem.run {
|
||||
titleTv.text = getString(R.string.setting_sdk_list)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_sdk_list)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_sdk)
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_sdk_list))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_sdk_list))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_sdk_list),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_sdk_list),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -940,20 +941,20 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
requireContext(),
|
||||
getString(R.string.setting_sdk_list),
|
||||
requireContext().getString(R.string.sdk_list_url)
|
||||
getString(com.gh.gamecenter.common.R.string.setting_sdk_list),
|
||||
requireContext().getString(com.gh.gamecenter.common.R.string.sdk_list_url)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
mStubBinding.permissionAndUsageItem.run {
|
||||
titleTv.text = getString(R.string.setting_permission_and_usage)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_permission_and_usage)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_permission)
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_permission_and_usage))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_permission_and_usage))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_permission_and_usage),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_permission_and_usage),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -961,20 +962,20 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
requireContext(),
|
||||
getString(R.string.setting_permission_and_usage),
|
||||
requireContext().getString(R.string.permission_and_usage_url)
|
||||
getString(com.gh.gamecenter.common.R.string.setting_permission_and_usage),
|
||||
requireContext().getString(com.gh.gamecenter.common.R.string.permission_and_usage_url)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
mStubBinding.childrenPolicyItem.run {
|
||||
titleTv.text = getString(R.string.setting_children_policy)
|
||||
titleTv.text = getString(com.gh.gamecenter.common.R.string.setting_children_policy)
|
||||
iconIv.setImageResource(R.drawable.ic_personal_children_policy)
|
||||
root.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(R.string.setting_children_policy))
|
||||
NewFlatLogUtils.logHaloSelfClick("其他功能", getString(com.gh.gamecenter.common.R.string.setting_children_policy))
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
profile = "其他功能",
|
||||
text = getString(R.string.setting_children_policy),
|
||||
text = getString(com.gh.gamecenter.common.R.string.setting_children_policy),
|
||||
linkType = "",
|
||||
linkId = "",
|
||||
linkText = ""
|
||||
@ -982,8 +983,8 @@ class HaloPersonalFragment : BaseLazyFragment() {
|
||||
startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
requireContext(),
|
||||
getString(R.string.setting_children_policy),
|
||||
requireContext().getString(R.string.children_policy_url)
|
||||
getString(com.gh.gamecenter.common.R.string.setting_children_policy),
|
||||
requireContext().getString(com.gh.gamecenter.common.R.string.children_policy_url)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@ -0,0 +1,114 @@
|
||||
package com.gh.gamecenter.personal
|
||||
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.lifecycle.LiveData
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.ethanhua.skeleton.ViewSkeletonScreen
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.databinding.FragmentMyHaloListBaseBinding
|
||||
import com.gh.gamecenter.core.iinterface.IScrollable
|
||||
|
||||
abstract class MyHaloBaseListFragment: LazyFragment(), IScrollable {
|
||||
protected lateinit var binding: FragmentMyHaloListBaseBinding
|
||||
protected var skeleton: ViewSkeletonScreen? = null
|
||||
|
||||
override fun getRealLayoutId(): Int = com.gh.gamecenter.common.R.layout.fragment_my_halo_list_base
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
super.onRealLayoutInflated(inflatedView)
|
||||
binding = FragmentMyHaloListBaseBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
skeleton = Skeleton.bind(binding.skeleton).shimmer(false)
|
||||
.load(R.layout.activity_install_skeleton).show()
|
||||
|
||||
binding.reuseNoConnection.connectionReloadTv.setOnClickListener {
|
||||
onLoadRefresh()
|
||||
}
|
||||
|
||||
provideLoadStatusLiveData()?.observe(viewLifecycleOwner) {
|
||||
when (it) {
|
||||
LoadStatus.INIT_EMPTY -> onLoadEmpty()
|
||||
LoadStatus.INIT_EXCEPTION -> onLoadException()
|
||||
LoadStatus.INIT_FAILED -> onLoadFailed()
|
||||
LoadStatus.INIT_LOADED, LoadStatus.INIT_OVER -> onLoadDone()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
abstract fun provideLoadStatusLiveData(): LiveData<LoadStatus>?
|
||||
|
||||
protected open fun onLoadDone() {
|
||||
skeleton?.hide()
|
||||
binding.reuseNoneData.reuseNoneData.isVisible = false
|
||||
binding.reuseNoConnection.reuseNoConnection.isVisible = false
|
||||
binding.reuseDataException.reuseDataException.isVisible = false
|
||||
binding.reuseGrantGetInstalledApps.reuseGrantGetInstalledApps.isVisible = false
|
||||
binding.listRv.isVisible = true
|
||||
}
|
||||
|
||||
protected open fun onLoadEmpty() {
|
||||
showEmpty()
|
||||
}
|
||||
|
||||
protected open fun showEmpty(isGranted: Boolean = true) {
|
||||
skeleton?.hide()
|
||||
binding.reuseNoneData.reuseNoneData.isVisible = isGranted
|
||||
binding.reuseGrantGetInstalledApps.reuseGrantGetInstalledApps.isVisible = !isGranted
|
||||
binding.reuseNoConnection.reuseNoConnection.isVisible = false
|
||||
binding.reuseDataException.reuseDataException.isVisible = false
|
||||
binding.listRv.isVisible = false
|
||||
}
|
||||
|
||||
open fun onLoadRefresh() {
|
||||
if (!isBindingInitialized()) return
|
||||
|
||||
skeleton?.show()
|
||||
binding.reuseNoneData.reuseNoneData.isVisible = false
|
||||
binding.reuseNoConnection.reuseNoConnection.isVisible = false
|
||||
binding.reuseDataException.reuseDataException.isVisible = false
|
||||
binding.reuseGrantGetInstalledApps.reuseGrantGetInstalledApps.isVisible = false
|
||||
binding.listRv.isVisible = false
|
||||
refresh()
|
||||
}
|
||||
|
||||
protected abstract fun refresh()
|
||||
|
||||
protected open fun onLoadFailed() {
|
||||
skeleton?.hide()
|
||||
binding.reuseNoneData.reuseNoneData.isVisible = false
|
||||
binding.reuseNoConnection.reuseNoConnection.isVisible = true
|
||||
binding.reuseDataException.reuseDataException.isVisible = false
|
||||
binding.reuseGrantGetInstalledApps.reuseGrantGetInstalledApps.isVisible = false
|
||||
binding.listRv.isVisible = false
|
||||
}
|
||||
|
||||
protected open fun onLoadException() {
|
||||
skeleton?.hide()
|
||||
binding.reuseNoneData.reuseNoneData.isVisible = false
|
||||
binding.reuseNoConnection.reuseNoConnection.isVisible = false
|
||||
binding.reuseDataException.reuseDataException.isVisible = true
|
||||
binding.reuseGrantGetInstalledApps.reuseGrantGetInstalledApps.isVisible = false
|
||||
binding.listRv.isVisible = false
|
||||
}
|
||||
|
||||
protected fun isBindingInitialized() = ::binding.isInitialized
|
||||
|
||||
override fun scrollToTop() {
|
||||
if (!isBindingInitialized()) return
|
||||
binding.listRv.scrollToPosition(0)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
if (isBindingInitialized()) {
|
||||
binding.listRv.adapter?.run { notifyItemRangeChanged(0, itemCount) }
|
||||
}
|
||||
}
|
||||
}
|
||||
1251
app/src/main/java/com/gh/gamecenter/personal/MyHaloFragment.kt
Normal file
1251
app/src/main/java/com/gh/gamecenter/personal/MyHaloFragment.kt
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,324 @@
|
||||
package com.gh.gamecenter.personal
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.setPadding
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DataCollectionUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloFunctionBinding
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloFunctionFirstBinding
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloFunctionSecondBinding
|
||||
import com.gh.gamecenter.entity.MyHaloContentCard.Function.FunctionData
|
||||
import com.gh.gamecenter.game.upload.GameSubmissionActivity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.message.MessageUnreadRepository
|
||||
import com.gh.gamecenter.savegame.GameArchiveListActivity
|
||||
import com.gh.gamecenter.setting.SettingBridge
|
||||
import com.gh.gamecenter.simulatorgame.SimulatorGameActivity
|
||||
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
|
||||
import com.gh.gamecenter.toolbox.ToolBoxActivity
|
||||
import com.gh.gamecenter.video.videomanager.VideoManagerActivity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class MyHaloFunctionAdapter(context: Context): BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
private val firstPageList = mutableListOf<FunctionData>()
|
||||
private val secondPageList = mutableListOf<FunctionData>()
|
||||
|
||||
private val secondPageItemWidth = (mContext.resources.displayMetrics.widthPixels - 40F.dip2px()) / 4F
|
||||
private val secondPageItemHeight = 64F.dip2px()
|
||||
|
||||
fun submitData(firstPageDataList: List<FunctionData>, secondPageDataList: List<FunctionData>?) {
|
||||
firstPageList.clear()
|
||||
secondPageList.clear()
|
||||
firstPageList.addAll(firstPageDataList)
|
||||
secondPageDataList?.let { secondPageList.addAll(it) }
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == 0) ITEM_FIRST else ITEM_SECOND
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return if (viewType == ITEM_FIRST) {
|
||||
FirstFunctionViewHolder(parent.toBinding())
|
||||
} else {
|
||||
SecondFunctionViewHolder(parent.toBinding())
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (secondPageList.isEmpty()) 1 else 2
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is FirstFunctionViewHolder) {
|
||||
holder.binding.root.run {
|
||||
if (firstPageList == holder.dataList && holder.itemBindingList.isNotEmpty()) {
|
||||
holder.itemBindingList.forEachIndexed { index, binding ->
|
||||
val entity = firstPageList.getOrNull(index)
|
||||
if (entity != null) {
|
||||
bindFunctionItem(binding, entity)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
holder.itemBindingList.clear()
|
||||
removeAllViews()
|
||||
|
||||
val itemWidth = if (itemCount == 1) {
|
||||
setPadding(4F.dip2px())
|
||||
secondPageItemWidth
|
||||
} else {
|
||||
setPadding(4F.dip2px(), 4F.dip2px(), 0, 4F.dip2px())
|
||||
(context.resources.displayMetrics.widthPixels - 36F.dip2px() - secondPageItemWidth / 2F) / 4F
|
||||
}
|
||||
firstPageList.forEachIndexed { _, entity ->
|
||||
ItemMyHaloFunctionBinding.inflate(layoutInflater).run {
|
||||
bindFunctionItem(this, entity)
|
||||
holder.itemBindingList.add(this)
|
||||
addView(root, itemWidth.toInt(), 64F.dip2px())
|
||||
}
|
||||
}
|
||||
holder.dataList = firstPageList.toMutableList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is SecondFunctionViewHolder) {
|
||||
holder.binding.root.run {
|
||||
val lineCount = (secondPageList.size + SECOND_PAGE_SPAN_COUNT - 1) / SECOND_PAGE_SPAN_COUNT
|
||||
updateLayoutParams<ViewGroup.LayoutParams> { height = lineCount * secondPageItemHeight + 8F.dip2px() }
|
||||
|
||||
if (adapter is SecondFunctionAdapter) {
|
||||
(adapter as SecondFunctionAdapter).let {
|
||||
if (it.currentList == secondPageList) {
|
||||
it.notifyItemRangeChanged(0, it.itemCount)
|
||||
} else {
|
||||
it.submitList(secondPageList.toMutableList())
|
||||
}
|
||||
}
|
||||
} else {
|
||||
layoutManager = GridLayoutManager(mContext, SECOND_PAGE_SPAN_COUNT)
|
||||
adapter = SecondFunctionAdapter().apply {
|
||||
submitList(secondPageList.toMutableList())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class FirstFunctionViewHolder(val binding: ItemMyHaloFunctionFirstBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
var itemBindingList = arrayListOf<ItemMyHaloFunctionBinding>()
|
||||
var dataList: List<FunctionData>? = null
|
||||
}
|
||||
|
||||
class SecondFunctionViewHolder(val binding: ItemMyHaloFunctionSecondBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class SecondFunctionAdapter: ListAdapter<FunctionData, SecondFunctionAdapter.FunctionItemViewHolder>(
|
||||
createDiffItemCallBack()) {
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FunctionItemViewHolder =
|
||||
FunctionItemViewHolder(parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: FunctionItemViewHolder, position: Int) {
|
||||
bindFunctionItem(holder.binding, getItem(position))
|
||||
}
|
||||
|
||||
class FunctionItemViewHolder(val binding: ItemMyHaloFunctionBinding): RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
fun createDiffItemCallBack() = object : ItemCallback<FunctionData>() {
|
||||
override fun areItemsTheSame(oldItem: FunctionData, newItem: FunctionData): Boolean = true
|
||||
override fun areContentsTheSame(oldItem: FunctionData, newItem: FunctionData): Boolean = oldItem == newItem
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val ITEM_FIRST = 0
|
||||
const val ITEM_SECOND = 1
|
||||
const val SECOND_PAGE_SPAN_COUNT = 4
|
||||
|
||||
fun bindFunctionItem(binding: ItemMyHaloFunctionBinding, entity: FunctionData) {
|
||||
binding.run {
|
||||
nameTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context))
|
||||
redDotIv.background = R.drawable.oval_hint_red_bg.toDrawable(root.context)
|
||||
iconIv.hierarchy.setActualImageColorFilter(
|
||||
if (entity.icon.endsWith(".svg")) {
|
||||
PorterDuffColorFilter(
|
||||
com.gh.gamecenter.common.R.color.text_primary.toColor(root.context),
|
||||
PorterDuff.Mode.SRC_ATOP
|
||||
)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
ImageUtils.display(iconIv, entity.icon)
|
||||
nameTv.text = entity.name
|
||||
redDotIv.isVisible = entity.remindSwitch
|
||||
root.setOnClickListener {
|
||||
directPage(root.context, entity)
|
||||
redDotIv.isVisible = false
|
||||
SensorsBridge.trackHaloSelfClick(
|
||||
"功能服务",
|
||||
entity.name,
|
||||
entity.link?.type ?: "",
|
||||
entity.link?.link ?: "",
|
||||
entity.link?.text ?: ""
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun directPage(context: Context, entity: FunctionData) {
|
||||
if (entity.remindSwitch) {
|
||||
val haveReadRecord= SPUtils.getStringSet(Constants.SP_ADDONS_FUNCS_HAVE_READ) as HashSet<String>
|
||||
val newReadRecord = hashSetOf<String>()//这里必须重新创建HashSet对象,否则重启app数据不能保存
|
||||
newReadRecord.addAll(haveReadRecord)
|
||||
newReadRecord.add(entity.id)
|
||||
SPUtils.setStringSet(Constants.SP_ADDONS_FUNCS_HAVE_READ, newReadRecord)
|
||||
MessageUnreadRepository.loadMessageUnreadTotal(true)
|
||||
entity.remindSwitch = false
|
||||
}
|
||||
|
||||
when (entity.link?.type) {
|
||||
"视频投稿" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
if (BuildConfig.DEBUG) {
|
||||
context.startActivity(VideoManagerActivity.getIntent(context, "", "我的光环-视频投稿"))
|
||||
} else {
|
||||
DialogHelper.showDialog(
|
||||
context, "提示",
|
||||
"抱歉,您当前系统版本过低,暂不支持视频功能", "我知道了", ""
|
||||
)
|
||||
}
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-视频投稿") {}
|
||||
}
|
||||
}
|
||||
|
||||
"账号安全" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
context.startActivity(SettingBridge.getSecurityIntent(context, "我的光环-账号安全"))
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-账号安全") {}
|
||||
}
|
||||
}
|
||||
|
||||
"模拟器游戏" -> {
|
||||
context.startActivity(SimulatorGameActivity.getIntent(context))
|
||||
}
|
||||
|
||||
"收货信息" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
context.startActivity(DeliveryInfoActivity.getIntent(context))
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-收货信息") {}
|
||||
}
|
||||
}
|
||||
|
||||
"实名认证" -> {
|
||||
context.startActivity(ShellActivity.getIntent(context, ShellActivity.Type.REAL_NAME_INFO, null))
|
||||
}
|
||||
|
||||
"微信提醒" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
context.startActivity(WebActivity.getBindWechatIntent(context))
|
||||
SensorsBridge.trackEvent(
|
||||
"AppointmenWechatRemindConfigPageShow",
|
||||
"source_entrance",
|
||||
"我的光环-微信提醒"
|
||||
)
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-微信提醒") { }
|
||||
}
|
||||
}
|
||||
|
||||
"游戏动态" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
DataCollectionUtils.uploadClick(context, "游戏动态", "发现")
|
||||
DirectUtils.directToConcernInfo(context, "我的光环-常用功能")
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-游戏动态") { }
|
||||
}
|
||||
}
|
||||
|
||||
"资讯中心" -> {
|
||||
DataCollectionUtils.uploadClick(context, "资讯中心", "发现")
|
||||
context.startActivity(InfoActivity.getIntent(context))
|
||||
}
|
||||
|
||||
"礼包中心" -> {
|
||||
DataCollectionUtils.uploadClick(context, "礼包中心", "发现")
|
||||
DirectUtils.directToGift(context, "(发现:礼包)")
|
||||
}
|
||||
|
||||
"工具箱" -> {
|
||||
DataCollectionUtils.uploadClick(context, "工具箱", "发现")
|
||||
context.startActivity(ToolBoxActivity.getIntent(context, "(发现:工具箱)"))
|
||||
}
|
||||
|
||||
"安装包清理" -> {
|
||||
DataCollectionUtils.uploadClick(context, "安装包清理", "发现")
|
||||
PermissionHelper.checkManageAllFilesOrStoragePermissionBeforeAction(context) {
|
||||
context.startActivity(CleanApkActivity.getIntent(context))
|
||||
}
|
||||
}
|
||||
|
||||
"个人中心" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
context.startActivity(UserInfoActivity.getIntent(context))
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-个人中心") {}
|
||||
}
|
||||
}
|
||||
|
||||
"游戏投稿" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(context) {
|
||||
context.startActivity(
|
||||
GameSubmissionActivity.getIntent(context, "(我的光环)", "")
|
||||
)
|
||||
}
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-游戏投稿") { }
|
||||
}
|
||||
}
|
||||
|
||||
"视频数据" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
DirectUtils.directVideoData(context, "我的光环-视频数据")
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-视频数据") {}
|
||||
}
|
||||
}
|
||||
|
||||
"青少年模式" -> {
|
||||
context.startActivity(TeenagerModeActivity.getIntent(context))
|
||||
}
|
||||
|
||||
"游戏存档" -> {
|
||||
context.startActivity(GameArchiveListActivity.getIntent(context))
|
||||
}
|
||||
|
||||
"开服管理" -> {// 开服管理
|
||||
DirectUtils.directToServersCalendarManagement("我的光环-常用功能")
|
||||
}
|
||||
|
||||
else -> entity.link?.let { DirectUtils.directToLinkPage(context, it, "我的光环", "常用功能") }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
210
app/src/main/java/com/gh/gamecenter/personal/MyHaloViewModel.kt
Normal file
210
app/src/main/java/com/gh/gamecenter/personal/MyHaloViewModel.kt
Normal file
@ -0,0 +1,210 @@
|
||||
package com.gh.gamecenter.personal
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.history.HistoryDatabase
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.entity.LinkEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.entity.AppEntity
|
||||
import com.gh.gamecenter.entity.MyHaloContentCard
|
||||
import com.gh.gamecenter.entity.SignEntity
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.newsdetail.NewsDetailActivity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
class MyHaloViewModel(application: Application) : AndroidViewModel(application) {
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
private val newApi = RetrofitManager.getInstance().newApi
|
||||
val appEntity = MutableLiveData<AppEntity>()
|
||||
val vipCardLiveData = MutableLiveData<MyHaloContentCard.VipCard?>()
|
||||
val functionLiveData = MutableLiveData<MyHaloContentCard.Function?>()
|
||||
val sortTypeLiveData = MutableLiveData<Event<String>>()
|
||||
val managementStateLiveData = MutableLiveData(false)
|
||||
val historyGameCountFlow = HistoryDatabase.instance.gameDao().getGameCount()
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
init {
|
||||
loadData()
|
||||
}
|
||||
|
||||
fun changeManagementState() {
|
||||
val state = managementStateLiveData.value ?: false
|
||||
managementStateLiveData.postValue(!state)
|
||||
}
|
||||
|
||||
fun loadData() {
|
||||
checkUpdate()
|
||||
getContentCard()
|
||||
}
|
||||
|
||||
fun logout() {
|
||||
vipCardLiveData.postValue(null)
|
||||
getContentCard()
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getContentCard() {
|
||||
newApi.myHaloContentCard
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
if (it != null) {
|
||||
vipCardLiveData.postValue(it.vipCard)
|
||||
|
||||
val functionData = it.function?.apply {
|
||||
val haveReadRecord: HashSet<String> =
|
||||
SPUtils.getStringSet(Constants.SP_ADDONS_FUNCS_HAVE_READ) as HashSet<String>
|
||||
data.forEach { functionData ->
|
||||
if (functionData.remindSwitch && haveReadRecord.contains(functionData.id)) {
|
||||
functionData.remindSwitch = false
|
||||
}
|
||||
}
|
||||
}
|
||||
functionLiveData.postValue(functionData)
|
||||
}
|
||||
}, {}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun checkUpdate() {
|
||||
api.getUpdate(
|
||||
PackageUtils.getGhVersionName(),
|
||||
PackageUtils.getGhVersionCode(),
|
||||
HaloApp.getInstance().channel,
|
||||
Build.VERSION.SDK_INT
|
||||
)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<AppEntity>() {
|
||||
override fun onResponse(response: AppEntity?) {
|
||||
super.onResponse(response)
|
||||
response?.let { appEntity.postValue(it) }
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun sign(successCallback: (signEntity: SignEntity) -> Unit) {
|
||||
val context = getApplication<Application>().applicationContext
|
||||
api.postSign(UserManager.getInstance().userId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<SignEntity>() {
|
||||
override fun onResponse(signEntity: SignEntity?) {
|
||||
if (signEntity != null) {
|
||||
successCallback.invoke(signEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
if (e == null || e.code() != 401) {
|
||||
ToastUtils.toast(context.getString(com.gh.gamecenter.common.R.string.loading_network_error))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun signSkip(context: Context, signEntity: SignEntity) {
|
||||
val data = signEntity.data
|
||||
val entrance = "(我的光环)+(签到)"
|
||||
if (data == null || TextUtils.isEmpty(data.type)) {
|
||||
DirectUtils.directToHomeDefaultTab(context)
|
||||
return
|
||||
}
|
||||
when (data.type) {
|
||||
"game" -> {
|
||||
GameDetailActivity.startGameDetailActivity(context, data.link, entrance)
|
||||
}
|
||||
|
||||
"news" -> {
|
||||
context.startActivity(NewsDetailActivity.getIntentById(context, data.link, entrance))
|
||||
}
|
||||
|
||||
"column" -> {
|
||||
SubjectActivity.startSubjectActivity(
|
||||
context,
|
||||
data.link,
|
||||
null,
|
||||
false,
|
||||
null,
|
||||
entrance
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
val linkEntity = LinkEntity()
|
||||
linkEntity.type = data.type
|
||||
linkEntity.link = data.link
|
||||
linkEntity.text = data.text
|
||||
linkEntity.community = data.community
|
||||
linkEntity.display = data.display
|
||||
DirectUtils.directToLinkPage(context, linkEntity, entrance, "")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun isCanSign(time: Long): Boolean {
|
||||
val context = getApplication<Application>().applicationContext
|
||||
val formatDay = SimpleDateFormat("dd", Locale.CHINA)
|
||||
val lastSignTime = time * 1000
|
||||
val curTime = Utils.getTime(context) * 1000
|
||||
val lastSignDay = formatDay.format(lastSignTime).toInt()
|
||||
val curDay = formatDay.format(curTime).toInt()
|
||||
return lastSignDay != curDay || curTime - lastSignTime > 24 * 60 * 60 * 1000
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun applyOrReceiveBadge(
|
||||
id: String,
|
||||
successCallback: (data: ResponseBody) -> Unit,
|
||||
failureCallback: () -> Unit
|
||||
) {
|
||||
api.applyOrReceiveBadge(id)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
successCallback.invoke(data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: java.lang.Exception) {
|
||||
super.onFailure(exception)
|
||||
failureCallback.invoke()
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
fun getLoginEntranceByType(loginTag: String): String = when (loginTag) {
|
||||
"qq" -> "我的光环-QQ"
|
||||
"wechat" -> "我的光环-微信"
|
||||
"weibo" -> "我的光环-新浪微博"
|
||||
else -> ""
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,572 @@
|
||||
package com.gh.gamecenter.personal.installed
|
||||
|
||||
import android.content.res.ColorStateList
|
||||
import android.graphics.PorterDuff
|
||||
import android.graphics.PorterDuffColorFilter
|
||||
import android.graphics.Typeface
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.widget.LinearLayout
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.facebook.imagepipeline.common.ImageDecodeOptions
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.image.CustomSvgImageDecoder
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.utils.NumberUtils
|
||||
import com.gh.gamecenter.databinding.*
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.feature.entity.AcctGameInfo
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.MyHaloGameTag
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
|
||||
import com.gh.gamecenter.feature.view.DownloadButton
|
||||
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_EMPTY
|
||||
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.DISTRICT_SERVER_HAVA
|
||||
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SCENE_TYPE_NO_GUIDE_LAYER
|
||||
import com.gh.gamecenter.gamedetail.accelerator.GameDetailAcceleratorUiHelper.Companion.SOURCE_ENTRANCE_MY_HALO_INSTALLED
|
||||
import com.gh.gamecenter.gamedetail.accelerator.dialog.AcceleratorZoneDialogFragment
|
||||
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
|
||||
import com.gh.gamecenter.search.SearchGameResultAdapter.Companion.startAccelerating
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.therouter.TheRouter
|
||||
|
||||
class InstalledAdapter: ListAdapter<InstalledGameItemData, RecyclerView.ViewHolder>(createDiffCallback()) {
|
||||
private val positionAndPackageMap = HashMap<String, Int>()
|
||||
private var isShowUpdate = false
|
||||
|
||||
override fun submitList(list: List<InstalledGameItemData>?) {
|
||||
positionAndPackageMap.clear()
|
||||
// 记录游戏位置
|
||||
list?.forEachIndexed { i, item ->
|
||||
if (i == 0) {
|
||||
isShowUpdate = item.isHead
|
||||
}
|
||||
item.gameEntity?.let { gameEntity ->
|
||||
val key = buildString {
|
||||
append(gameEntity.id)
|
||||
gameEntity.getApk().forEach { apk ->
|
||||
append(apk.packageName)
|
||||
}
|
||||
append(i)
|
||||
}
|
||||
positionAndPackageMap[key] = i
|
||||
}
|
||||
}
|
||||
|
||||
super.submitList(list)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val itemData = currentList.getOrNull(position) ?: return ITEM_NORMAL
|
||||
return if (itemData.isHead) ITEM_HEAD else if (itemData.isFooter) ITEM_FOOTER else ITEM_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
when (viewType) {
|
||||
ITEM_HEAD -> {
|
||||
InstalledGameUpdateItemViewHolder(parent.toBinding())
|
||||
}
|
||||
ITEM_FOOTER -> {
|
||||
FooterViewHolder(parent.layoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
else -> {
|
||||
InstalledGameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val itemData = currentList.getOrNull(position) ?: return
|
||||
if (holder is InstalledGameUpdateItemViewHolder) {
|
||||
holder.binding.run {
|
||||
root.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(root.context))
|
||||
container.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
updateDataTv.typeface = Typeface.createFromAsset(root.context.assets, Constants.DIN_FONT_PATH)
|
||||
updateDataTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(root.context))
|
||||
updateTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context))
|
||||
arrowIv.setImageResource(R.drawable.ic_auxiliary_arrow_right_8)
|
||||
updateDataTv.text = itemData.updatableGameCount.toString()
|
||||
|
||||
val gameIconList = listOf(gameIcon1, gameIcon2, gameIcon3)
|
||||
gameIconList.forEach {
|
||||
it.setBorderColor(com.gh.gamecenter.common.R.color.ui_surface)
|
||||
}
|
||||
itemData.updatableGameList?.forEachIndexed { index, game ->
|
||||
gameIconList[index].displayGameIcon(game)
|
||||
}
|
||||
itemData.updatableGameList?.size?.let {
|
||||
gameIcon1.goneIf(it == 0)
|
||||
gameIcon2.goneIf(it < 2)
|
||||
gameIcon3.goneIf(it < 3)
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
DirectUtils.directToDownloadManagerUpdate(root.context)
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(false, profile = "可更新入口", text = "可更新入口")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is InstalledGameItemViewHolder) {
|
||||
val gameEntity = itemData.gameEntity ?: return
|
||||
val isMultiVersion = gameEntity.getApk().size > 1
|
||||
holder.binding.run {
|
||||
root.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(root.context))
|
||||
myHaloGame.run {
|
||||
selectIv.isVisible = false
|
||||
root.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
gameInfo.run {
|
||||
wifiAutoDownloadTv.isVisible = false
|
||||
tagTv.isVisible = false
|
||||
gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(root.context))
|
||||
val provider = TheRouter.get(IBindingAdaptersProvider::class.java)
|
||||
provider?.setGameName(gameNameTv, gameEntity, false)
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
|
||||
downloadBtn.goneIf(isMultiVersion)
|
||||
|
||||
if (isMultiVersion) {
|
||||
gameDesTv.text = "${gameEntity.getApk().size} 个版本"
|
||||
} else if (gameEntity.getApk().size == 1) {
|
||||
var gameDesText = gameEntity.getApk().first().getPlatformName()
|
||||
if (gameEntity.playedTime != 0L) {
|
||||
gameDesText += " 已玩${NumberUtils.transSimpleUsageTime(gameEntity.playedTime)}"
|
||||
}
|
||||
gameDesTv.text = gameDesText
|
||||
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
root.context,
|
||||
downloadBtn,
|
||||
gameEntity,
|
||||
holder.bindingAdapterPosition,
|
||||
this@InstalledAdapter,
|
||||
"(我的光环-已安装)",
|
||||
location = "我的光环-已安装" + ":" + gameEntity.name,
|
||||
traceEvent = gameEntity.exposureEvent,
|
||||
clickCallback = {
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
false,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
if (isShowUpdate) holder.bindingAdapterPosition else holder.bindingAdapterPosition + 1,
|
||||
profile = "按钮",
|
||||
text = downloadBtn.text
|
||||
)
|
||||
}
|
||||
)
|
||||
DownloadItemUtils.updateItem(root.context, gameEntity, GameViewHolder(root).apply {
|
||||
gameDownloadBtn = downloadBtn
|
||||
}, listener = object : DownloadButton.OnUpdateListener {
|
||||
override fun completion(text: String) {
|
||||
// 如果下载按钮状态为启动,则需要判断是否需要显示加速按钮
|
||||
showSpeedButton(
|
||||
text,
|
||||
gameEntity,
|
||||
gameInfo,
|
||||
if (isShowUpdate) holder.bindingAdapterPosition else holder.bindingAdapterPosition + 1
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
multiVersionContainer.goneIf(!isMultiVersion) {
|
||||
multiVersionContainer.run {
|
||||
removeAllViews()
|
||||
gameEntity.getApk().forEach { apkEntity ->
|
||||
ItemInstalledGameMultiVersionBinding.inflate(layoutInflater).apply {
|
||||
divider.setBackgroundColor(gameEntity.iconThemeColor.hexStringToIntColor().modifyHSV(s = 0.5F, v = 0.5F))
|
||||
platformNameTv.text = apkEntity.getPlatformName()
|
||||
var gameDesText = "V${PackageUtils.getVersionNameByPackageName(apkEntity.packageName)}"
|
||||
if (gameEntity.playedTime != 0L) {
|
||||
gameDesText += " 已玩${NumberUtils.transSimpleUsageTime(apkEntity.playedTime)}"
|
||||
}
|
||||
desTv.text = gameDesText
|
||||
|
||||
val downloadGameEntity = gameEntity.copy()
|
||||
downloadGameEntity.setApk(gameEntity.getApk().filter { it.packageName == apkEntity.packageName }.toArrayList())
|
||||
downloadGameEntity.dropOtherApk()
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
root.context,
|
||||
downloadBtn,
|
||||
downloadGameEntity,
|
||||
holder.bindingAdapterPosition,
|
||||
this@InstalledAdapter,
|
||||
"(我的光环-已安装)",
|
||||
location = "我的光环-已安装" + ":" + gameEntity.name,
|
||||
traceEvent = gameEntity.exposureEvent
|
||||
)
|
||||
DownloadItemUtils.updateItem(root.context, downloadGameEntity, GameViewHolder(root).apply {
|
||||
gameDownloadBtn = downloadBtn
|
||||
})
|
||||
addView(root, LinearLayout.LayoutParams.MATCH_PARENT, 48F.dip2px())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
bindAnnouncement(TYPE_INSTALLED, this, gameEntity, if (isShowUpdate) holder.bindingAdapterPosition else holder.bindingAdapterPosition + 1)
|
||||
bindService(TYPE_INSTALLED, this, gameEntity, if (isShowUpdate) holder.bindingAdapterPosition else holder.bindingAdapterPosition + 1, "我的光环-已安装")
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
root.context,
|
||||
gameEntity.id,
|
||||
"(我的光环:我的游戏)",
|
||||
gameEntity.exposureEvent
|
||||
)
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
false,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
if (isShowUpdate) holder.bindingAdapterPosition else holder.bindingAdapterPosition + 1,
|
||||
profile = "游戏",
|
||||
text = "游戏"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is FooterViewHolder) {
|
||||
holder.initFooterViewHolder(false, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSpeedButton(
|
||||
text: String,
|
||||
gameEntity: GameEntity,
|
||||
binding: LayoutMyHaloGameInfoBinding,
|
||||
position: Int
|
||||
) {
|
||||
if (text == com.gh.gamecenter.feature.R.string.launch.toResString() && !gameEntity.isVGamePreferred() && gameEntity.canSpeed) {
|
||||
val context = binding.root.context
|
||||
binding.tvSpeed.goneIf(false)
|
||||
binding.downloadBtn.goneIf(true)
|
||||
|
||||
val serviceArea = gameEntity.serviceArea
|
||||
val hasMutualityZone = serviceArea.size > 1
|
||||
|
||||
val lastAcctGame = gameEntity.lastAcctGame
|
||||
val isCurrentGameAccelerating = AcceleratorDataHolder.instance.isCurrentGameAccelerating(gameEntity.id)
|
||||
|
||||
if (isCurrentGameAccelerating) {
|
||||
binding.arrowIv.goneIf(true)
|
||||
binding.zoneDivider.goneIf(true)
|
||||
binding.tvSpeed.updateLayoutParams<LayoutParams> { width = LayoutParams.WRAP_CONTENT }
|
||||
binding.tvSpeed.setPadding(12F.dip2px(), 0, 12F.dip2px(), 0)
|
||||
binding.tvSpeed.text = R.string.accelerating.toResString()
|
||||
binding.tvSpeed.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_light_fill_blue)
|
||||
binding.tvSpeed.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
} else {
|
||||
binding.arrowIv.goneIf(!hasMutualityZone)
|
||||
binding.zoneDivider.goneIf(!hasMutualityZone)
|
||||
binding.tvSpeed.updateLayoutParams<LayoutParams> { width = if (hasMutualityZone) 93F.dip2px() else LayoutParams.WRAP_CONTENT }
|
||||
binding.tvSpeed.setPadding(12F.dip2px(), 0, if (hasMutualityZone) 32F.dip2px() else 12F.dip2px(), 0)
|
||||
binding.tvSpeed.setBackgroundResource(com.gh.gamecenter.common.R.drawable.bg_common_button_fill_gradient_blue)
|
||||
binding.tvSpeed.text = lastAcctGame?.zoneName ?: R.string.network_acceleration.toResString()
|
||||
binding.tvSpeed.setTextColor(com.gh.gamecenter.common.R.color.text_aw_primary.toColor(context))
|
||||
}
|
||||
|
||||
binding.tvSpeed.setOnClickListener {
|
||||
val districtServer = when {
|
||||
hasMutualityZone && lastAcctGame != null -> lastAcctGame.zoneName
|
||||
hasMutualityZone -> DISTRICT_SERVER_EMPTY
|
||||
else -> DISTRICT_SERVER_HAVA
|
||||
}
|
||||
SensorsBridge.trackNetworkAccelerationButtonClick(
|
||||
gameEntity.getUniquePackageName() ?: "",
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
AcceleratorDataHolder.instance.memberType,
|
||||
districtServer,
|
||||
SCENE_TYPE_NO_GUIDE_LAYER,
|
||||
SOURCE_ENTRANCE_MY_HALO_INSTALLED
|
||||
)
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
false,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
position,
|
||||
profile = "按钮",
|
||||
text = binding.tvSpeed.text.toString()
|
||||
)
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
when {
|
||||
!hasMutualityZone -> {
|
||||
// 单区服,直接启动
|
||||
val zoneInfo = gameEntity.serviceArea.firstOrNull() ?: AcctGameInfo.ZoneInfo(0)
|
||||
startAccelerating(gameEntity, context, zoneInfo, false, SOURCE_ENTRANCE_MY_HALO_INSTALLED)
|
||||
}
|
||||
|
||||
lastAcctGame != null -> {
|
||||
// 多区服,有缓存的加速记录
|
||||
startAccelerating(gameEntity, context, lastAcctGame.zoneInfo, true, SOURCE_ENTRANCE_MY_HALO_INSTALLED)
|
||||
}
|
||||
|
||||
else -> {
|
||||
// 多区服,没有缓存的加速记录
|
||||
AcceleratorZoneDialogFragment.show(
|
||||
context,
|
||||
null,
|
||||
gameEntity.serviceArea.toArrayList(),
|
||||
gameEntity,
|
||||
SOURCE_ENTRANCE_MY_HALO_INSTALLED
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, null, true, "") {}
|
||||
}
|
||||
}
|
||||
binding.tvSpeed.isClickable = !isCurrentGameAccelerating
|
||||
|
||||
binding.arrowIv.setOnClickListener {
|
||||
context.ifLogin("[网络加速]") {
|
||||
AcceleratorZoneDialogFragment.show(
|
||||
context,
|
||||
lastAcctGame?.zoneInfo?.id,
|
||||
gameEntity.serviceArea.toArrayList(),
|
||||
gameEntity,
|
||||
SOURCE_ENTRANCE_MY_HALO_INSTALLED
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.tvSpeed.goneIf(true)
|
||||
binding.arrowIv.goneIf(true)
|
||||
binding.zoneDivider.goneIf(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemByDownload(download: DownloadEntity) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(download.packageName) && key.contains(download.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && position < currentList.size) {
|
||||
currentList[position].gameEntity?.getEntryMap()?.set(download.platform, download)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyByDownloadRemoved(status: EBDownloadStatus) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(status.packageName)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && position < currentList.size) {
|
||||
currentList[position].gameEntity?.getEntryMap()?.remove(status.platform)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InstalledGameUpdateItemViewHolder(val binding: ItemInstalledGameUpdateBinding): RecyclerView.ViewHolder(binding.root)
|
||||
class InstalledGameItemViewHolder(val binding: ItemInstalledGameBinding): RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
const val ITEM_HEAD = 0
|
||||
const val ITEM_NORMAL = 1
|
||||
const val ITEM_FOOTER = 2
|
||||
|
||||
const val TYPE_INSTALLED = "installed"
|
||||
const val TYPE_V_GAME = "smooth"
|
||||
const val TYPE_RESERVATION = "reservation"
|
||||
|
||||
fun createDiffCallback() = object : ItemCallback<InstalledGameItemData>() {
|
||||
override fun areItemsTheSame(oldItem: InstalledGameItemData, newItem: InstalledGameItemData): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: InstalledGameItemData, newItem: InstalledGameItemData): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
|
||||
fun bindAnnouncement(type: String, binding: ItemMyHaloGameBinding, gameEntity: GameEntity, position: Int) {
|
||||
binding.run {
|
||||
announcementContainer.goneIf(gameEntity.announcementTag.isEmpty()) {
|
||||
announcementContainer.removeAllViews()
|
||||
|
||||
val themeColor = gameEntity.iconThemeColor.hexStringToIntColor().modifyHSV(s = 0.5F, v = 0.5F)
|
||||
val isDarkModeOn = DarkModeUtils.isDarkModeOn(root.context)
|
||||
announcementContainer.setRoundedColorBackgroundByColorInt(
|
||||
if (isDarkModeOn) com.gh.gamecenter.common.R.color.ui_container_1.toColor(root.context) else themeColor, 6F, if (isDarkModeOn) 1F else 0.05F
|
||||
)
|
||||
gameEntity.announcementTag.forEach {
|
||||
ItemMyHaloGameAnnouncementBinding.inflate(root.layoutInflater).apply {
|
||||
val decodeOptions = ImageDecodeOptions.newBuilder()
|
||||
.setCustomImageDecoder(CustomSvgImageDecoder(themeColor))
|
||||
.build()
|
||||
if (it.icon.isEmpty() && it.defaultAnnouncementIconPath != null) {
|
||||
ImageUtils.displayFromAssets(iconIv, it.defaultAnnouncementIconPath!!, decodeOptions)
|
||||
} else {
|
||||
ImageUtils.display(iconIv, it.icon, customImageDecodeOptions = if (it.icon.endsWith(".svg")) decodeOptions else null)
|
||||
}
|
||||
titleTv.setTextColor(if (isDarkModeOn) com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context) else themeColor)
|
||||
titleTv.text = it.title
|
||||
arrowIv.alpha = if (isDarkModeOn) 1F else 0.25F
|
||||
arrowIv.imageTintList = ColorStateList.valueOf(if (isDarkModeOn) com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context) else themeColor)
|
||||
root.setOnClickListener { _ ->
|
||||
it.link?.let { link ->
|
||||
DirectUtils.directToLinkPage(root.context, link, "", "")
|
||||
if (type == TYPE_RESERVATION) {
|
||||
SensorsBridge.trackHaloSelfGameAppointmentClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
"游戏公告",
|
||||
it.title,
|
||||
link.link ?: "",
|
||||
link.text ?: "",
|
||||
link.type ?: "",
|
||||
)
|
||||
} else {
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
type == TYPE_V_GAME,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
position,
|
||||
"游戏公告",
|
||||
it.title,
|
||||
link.link ?: "",
|
||||
link.text ?: "",
|
||||
link.type ?: "",
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
announcementContainer.addView(root, LinearLayout.LayoutParams.MATCH_PARENT, 28F.dip2px())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bindService(type: String, binding: ItemMyHaloGameBinding, gameEntity: GameEntity, position: Int, entrance: String) {
|
||||
binding.run {
|
||||
serviceContainer.goneIf(gameEntity.serviceTag.isEmpty()) {
|
||||
serviceContainer.removeAllViews()
|
||||
serviceContainer.dividerDrawable = R.drawable.divider_installed_game_service.toDrawable(root.context)
|
||||
|
||||
gameEntity.serviceTag.forEach {
|
||||
ItemMyHaloGameServiceBinding.inflate(root.layoutInflater).apply {
|
||||
val textSecondaryColorFilter = PorterDuffColorFilter(com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context), PorterDuff.Mode.SRC_ATOP)
|
||||
if (it.icon.isEmpty() && it.defaultServiceIcon != null) {
|
||||
iconIv.hierarchy.setPlaceholderImage(it.defaultServiceIcon!!.toDrawable(root.context)?.apply {
|
||||
colorFilter = textSecondaryColorFilter
|
||||
})
|
||||
} else {
|
||||
iconIv.hierarchy.setActualImageColorFilter(if (it.icon.endsWith(".svg")) textSecondaryColorFilter else null)
|
||||
iconIv.hierarchy.setPlaceholderImage(com.gh.gamecenter.common.R.drawable.occupy)
|
||||
ImageUtils.display(iconIv, it.icon)
|
||||
}
|
||||
nameTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context))
|
||||
nameTv.text = it.title
|
||||
root.setOnClickListener { _ ->
|
||||
if (it.link != null) {
|
||||
DirectUtils.directToLinkPage(root.context, it.link!!, entrance, "")
|
||||
|
||||
if (type == TYPE_RESERVATION) {
|
||||
SensorsBridge.trackHaloSelfGameAppointmentClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
"服务标签",
|
||||
it.title,
|
||||
it.link?.link ?: "",
|
||||
it.link?.text ?: "",
|
||||
it.link?.type ?: "",
|
||||
)
|
||||
} else {
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
type == TYPE_V_GAME,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
position,
|
||||
"服务标签",
|
||||
it.title,
|
||||
it.link?.link ?: "",
|
||||
it.link?.text ?: "",
|
||||
it.link?.type ?: "",
|
||||
)
|
||||
}
|
||||
} else {
|
||||
when (it.type) {
|
||||
MyHaloGameTag.TYPE_ARCHIVE -> {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
binding.root.context,
|
||||
gameEntity,
|
||||
entrance,
|
||||
defaultTab = GameDetailTabEntity.TYPE_ARCHIVE
|
||||
)
|
||||
}
|
||||
MyHaloGameTag.TYPE_BBS -> {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
binding.root.context,
|
||||
gameEntity,
|
||||
entrance,
|
||||
defaultTab = GameDetailTabEntity.TYPE_BBS
|
||||
)
|
||||
}
|
||||
MyHaloGameTag.TYPE_ZONE -> {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
binding.root.context,
|
||||
gameEntity,
|
||||
entrance,
|
||||
defaultTab = GameDetailTabEntity.TYPE_ZONE
|
||||
)
|
||||
}
|
||||
MyHaloGameTag.TYPE_GIFT -> {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
binding.root.context,
|
||||
gameEntity,
|
||||
entrance,
|
||||
defaultTab = GameDetailTabEntity.TYPE_GIFT,
|
||||
scrollToLibao = true
|
||||
)
|
||||
}
|
||||
MyHaloGameTag.TYPE_SERVER -> {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
binding.root.context,
|
||||
gameEntity,
|
||||
entrance,
|
||||
scrollToServer = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
serviceContainer.addView(
|
||||
root,
|
||||
LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
).apply {
|
||||
weight = 1F
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,196 @@
|
||||
package com.gh.gamecenter.personal.installed
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.gh.common.iinterface.IInstalledSortType
|
||||
import com.gh.common.iinterface.InstalledSortType
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.PackageHelper
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse
|
||||
import com.gh.gamecenter.common.utils.PermissionHelper
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.isSimulatorGame
|
||||
import com.gh.gamecenter.download.UpdatableGameViewModel
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.GameInstall
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.packagehelper.PackageViewModel
|
||||
import com.gh.gamecenter.personal.MyHaloBaseListFragment
|
||||
import com.gh.gamecenter.personal.MyHaloViewModel
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class InstalledFragment: MyHaloBaseListFragment(), IInstalledSortType {
|
||||
private val viewModel by viewModels<InstalledViewModel>()
|
||||
private val myHaloViewModel by activityViewModels<MyHaloViewModel>()
|
||||
private val packageViewModel by viewModels<PackageViewModel> { PackageViewModel.Factory() }
|
||||
private val updatableGameViewModel by viewModels<UpdatableGameViewModel> { UpdatableGameViewModel.Factory("", mEntrance) }
|
||||
private val installedAdapter by lazy { InstalledAdapter() }
|
||||
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
if (downloadEntity.status == DownloadStatus.done && downloadEntity.isSimulatorGame()) {
|
||||
DownloadManager.getInstance().getEntryMap(downloadEntity.name)?.let {
|
||||
it[downloadEntity.platform] = downloadEntity
|
||||
}
|
||||
packageViewModel.getGameInstalledLiveData().value?.let {
|
||||
viewModel.initData(
|
||||
PackagesManager.filterSameApk(
|
||||
PackagesManager.filterDownloadBlackPackage(
|
||||
it as MutableList<GameInstall>?
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
installedAdapter.notifyItemByDownload(downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
if (packageViewModel.getGameUpdateIncludeCurrentVersion().value.isNullOrEmpty()) {
|
||||
updatableGameViewModel.setUpdatableList(arrayListOf())
|
||||
}
|
||||
observeData()
|
||||
|
||||
binding.run {
|
||||
binding.reuseNoneData.reuseResetLoadTv.updateLayoutParams<MarginLayoutParams> { width = 150F.dip2px() }
|
||||
binding.reuseNoneData.reuseResetLoadTv.visibility = View.VISIBLE
|
||||
binding.reuseNoneData.reuseNoneDataTv.text = getString(com.gh.gamecenter.common.R.string.game_no_data)
|
||||
binding.reuseNoneData.reuseNoneDataDescTv.visibility = View.VISIBLE
|
||||
binding.reuseNoneData.reuseNoneDataDescTv.text = getString(com.gh.gamecenter.common.R.string.game_no_data_desc)
|
||||
binding.reuseNoneData.reuseResetLoadTv.text = "去首页看看"
|
||||
binding.reuseNoneData.reuseResetLoadTv.setOnClickListener {
|
||||
DirectUtils.directToHomeDefaultTab(requireContext())
|
||||
}
|
||||
|
||||
listRv.run {
|
||||
itemAnimator = null
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = installedAdapter
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideLoadStatusLiveData(): LiveData<LoadStatus> = viewModel.loadStatusLiveData
|
||||
|
||||
override fun onFragmentResume() {
|
||||
super.onFragmentResume()
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
myHaloViewModel.sortTypeLiveData.postValue(Event(getCurrentSortType().des))
|
||||
}
|
||||
|
||||
override fun onFragmentPause() {
|
||||
super.onFragmentPause()
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
private fun observeData() {
|
||||
packageViewModel.getGameInstalledLiveData().observe(viewLifecycleOwner) { gameInstalls ->
|
||||
viewModel.initData(
|
||||
PackagesManager.filterSameApk(
|
||||
PackagesManager.filterDownloadBlackPackage(gameInstalls as MutableList<GameInstall>?)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
packageViewModel.getGameUpdateIncludeCurrentVersion().observe(viewLifecycleOwner) { updatableList ->
|
||||
updatableGameViewModel.setUpdatableList(updatableList)
|
||||
}
|
||||
|
||||
updatableGameViewModel.validGameUpdateData.observe(viewLifecycleOwner) {
|
||||
viewModel.updateGameUpdateData(it)
|
||||
}
|
||||
|
||||
viewModel.itemDataListLiveData.observe(viewLifecycleOwner) {
|
||||
installedAdapter.submitList(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
updateNoDataView()
|
||||
}
|
||||
|
||||
private fun updateNoDataView() {
|
||||
binding.run {
|
||||
val isGetInstalledListDisagreed = PackageHelper.isGetInstalledPackagesAgreedRequired()
|
||||
&& !PackageHelper.isGetInstalledPackagesAgreed()
|
||||
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
|
||||
|
||||
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
|
||||
showEmpty(false)
|
||||
reuseGrantGetInstalledApps.grantTv.setOnClickListener {
|
||||
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
|
||||
if (isGranted) {
|
||||
updateNoDataView()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
showEmpty(true)
|
||||
reuseNoneData.reuseResetLoadTv.setOnClickListener {
|
||||
DirectUtils.directToHomeDefaultTab(requireContext())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getCurrentSortType(): InstalledSortType = viewModel.sortedType
|
||||
|
||||
override fun changeSortType(sortType: InstalledSortType) {
|
||||
viewModel.sortedType = sortType
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
packageViewModel.getGameInstalledLiveData().value?.let {
|
||||
viewModel.initData(PackagesManager.filterSameApk(
|
||||
PackagesManager.filterDownloadBlackPackage(it as MutableList<GameInstall>?)
|
||||
))
|
||||
}
|
||||
packageViewModel.getGameUpdateIncludeCurrentVersion().value?.let {
|
||||
updatableGameViewModel.setUpdatableList(it)
|
||||
}
|
||||
}
|
||||
|
||||
// 打开下载按钮事件
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(reuse: EBReuse) {
|
||||
if (("Refresh" == reuse.type || "PlatformChanged" == reuse.type)) {
|
||||
installedAdapter.notifyItemRangeChanged(0, installedAdapter.itemCount)
|
||||
}
|
||||
}
|
||||
|
||||
//下载被删除事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(status: EBDownloadStatus) {
|
||||
if ("delete" == status.status) {
|
||||
DownloadManager.getInstance()
|
||||
.removePlatform(status.name, status.platform)
|
||||
installedAdapter.notifyByDownloadRemoved(status)
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(packageEb: EBPackage) {
|
||||
if (!packageEb.fromInit && packageEb.isInstalledOrUninstalled()) {
|
||||
onLoadRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,20 @@
|
||||
package com.gh.gamecenter.personal.installed
|
||||
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
data class InstalledGameItemData(
|
||||
var gameEntity: GameEntity? = null,
|
||||
var updatableGameCount: Int = 0,
|
||||
var updatableGameList: List<GameEntity>? = null,
|
||||
var isHead: Boolean = false,
|
||||
var isFooter: Boolean = false
|
||||
) {
|
||||
private val isGame
|
||||
get() = gameEntity != null
|
||||
|
||||
fun areItemsTheSame(other: InstalledGameItemData) =
|
||||
isHead == other.isHead && isFooter == other.isFooter && isGame == other.isGame
|
||||
|
||||
fun areContentsTheSame(other: InstalledGameItemData) =
|
||||
updatableGameList == other.updatableGameList && gameEntity == other.gameEntity
|
||||
}
|
||||
@ -0,0 +1,234 @@
|
||||
package com.gh.gamecenter.personal.installed
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.text.TextUtils
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.filter.RegionSettingHelper.shouldThisGameBeFiltered
|
||||
import com.gh.common.iinterface.InstalledSortType
|
||||
import com.gh.common.util.GameUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper.getGameId
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.GameInstall
|
||||
import com.gh.gamecenter.feature.utils.ApkActiveUtils
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.disposables.Disposable
|
||||
|
||||
class InstalledViewModel(application: Application): AndroidViewModel(application) {
|
||||
private val newApi = RetrofitManager.getInstance().newApi
|
||||
private var packageUpdateList: List<GameUpdateEntity>? = null
|
||||
var sortedType = InstalledSortType.valueOf(
|
||||
SPUtils.getString(
|
||||
Constants.SP_MY_HALO_INSTALLED_ORDER,
|
||||
InstalledSortType.RECENTLY_PLAYED.name
|
||||
) ?: InstalledSortType.RECENTLY_PLAYED.name
|
||||
)
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
changeSortedType()
|
||||
SPUtils.setString(Constants.SP_MY_HALO_INSTALLED_ORDER, value.name)
|
||||
}
|
||||
}
|
||||
|
||||
private var disposable: Disposable? = null
|
||||
val itemDataListLiveData = MutableLiveData<List<InstalledGameItemData>>()
|
||||
val loadStatusLiveData = MutableLiveData<LoadStatus>()
|
||||
private val allGameInstallList: ArrayList<GameInstall> = ArrayList()
|
||||
private val sortedItemDataList = ArrayList<InstalledGameItemData>()
|
||||
|
||||
private fun changeSortedType() {
|
||||
if (sortedItemDataList.isNotEmpty()) {
|
||||
when (sortedType) {
|
||||
InstalledSortType.RECENTLY_PLAYED -> {
|
||||
sortedItemDataList.forEach { it.gameEntity?.updateRecentlyPlayedTime() }
|
||||
sortedItemDataList.sortByDescending { it.gameEntity?.recentlyPlayedTime }
|
||||
}
|
||||
InstalledSortType.LATEST_UPDATED -> sortedItemDataList.sortByDescending { it.gameEntity?.updateTime }
|
||||
InstalledSortType.MOST_PLAYED -> sortedItemDataList.sortWith(compareByDescending<InstalledGameItemData> { it.gameEntity?.playedTime }.thenByDescending { it.gameEntity?.packageInstallTime })
|
||||
InstalledSortType.LEAST_PLAYED -> sortedItemDataList.sortWith(compareBy<InstalledGameItemData> { it.gameEntity?.playedTime }.thenByDescending { it.gameEntity?.packageInstallTime })
|
||||
}
|
||||
decorateListAndPost()
|
||||
}
|
||||
}
|
||||
|
||||
fun initData(list: ArrayList<GameInstall>) {
|
||||
val packageLaunchTimeSp = HaloApp.getInstance().getSharedPreferences(Constants.SP_PACKAGE_LAUNCH_TIME, Context.MODE_PRIVATE)
|
||||
val simulatorGameInstallList = DownloadManager.getInstance().allSimulatorDownloadEntity.asSequence()
|
||||
.mapNotNull { entity ->
|
||||
entity.gameId.takeUnless { shouldThisGameBeFiltered(it) }?.let { gameId ->
|
||||
GameInstall(
|
||||
id = gameId,
|
||||
isSimulatorGame = true,
|
||||
lastUsedTime = SPUtils.getLong(packageLaunchTimeSp, gameId, 0L)
|
||||
)
|
||||
}
|
||||
}
|
||||
.toList()
|
||||
|
||||
val normalGameInstallList = list.asSequence()
|
||||
.filterNot { it.isVGame || shouldThisGameBeFiltered(it.id) }
|
||||
.onEach { gameInstall ->
|
||||
val ghId = PackageUtils.getMetaData(getApplication(), gameInstall.packageName, "gh_id")
|
||||
if (ghId != null && ghId != gameInstall.id) {
|
||||
gameInstall.id = ghId.toString()
|
||||
} else {
|
||||
val gameId = getGameId(gameInstall.packageName)
|
||||
if (!TextUtils.isEmpty(gameId)) {
|
||||
gameInstall.id = gameId
|
||||
}
|
||||
}
|
||||
GameInstall.updateRecentlyPlayedTime(gameInstall)
|
||||
}
|
||||
.toList()
|
||||
|
||||
allGameInstallList.clear()
|
||||
allGameInstallList.addAll(simulatorGameInstallList)
|
||||
allGameInstallList.addAll(normalGameInstallList)
|
||||
|
||||
if (allGameInstallList.isNotEmpty()) {
|
||||
loadGamesData(allGameInstallList.mapNotNull { it.id }.toSet())
|
||||
} else {
|
||||
itemDataListLiveData.postValue(emptyList())
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadGamesData(ids: Set<String>) {
|
||||
val paramsMap = mapOf(
|
||||
"game_id" to ids
|
||||
)
|
||||
disposable = newApi.postMyHaloGame(TAB_INSTALLED, 1, ids.size, paramsMap.toRequestBody())
|
||||
.map(ApkActiveUtils.filterMapperList)
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
processingData(it)
|
||||
}, {
|
||||
loadStatusLiveData.postValue(LoadStatus.INIT_FAILED)
|
||||
})
|
||||
}
|
||||
|
||||
private fun processingData(gameList: List<GameEntity>) {
|
||||
val sortedGameList = mutableListOf<GameEntity>()
|
||||
for (entity in gameList) {
|
||||
val newEntity = entity.copy()
|
||||
// 下载管理不显示镜像游戏,不然会有奇怪的问题
|
||||
if (entity.shouldUseMirrorInfo()) {
|
||||
continue
|
||||
}
|
||||
|
||||
val gameInstallList = allGameInstallList.filter { it.id == entity.id }
|
||||
if (gameInstallList.isEmpty()) {
|
||||
continue
|
||||
}
|
||||
|
||||
if (gameInstallList.size > 1) {
|
||||
val gameInstall = gameInstallList.maxByOrNull { it.recentlyPlayedTime }
|
||||
newEntity.packageInstallTime = gameInstall?.installTime ?: 0L
|
||||
newEntity.packageLastUpdateTime = gameInstall?.lastUpdateTime ?: 0L
|
||||
newEntity.lastUsedTime = gameInstall?.lastUsedTime ?: 0L
|
||||
} else {
|
||||
newEntity.packageInstallTime = gameInstallList.first().installTime
|
||||
newEntity.packageLastUpdateTime = gameInstallList.first().lastUpdateTime
|
||||
newEntity.lastUsedTime = gameInstallList.first().lastUsedTime
|
||||
}
|
||||
|
||||
if (entity.getApk().size > 1) {
|
||||
val packageNameSet = gameInstallList.map { it.packageName }.toSet()
|
||||
newEntity.setApk(entity.getApk().filter { packageNameSet.contains(it.packageName) }.toArrayList())
|
||||
newEntity.dropOtherApk()
|
||||
newEntity.getApk().forEach { apkEntity ->
|
||||
if (newEntity.pluggableCollection == null && PackageUtils.isCanPluggable(apkEntity)) {
|
||||
val pluggableCollection =
|
||||
GameUtils.getPluggableCollectionFromGameEntity(entity, apkEntity.packageName)
|
||||
if (pluggableCollection != null) {
|
||||
newEntity.pluggableCollection = pluggableCollection
|
||||
}
|
||||
}
|
||||
}
|
||||
newEntity.playedTime = newEntity.getApk().maxOfOrNull { it.playedTime } ?: 0L
|
||||
} else if (entity.getApk().size == 1) {
|
||||
newEntity.playedTime = newEntity.getApk().firstOrNull()?.playedTime ?: 0L
|
||||
}
|
||||
|
||||
if (newEntity.getApk().size == 1 && (newEntity.isDualBtnModeEnabled() || newEntity.isVGamePreferred())) {
|
||||
// 双下载按钮或畅玩优先的游戏,仅本地已安装才显示
|
||||
if (!PackagesManager.isInstalled(newEntity.getUniquePackageName())) {
|
||||
continue
|
||||
}
|
||||
|
||||
newEntity.forceToBeDownloadModeOnly()
|
||||
}
|
||||
|
||||
if (newEntity.getApk().isEmpty()) continue
|
||||
|
||||
sortedGameList.add(newEntity)
|
||||
}
|
||||
|
||||
when (sortedType) {
|
||||
InstalledSortType.RECENTLY_PLAYED -> sortedGameList.sortByDescending { it.recentlyPlayedTime }
|
||||
InstalledSortType.LATEST_UPDATED -> sortedGameList.sortByDescending { it.updateTime }
|
||||
InstalledSortType.MOST_PLAYED -> sortedGameList.sortWith(compareByDescending<GameEntity> { it.playedTime }.thenByDescending { it.packageInstallTime })
|
||||
InstalledSortType.LEAST_PLAYED -> sortedGameList.sortWith(compareBy<GameEntity> { it.playedTime }.thenByDescending { it.packageInstallTime })
|
||||
}
|
||||
|
||||
for (entity in sortedGameList) {
|
||||
entity.setEntryMap(DownloadManager.getInstance().getEntryMap(entity.name))
|
||||
}
|
||||
|
||||
sortedItemDataList.clear()
|
||||
sortedItemDataList.addAll(sortedGameList.map { InstalledGameItemData(gameEntity = it) })
|
||||
|
||||
decorateListAndPost()
|
||||
}
|
||||
|
||||
private fun decorateListAndPost() {
|
||||
val itemDataList = mutableListOf<InstalledGameItemData>()
|
||||
if (!packageUpdateList.isNullOrEmpty()) {
|
||||
itemDataList.add(
|
||||
InstalledGameItemData(
|
||||
isHead = true,
|
||||
updatableGameCount = packageUpdateList!!.size,
|
||||
updatableGameList = packageUpdateList!!.take(3)
|
||||
.map { it.transformGameEntity() }
|
||||
)
|
||||
)
|
||||
}
|
||||
itemDataList.addAll(sortedItemDataList)
|
||||
itemDataList.add(InstalledGameItemData(isFooter = true))
|
||||
itemDataListLiveData.postValue(itemDataList)
|
||||
loadStatusLiveData.postValue(if (itemDataList.isEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_LOADED)
|
||||
}
|
||||
|
||||
fun updateGameUpdateData(dataList: List<GameUpdateEntity>) {
|
||||
packageUpdateList = dataList
|
||||
|
||||
val itemDataList = itemDataListLiveData.value?.toMutableList() ?: arrayListOf()
|
||||
if (itemDataList.isNotEmpty() && dataList.isNotEmpty()) {
|
||||
if (itemDataList.first().isHead) itemDataList.removeAt(0)
|
||||
decorateListAndPost()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAB_INSTALLED = "installed"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,29 @@
|
||||
package com.gh.gamecenter.personal.playtime
|
||||
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
|
||||
/**
|
||||
* 游戏时长数据
|
||||
*/
|
||||
class PlayTimeActivity : BaseActivity() {
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.activity_amway
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
DisplayUtils.transparentStatusBar(this)
|
||||
|
||||
val containerFragment = supportFragmentManager.findFragmentByTag(
|
||||
PlayTimeFragment::class.java.name
|
||||
)
|
||||
?: PlayTimeFragment().with(intent.extras)
|
||||
supportFragmentManager.beginTransaction()
|
||||
.replace(R.id.placeholder, containerFragment, PlayTimeFragment::class.java.name)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,88 @@
|
||||
package com.gh.gamecenter.personal.playtime
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.ItemViewType
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.utils.NumberUtils
|
||||
import com.gh.gamecenter.databinding.ItemPlayTimeBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
class PlayTimeAdapter(context: Context, private val viewModel: PlayTimeViewModel): ListAdapter<GameEntity>(context) {
|
||||
|
||||
private var maxWidth = context.resources.displayMetrics.widthPixels - 80F.dip2px()
|
||||
private var minWidth = 32F.dip2px()
|
||||
private var maxPlayTime = 0L
|
||||
|
||||
override fun getItemViewType(position: Int): Int = if (position == itemCount - 1) {
|
||||
ItemViewType.ITEM_FOOTER
|
||||
} else {
|
||||
ItemViewType.GAME_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
when (viewType) {
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
PlayTimeItemViewHolder(parent.toBinding())
|
||||
}
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is PlayTimeItemViewHolder) {
|
||||
val gameEntity = mEntityList.getOrNull(position) ?: return
|
||||
holder.binding.run {
|
||||
gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(mContext))
|
||||
playTimeTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext))
|
||||
root.updateLayoutParams<MarginLayoutParams> { updateMargins(top = if (position == 0) 10F.dip2px() else 0) }
|
||||
rankTv.typeface = Typeface.createFromAsset(mContext.assets, Constants.DIN_FONT_PATH)
|
||||
rankTv.text = (position + 1).toString()
|
||||
rankTv.setTextColor(
|
||||
when (position) {
|
||||
0 -> com.gh.gamecenter.common.R.color.number_1.toColor(mContext)
|
||||
1 -> com.gh.gamecenter.common.R.color.number_2.toColor(mContext)
|
||||
2 -> com.gh.gamecenter.common.R.color.number_3.toColor(mContext)
|
||||
else -> com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext)
|
||||
}
|
||||
)
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
gameNameTv.text = gameEntity.name
|
||||
val playTime = NumberUtils.getPlayTimeFormatTime(gameEntity.playedTime)
|
||||
if (position == 0) {
|
||||
val playTimeTextWidth = playTimeTv.paint.measureText(playTime)
|
||||
maxWidth = (mContext.resources.displayMetrics.widthPixels - 80F.dip2px() - playTimeTextWidth).toInt()
|
||||
maxPlayTime = gameEntity.playedTime
|
||||
}
|
||||
playTimeTv.text = playTime
|
||||
progressBar.updateLayoutParams<MarginLayoutParams> {
|
||||
width = if (position == 0) maxWidth else (minWidth + (maxWidth - minWidth) * (gameEntity.playedTime / maxPlayTime.toFloat())).toInt()
|
||||
}
|
||||
progressBar.setRoundedColorBackgroundByColorInt(gameEntity.iconThemeColor.hexStringToIntColor().modifyHSV(s = 0.8F, v = 0.8F), 8F, 0.1F)
|
||||
gameContainer.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, "游戏时长数据", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is FooterViewHolder) {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(viewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
holder.hint.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (mEntityList.isEmpty()) 0 else mEntityList.size + 1
|
||||
|
||||
class PlayTimeItemViewHolder(var binding: ItemPlayTimeBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
}
|
||||
@ -0,0 +1,202 @@
|
||||
package com.gh.gamecenter.personal.playtime
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup.LayoutParams
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.core.view.ViewCompat
|
||||
import androidx.core.view.WindowInsetsCompat
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.common.util.UsageStatsHelper
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LazyListFragment
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.callback.CancelListener
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.setSwitchAnimation
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.core.utils.ClickUtils
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.core.utils.NumberUtils
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.FragmentPlayTimeBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import kotlin.math.abs
|
||||
|
||||
class PlayTimeFragment: LazyListFragment<GameEntity, PlayTimeViewModel>() {
|
||||
private lateinit var binding: FragmentPlayTimeBinding
|
||||
private val viewModel: PlayTimeViewModel by lazy { viewModelProvider() }
|
||||
private val playTimeAdapter by lazy { PlayTimeAdapter(requireContext(), viewModel) }
|
||||
private var dismissGameTimeLongDialogFlag = false
|
||||
private var isCollapsed = false
|
||||
|
||||
override fun getStubLayoutId() = com.gh.gamecenter.common.R.layout.fragment_stub
|
||||
|
||||
override fun getRealLayoutId() = R.layout.fragment_play_time
|
||||
|
||||
override fun provideListViewModel(): PlayTimeViewModel = viewModel
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<*> = playTimeAdapter
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
// 提交用户使用数据
|
||||
runOnIoThread {
|
||||
UsageStatsHelper.checkAndPostUsageStats()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
super.onRealLayoutInflated(inflatedView)
|
||||
binding = FragmentPlayTimeBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun inflateRealView() {
|
||||
super.inflateRealView()
|
||||
binding.run {
|
||||
hourDataTv.typeface = Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
|
||||
minuteDataTv.typeface = Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
|
||||
updatePlayTimeSetting()
|
||||
|
||||
// toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding
|
||||
// [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits]
|
||||
ViewCompat.setOnApplyWindowInsetsListener(appbar) { _, insets ->
|
||||
(toolbar.layoutParams as MarginLayoutParams).topMargin = insets.getInsets(WindowInsetsCompat.Type.systemBars()).top
|
||||
WindowInsetsCompat.CONSUMED
|
||||
}
|
||||
|
||||
val statusBarHeight = DisplayUtils.getStatusBarHeight(context?.resources)
|
||||
val collapsingTrigger = 66F.dip2px() + statusBarHeight
|
||||
|
||||
toolbar.setNavigationOnClickListener { requireActivity().finish() }
|
||||
|
||||
collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger
|
||||
collapsingToolbar.scrimShownAction = {
|
||||
isCollapsed = it
|
||||
titleTv.isVisible = it
|
||||
}
|
||||
|
||||
titleTv.setOnClickListener {
|
||||
if (ClickUtils.isFastDoubleClick(titleTv.id, 300)) {
|
||||
scrollToTop()
|
||||
}
|
||||
}
|
||||
|
||||
appbar.addOnOffsetChangedListener { _, verticalOffset ->
|
||||
val absOffset = abs(verticalOffset)
|
||||
mListRefresh?.isEnabled = absOffset <= 2
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentResume() {
|
||||
super.onFragmentResume()
|
||||
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
showUsageStatsDialogIfNeeded()
|
||||
|
||||
viewModel.totalPlayTimeLiveData.observe(viewLifecycleOwner) {
|
||||
val hourAndMinute = NumberUtils.splitToHourAndMinute(it)
|
||||
binding.hourDataTv.goneIf(hourAndMinute.first == 0L) {
|
||||
binding.hourDataTv.text = hourAndMinute.first.toString()
|
||||
}
|
||||
binding.hourTv.goneIf(hourAndMinute.first == 0L)
|
||||
binding.minuteDataTv.isVisible = true
|
||||
binding.minuteTv.isVisible = true
|
||||
binding.minuteDataTv.text = hourAndMinute.second.toString()
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUsageStatsDialogIfNeeded() {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 &&
|
||||
!UsageStatsHelper.checkForPermission() &&
|
||||
SPUtils.getBoolean(Constants.SP_SHOW_USAGE_STATS_DIALOG, true)) {
|
||||
showUsageStatsDialog()
|
||||
SPUtils.setBoolean(Constants.SP_SHOW_USAGE_STATS_DIALOG, false)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updatePlayTimeSetting() {
|
||||
binding.run {
|
||||
val showUsageStats = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 && !UsageStatsHelper.checkForPermission()
|
||||
playTimeSettingContainer.goneIf(!showUsageStats) {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
lottieView.setSwitchAnimation(UsageStatsHelper.checkForPermission())
|
||||
lottieView.setOnClickListener {
|
||||
dismissGameTimeLongDialogFlag = false
|
||||
if (!UsageStatsHelper.checkForPermission()) {
|
||||
showUsageStatsDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun showUsageStatsDialog() {
|
||||
DialogUtils.showUsageStatsDialog(
|
||||
requireContext(), {
|
||||
NewFlatLogUtils.logGameTimeLongDialogClick("去开启")
|
||||
dismissGameTimeLongDialogFlag = true
|
||||
UsageStatsHelper.skipToUsageStats(
|
||||
requireContext(),
|
||||
UsageStatsHelper.USAGE_STATUS_REQUEST_CODE
|
||||
)
|
||||
}, object : CancelListener {
|
||||
override fun onCancel() {
|
||||
if (!dismissGameTimeLongDialogFlag) {
|
||||
NewFlatLogUtils.logGameTimeLongDialogClick("关闭弹窗")
|
||||
dismissGameTimeLongDialogFlag = true
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private fun scrollToTop() {
|
||||
val firstItemPosition = mLayoutManager.findFirstVisibleItemPosition()
|
||||
if (firstItemPosition >= 10) {
|
||||
mListRv?.scrollToPosition(6)
|
||||
}
|
||||
mListRv?.smoothScrollToPosition(0)
|
||||
binding.appbar.setExpanded(true)
|
||||
}
|
||||
|
||||
override fun onLoadDone() {
|
||||
super.onLoadDone()
|
||||
binding.container.updateLayoutParams<LayoutParams> { height = LayoutParams.WRAP_CONTENT }
|
||||
}
|
||||
|
||||
override fun onLoadRefresh() {
|
||||
super.onLoadRefresh()
|
||||
binding.container.updateLayoutParams<LayoutParams> { height = LayoutParams.MATCH_PARENT }
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == UsageStatsHelper.USAGE_STATUS_REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
updatePlayTimeSetting()
|
||||
SPUtils.setBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, UsageStatsHelper.checkForPermission())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
DisplayUtils.setLightStatusBar(requireActivity(), !mIsDarkModeOn)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.gh.gamecenter.personal.playtime
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
class PlayTimeViewModel(application: Application) :
|
||||
ListViewModel<GameEntity, GameEntity>(application) {
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
|
||||
val totalPlayTimeLiveData = MutableLiveData<Long>()
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<List<GameEntity>>? {
|
||||
return api.getMostPlayedGames(UserManager.getInstance().userId, Utils.getTime(getApplication()))
|
||||
.map {
|
||||
totalPlayTimeLiveData.postValue(it.headers()["TotalTime"]?.toLong() ?: 0L)
|
||||
it.body()
|
||||
}
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<GameEntity>>? = null
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.provider.IPushProvider
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import com.therouter.TheRouter
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class ManageReservationUseCase<T> {
|
||||
private val repository = ReservationRepository()
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun deleteOrCancelReservation(game: GameEntity, deleteReservation: Boolean, onSuccess: () -> Unit) {
|
||||
val single = if (deleteReservation) {
|
||||
repository.deleteReservation(game.id, game).subscribeOn(Schedulers.io())
|
||||
} else {
|
||||
repository.cancelReservation(game.id, game)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
single.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
repository.refreshReservations()
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
Utils.toast(HaloApp.getInstance().application, exception.message)
|
||||
exception.printStackTrace()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private val _enableAutoDownloadSuccessfully = MutableLiveData<Event<Boolean>>()
|
||||
val enableAutoDownloadSuccessfully: LiveData<Event<Boolean>> = _enableAutoDownloadSuccessfully
|
||||
fun enableAutoDownload(enable: Boolean, games: Set<GameEntity>, mapper: (ResponseBody) -> List<T>, onSuccess: (List<T>) -> Unit) {
|
||||
if (enable) {
|
||||
games.forEach {
|
||||
SensorsBridge.trackOpenAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese)
|
||||
}
|
||||
} else {
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentAutomaticDownload(it.id, it.name ?: "", it.categoryChinese)
|
||||
}
|
||||
}
|
||||
val params = createParamsInBatch(games).apply {
|
||||
put("wifi_auto_download", enable)
|
||||
}
|
||||
repository.enableAutoDownloadInBatches(params.toRequestBody())
|
||||
.map(mapper)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<T>>() {
|
||||
override fun onSuccess(data: List<T>) {
|
||||
onSuccess.invoke(data)
|
||||
_enableAutoDownloadSuccessfully.value = Event(enable)
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
fun cancelReserveInBatch(games: Set<GameEntity>, onSuccess: () -> Unit) {
|
||||
val params = createParamsInBatch(games).apply {
|
||||
put("appointment", false)
|
||||
}
|
||||
repository.cancelReserveInBatch(params.toRequestBody())
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
repository.refreshReservations()
|
||||
onSuccess()
|
||||
}
|
||||
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun createParamsInBatch(games: Set<GameEntity>): HashMap<String, Any> {
|
||||
val jPushId =
|
||||
(TheRouter.get(IPushProvider::class.java)
|
||||
?.getRegistrationId(HaloApp.getInstance().application)) ?: ""
|
||||
val idAndMirrorType = games.map {
|
||||
var mirrorPosition = it.getMirrorPosition()
|
||||
if (mirrorPosition == -1) {
|
||||
mirrorPosition = 0
|
||||
}
|
||||
hashMapOf("_id" to it.id, "mirror_type" to mirrorPosition)
|
||||
}
|
||||
return hashMapOf("jpush_id" to jPushId, "games" to idAndMirrorType)
|
||||
}
|
||||
|
||||
fun onCleared() {
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,496 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Typeface
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.OnScrollListener
|
||||
import com.gh.common.pop.ReserveAllSelectPop
|
||||
import com.gh.common.util.CommentUtils
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.common.util.ReservationHelper
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.callback.CancelListener
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.DrawableView
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.utils.StringUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloGameBinding
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloReservationHeadBinding
|
||||
import com.gh.gamecenter.databinding.ItemMyHaloReservationMoreBinding
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
|
||||
import com.gh.gamecenter.game.GameAndPosition
|
||||
import com.gh.gamecenter.mygame.MyReservationFragment
|
||||
import com.gh.gamecenter.personal.installed.InstalledAdapter
|
||||
import com.lightgame.view.CheckableImageView
|
||||
import com.therouter.TheRouter
|
||||
|
||||
class ReservationAdapter(private val context: Context, private val viewModel: ReservationViewModel) :
|
||||
ListAdapter<ReservationGameItemData, RecyclerView.ViewHolder>(createDiffCallback()) {
|
||||
|
||||
private var pageState: MyReservationFragment.ReservePageState = MyReservationFragment.ReservePageState.Normal
|
||||
private val isEditing: Boolean
|
||||
get() = pageState != MyReservationFragment.ReservePageState.Normal
|
||||
|
||||
fun changeStatus(status: MyReservationFragment.ReservePageState) {
|
||||
if (pageState != status) {
|
||||
selectedGames.clear()
|
||||
pageState = status
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
|
||||
if (status == MyReservationFragment.ReservePageState.Normal) {
|
||||
allSelectPop.dismiss()
|
||||
} else {
|
||||
allSelectPop.show(status, context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyFooterItemOnLoadStatusChanged(isDownloadable: Boolean, loadStatus: LoadStatus) {
|
||||
if (loadStatus != LoadStatus.LIST_LOADED) {
|
||||
currentList.indexOfFirst { it.isFooter && it.isDownloadable == isDownloadable }.let {
|
||||
if (it != -1) {
|
||||
notifyItemChanged(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val selectedGames = mutableSetOf<GameEntity>()
|
||||
private val allSelectPop by lazy {
|
||||
ReserveAllSelectPop.create(context, object : ReserveAllSelectPop.OnReserveAllSelectListener {
|
||||
override fun selectAll(isChecked: Boolean): Int {
|
||||
selectedGames.clear()
|
||||
if (isChecked) {
|
||||
if (pageState == MyReservationFragment.ReservePageState.EnableAutoDownload) {
|
||||
currentList
|
||||
.mapNotNull { it.gameEntity }
|
||||
.filter {
|
||||
it.wifiAutoDownloadEnable && !it.wifiAutoDownload
|
||||
}
|
||||
.let(selectedGames::addAll)
|
||||
} else {
|
||||
currentList.mapNotNull { it.gameEntity }.let(selectedGames::addAll)
|
||||
}
|
||||
|
||||
}
|
||||
notifyItemRangeChanged(0, currentList.size)
|
||||
return selectedGames.size
|
||||
}
|
||||
|
||||
override fun submit() {
|
||||
doSubmit()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun doSubmit() {
|
||||
if (pageState == MyReservationFragment.ReservePageState.EnableAutoDownload) {
|
||||
allSelectPop.dismiss()
|
||||
viewModel.enableAutoDownload(true, selectedGames.toSet())
|
||||
viewModel.changeReserveStatus(MyReservationFragment.ReservePageState.Normal)
|
||||
} else {
|
||||
showCancelReservationInBatchDialog(selectedGames.toSet())
|
||||
}
|
||||
}
|
||||
|
||||
private fun showCancelReservationInBatchDialog(games: Set<GameEntity>) {
|
||||
// 批量删除,埋点在外部上报
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentDialogShow(it.id, it.name ?: "", it.categoryChinese)
|
||||
}
|
||||
ReservationHelper.showCancelReservationDialog(context, null, {
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentDialogClick(it.id, it.name ?: "", it.categoryChinese, "确定取消")
|
||||
}
|
||||
viewModel.cancelReserveInBatch(games)
|
||||
if (allSelectPop.isShowing) {
|
||||
allSelectPop.dismiss()
|
||||
}
|
||||
viewModel.changeReserveStatus(MyReservationFragment.ReservePageState.Normal)
|
||||
}, object : CancelListener {
|
||||
override fun onCancel() {
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentDialogClick(
|
||||
it.id,
|
||||
it.name ?: "",
|
||||
it.categoryChinese,
|
||||
"暂不取消"
|
||||
)
|
||||
}
|
||||
}
|
||||
}, dialogCancelCallback = {
|
||||
games.forEach {
|
||||
SensorsBridge.trackCancelAppointmentDialogClick(it.id, it.name ?: "", it.categoryChinese, "关闭弹窗")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun showCancelReservationDialog(game: GameEntity) {
|
||||
NewFlatLogUtils.logMyGameReserveTabGameCardClick("按钮", game.id, game.name ?: "")
|
||||
ReservationHelper.showCancelReservationDialog(context, game, {
|
||||
NewFlatLogUtils.logMyGameCancelReserveDialogClick("确定取消", game.id, game.name ?: "")
|
||||
viewModel.cancelReservation(game)
|
||||
}, object : CancelListener {
|
||||
override fun onCancel() {
|
||||
NewFlatLogUtils.logMyGameCancelReserveDialogClick("关闭弹窗", game.id, game.name ?: "")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val itemData = currentList.getOrNull(position) ?: return ITEM_NORMAL
|
||||
return when {
|
||||
itemData.isHead -> ITEM_HEAD
|
||||
itemData.isMore -> ITEM_MORE
|
||||
itemData.isFooter -> ITEM_FOOTER
|
||||
else -> ITEM_NORMAL
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
when (viewType) {
|
||||
ITEM_HEAD -> {
|
||||
ReservationHeadItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
ITEM_MORE -> {
|
||||
ReservationMoreItemViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
ITEM_FOOTER -> {
|
||||
FooterViewHolder(
|
||||
parent.layoutInflater.inflate(
|
||||
com.gh.gamecenter.common.R.layout.refresh_footerview,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ReservationGameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val itemData = currentList.getOrNull(position) ?: return
|
||||
if (holder is ReservationHeadItemViewHolder) {
|
||||
holder.binding.run {
|
||||
root.background =
|
||||
com.gh.gamecenter.common.R.drawable.background_shape_white_radius_12_top_only.toDrawable(context)
|
||||
titleTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(context))
|
||||
countTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
countTv.typeface = Typeface.createFromAsset(context.assets, Constants.DIN_FONT_PATH)
|
||||
titleTv.text = itemData.title
|
||||
countTv.text = itemData.gameCount.toString()
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is ReservationMoreItemViewHolder) {
|
||||
holder.binding.run {
|
||||
root.background =
|
||||
com.gh.gamecenter.common.R.drawable.ui_surface_radius_12_bottom_only_item_style.toDrawable(context)
|
||||
divider.setBackgroundColor(com.gh.gamecenter.common.R.color.ui_divider.toColor(context))
|
||||
moreTv.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
moreTv.setDrawableEnd(if (viewModel.isDownloadableExpand) R.drawable.ic_auxiliary_arrow_up_8 else R.drawable.ic_auxiliary_arrow_down_8)
|
||||
moreTv.text = if (viewModel.isDownloadableExpand) "收起" else "查看全部"
|
||||
root.setOnClickListener {
|
||||
viewModel.onMoreClick()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is ReservationGameItemViewHolder) {
|
||||
val gameEntity = itemData.gameEntity ?: return
|
||||
holder.binding.run {
|
||||
multiVersionContainer.isVisible = false
|
||||
root.updateLayoutParams<MarginLayoutParams> {
|
||||
leftMargin = 8F.dip2px()
|
||||
rightMargin = 8F.dip2px()
|
||||
}
|
||||
root.background = if (itemData.roundBottomOnly) {
|
||||
com.gh.gamecenter.common.R.drawable.ui_surface_radius_12_bottom_only_item_style.toDrawable(context)
|
||||
} else {
|
||||
com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
}
|
||||
gameInfo.run {
|
||||
tvSpeed.isVisible = false
|
||||
arrowIv.isVisible = false
|
||||
zoneDivider.isVisible = false
|
||||
gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(root.context))
|
||||
val provider = TheRouter.get(IBindingAdaptersProvider::class.java)
|
||||
provider?.setGameName(gameNameTv, gameEntity, false)
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
|
||||
gameDesTv.text = "${CommentUtils.getCommentTime(gameEntity.reserveTime)} 预约"
|
||||
wifiAutoDownloadTv.goneIf(!gameEntity.wifiAutoDownload)
|
||||
tagTv.goneIf(gameEntity.wifiAutoDownload) {
|
||||
tagTv.text = gameEntity.tagStyle.take(3).joinToString("·") { it.name }
|
||||
}
|
||||
|
||||
selectIv.isVisible = isEditing
|
||||
updateSelectedStatus(selectIv, gameEntity)
|
||||
downloadBtn.isVisible = !isEditing
|
||||
moreIv.isVisible = !isEditing
|
||||
moreIv.setOnClickListener {
|
||||
ReservationManageDialogFragment.show(
|
||||
context,
|
||||
gameEntity,
|
||||
onWifiAutoDownloadClickListener = {
|
||||
viewModel.enableAutoDownload(it, setOf(gameEntity))
|
||||
},
|
||||
onCancelReservationClickListener = {
|
||||
showCancelReservationDialog(gameEntity)
|
||||
})
|
||||
}
|
||||
|
||||
if (!isEditing) {
|
||||
DownloadItemUtils.updateItemWithReserveStatus(
|
||||
GameViewHolder(root).also {
|
||||
it.gameDownloadBtn = downloadBtn
|
||||
it.gameDownloadTips = downloadTipsLottie
|
||||
it.multiVersionDownloadTv = multiVersionDownloadTv
|
||||
},
|
||||
gameEntity
|
||||
)
|
||||
}
|
||||
|
||||
if ("appointment" == gameEntity.reserveStatus) {
|
||||
downloadBtn.setOnClickListener {
|
||||
showCancelReservationDialog(gameEntity)
|
||||
}
|
||||
} else {
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
context,
|
||||
downloadBtn,
|
||||
gameEntity,
|
||||
holder.bindingAdapterPosition,
|
||||
this@ReservationAdapter,
|
||||
"我的光环-预约",
|
||||
location = StringUtils.buildString("我的光环-预约", ":", gameEntity.name),
|
||||
traceEvent = null,
|
||||
clickCallback = {
|
||||
SensorsBridge.trackHaloSelfGameAppointmentClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
"按钮",
|
||||
downloadBtn.text,
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InstalledAdapter.bindAnnouncement(InstalledAdapter.TYPE_RESERVATION, this, gameEntity, -1)
|
||||
InstalledAdapter.bindService(InstalledAdapter.TYPE_RESERVATION, this, gameEntity, -1, "我的光环-预约")
|
||||
|
||||
root.setOnClickListener {
|
||||
if (pageState == MyReservationFragment.ReservePageState.Normal) {
|
||||
GameDetailActivity.startGameDetailActivity(context, gameEntity.id, "我的光环-预约", null)
|
||||
SensorsBridge.trackHaloSelfGameAppointmentClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
"游戏",
|
||||
"游戏",
|
||||
)
|
||||
} else {
|
||||
val isEnableSelect = pageState == MyReservationFragment.ReservePageState.CancelReserve
|
||||
|| (gameEntity.wifiAutoDownloadEnable && !gameEntity.wifiAutoDownload)
|
||||
if (isEnableSelect) { // 可勾选状态
|
||||
if (selectIv.isEnabled) {
|
||||
selectIv.toggle()
|
||||
if (selectIv.isChecked) {
|
||||
selectedGames.add(gameEntity)
|
||||
} else {
|
||||
selectedGames.remove(gameEntity)
|
||||
}
|
||||
updateSelectedStatus(selectIv, gameEntity)
|
||||
allSelectPop.updateSelectedCount(selectedGames.size)
|
||||
}
|
||||
} else { // 不可勾选状态,弹出相应的提示
|
||||
val toastResId = if (gameEntity.wifiAutoDownload) {
|
||||
// 已经开启wifi自动下载
|
||||
R.string.has_enabled_for_auto_download_in_wifi
|
||||
} else {
|
||||
R.string.not_support_auto_download_in_wifi
|
||||
}
|
||||
ToastUtils.showToast(toastResId.toResString())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.itemView.tag = gameEntity.id
|
||||
}
|
||||
|
||||
if (holder is FooterViewHolder) {
|
||||
val loadStatus =
|
||||
if (itemData.isDownloadable) viewModel.downloadableLoadStatus.value else viewModel.reservableLoadStatus.value
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(
|
||||
loadStatus == LoadStatus.LIST_LOADING,
|
||||
loadStatus == LoadStatus.LIST_FAILED,
|
||||
loadStatus == LoadStatus.LIST_OVER
|
||||
) {
|
||||
if (itemData.isDownloadable && viewModel.downloadableLoadStatus.value == LoadStatus.LIST_FAILED) {
|
||||
viewModel.onDownloadableLoadMore()
|
||||
} else if (viewModel.reservableLoadStatus.value == LoadStatus.LIST_FAILED) {
|
||||
viewModel.onReservableLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSelectedStatus(selectIv: CheckableImageView, gameEntity: GameEntity) {
|
||||
if (pageState == MyReservationFragment.ReservePageState.CancelReserve) {
|
||||
selectIv.isChecked = selectedGames.any { gameEntity.id == it.id }
|
||||
val isChecked = selectedGames.any { gameEntity.id == it.id }
|
||||
if (isChecked) {
|
||||
selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_selected)
|
||||
} else {
|
||||
selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_default)
|
||||
}
|
||||
} else { // 批量开启wifi自动下载
|
||||
when {
|
||||
!gameEntity.wifiAutoDownloadEnable -> { // 此游戏不支持wifi自动下载,不可勾选状态
|
||||
selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_unable)
|
||||
}
|
||||
|
||||
gameEntity.wifiAutoDownload -> {// 已经开启了wifi自动下载,勾选-不可编辑状态
|
||||
selectIv.setImageResource(com.gh.gamecenter.common.R.drawable.ic_selector_selected_unable)
|
||||
}
|
||||
|
||||
else -> { // 默认状态,可勾选
|
||||
selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(context))
|
||||
selectIv.isChecked = selectedGames.any { gameEntity.id == it.id }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private val onLoadMoreListener = object : OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
autoLoadMore(recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
private fun autoLoadMore(recyclerView: RecyclerView) {
|
||||
val layoutManger = recyclerView.layoutManager
|
||||
if (layoutManger is LinearLayoutManager) {
|
||||
val lastVisibleItemPosition = layoutManger.findLastVisibleItemPosition()
|
||||
val lastVisibleItemData = currentList.getOrNull(lastVisibleItemPosition)
|
||||
|
||||
if (lastVisibleItemData?.isFooter == true) {
|
||||
val loadStatus =
|
||||
if (lastVisibleItemData.isDownloadable) viewModel.downloadableLoadStatus.value else viewModel.reservableLoadStatus.value
|
||||
if (lastVisibleItemData.isDownloadable &&
|
||||
loadStatus != LoadStatus.LIST_OVER &&
|
||||
loadStatus != LoadStatus.LIST_LOADING
|
||||
) {
|
||||
viewModel.onDownloadableLoadMore()
|
||||
} else if (loadStatus != LoadStatus.LIST_OVER &&
|
||||
loadStatus != LoadStatus.LIST_LOADING
|
||||
) {
|
||||
viewModel.onReservableLoadMore()
|
||||
}
|
||||
}
|
||||
|
||||
if (lastVisibleItemData?.isDownloadable == false) {
|
||||
val loadStatus = viewModel.downloadableLoadStatus.value
|
||||
if (loadStatus != LoadStatus.INIT_EMPTY &&
|
||||
loadStatus != LoadStatus.LIST_FAILED &&
|
||||
loadStatus != LoadStatus.LIST_OVER &&
|
||||
loadStatus != LoadStatus.LIST_LOADING
|
||||
) {
|
||||
viewModel.onDownloadableLoadMore()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun submitList(recyclerView: RecyclerView, list: List<ReservationGameItemData>) {
|
||||
submitList(list) {
|
||||
autoLoadMore(recyclerView)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAttachedToRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onAttachedToRecyclerView(recyclerView)
|
||||
recyclerView.addOnScrollListener(onLoadMoreListener)
|
||||
}
|
||||
|
||||
override fun onDetachedFromRecyclerView(recyclerView: RecyclerView) {
|
||||
super.onDetachedFromRecyclerView(recyclerView)
|
||||
recyclerView.removeOnScrollListener(onLoadMoreListener)
|
||||
}
|
||||
|
||||
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
|
||||
val data = getGameEntityByPackage(status.packageName)
|
||||
for (gameAndPosition in data) {
|
||||
if (gameAndPosition.entity != null && gameAndPosition.entity.name == status.name) {
|
||||
gameAndPosition.entity.getEntryMap().remove(status.platform)
|
||||
}
|
||||
notifyItemChanged(gameAndPosition.position)
|
||||
}
|
||||
}
|
||||
|
||||
fun getGameEntityByPackage(packageName: String): List<GameAndPosition> {
|
||||
val positionList = ArrayList<GameAndPosition>()
|
||||
val positionMap = viewModel.positionAndPackageMap
|
||||
for (key in positionMap.keys) {
|
||||
if (key.contains(packageName)) {
|
||||
val position = positionMap[key]!!
|
||||
val game = currentList.getOrNull(position)?.gameEntity
|
||||
if (game != null) {
|
||||
positionList.add(GameAndPosition(game, position))
|
||||
}
|
||||
}
|
||||
}
|
||||
return positionList
|
||||
}
|
||||
|
||||
class ReservationHeadItemViewHolder(val binding: ItemMyHaloReservationHeadBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class ReservationGameItemViewHolder(val binding: ItemMyHaloGameBinding) : RecyclerView.ViewHolder(binding.root)
|
||||
class ReservationMoreItemViewHolder(val binding: ItemMyHaloReservationMoreBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
const val ITEM_HEAD = 0
|
||||
const val ITEM_NORMAL = 1
|
||||
const val ITEM_MORE = 2
|
||||
const val ITEM_FOOTER = 3
|
||||
|
||||
fun createDiffCallback() = object : ItemCallback<ReservationGameItemData>() {
|
||||
override fun areItemsTheSame(oldItem: ReservationGameItemData, newItem: ReservationGameItemData): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(
|
||||
oldItem: ReservationGameItemData,
|
||||
newItem: ReservationGameItemData
|
||||
): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.databinding.DialogReservationBatchManageBinding
|
||||
|
||||
class ReservationBatchManageDialogFragment : BaseBottomDialogFragment<DialogReservationBatchManageBinding>() {
|
||||
private var onEnableWifiAutoDownloadClickListener: (() -> Unit)? = null
|
||||
private var onCancelReservationClickListener: (() -> Unit)? = null
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mBinding.enableWifiAutoDownloadTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onEnableWifiAutoDownloadClickListener?.invoke()
|
||||
}
|
||||
mBinding.cancelReservationTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onCancelReservationClickListener?.invoke()
|
||||
}
|
||||
mBinding.cancelTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun show(
|
||||
context: Context,
|
||||
onEnableWifiAutoDownloadClickListener: () -> Unit,
|
||||
onCancelReservationClickListener: () -> Unit,
|
||||
) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = ReservationBatchManageDialogFragment()
|
||||
fragment.onEnableWifiAutoDownloadClickListener = onEnableWifiAutoDownloadClickListener
|
||||
fragment.onCancelReservationClickListener = onCancelReservationClickListener
|
||||
fragment.show(it, fragment::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,167 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.gh.common.iinterface.IBatchManage
|
||||
import com.gh.common.util.CheckLoginUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.eventbus.EBReuse
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.mygame.MyReservationFragment
|
||||
import com.gh.gamecenter.personal.MyHaloBaseListFragment
|
||||
import com.gh.gamecenter.personal.MyHaloViewModel
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class ReservationFragment: MyHaloBaseListFragment(), IBatchManage {
|
||||
private val viewModel by viewModels<ReservationViewModel>()
|
||||
private val myHaloViewModel by activityViewModels<MyHaloViewModel>()
|
||||
private val reservationAdapter by lazy { ReservationAdapter(requireContext(), viewModel) }
|
||||
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
val data = reservationAdapter.getGameEntityByPackage(downloadEntity.packageName)
|
||||
for (gameAndPosition in data) {
|
||||
if (gameAndPosition.entity != null && gameAndPosition.entity.name == downloadEntity.name) {
|
||||
val entryMap = gameAndPosition.entity.getEntryMap()
|
||||
entryMap[downloadEntity.platform] = downloadEntity
|
||||
}
|
||||
reservationAdapter.notifyItemChanged(gameAndPosition.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
onLoadRefresh()
|
||||
observeData()
|
||||
|
||||
binding.listRv.run {
|
||||
itemAnimator = null
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = reservationAdapter
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentResume() {
|
||||
super.onFragmentResume()
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun onFragmentPause() {
|
||||
super.onFragmentPause()
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun provideLoadStatusLiveData(): LiveData<LoadStatus> = viewModel.loadStatus
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
super.onLoadEmpty()
|
||||
updateNoDataView()
|
||||
}
|
||||
|
||||
private fun updateNoDataView() {
|
||||
binding.reuseNoneData.run {
|
||||
if (!CheckLoginUtils.isLogin()) {
|
||||
reuseNoneDataTv.text = "登录后可查看预约游戏"
|
||||
reuseResetLoadTv.isVisible = true
|
||||
reuseResetLoadTv.text = "去登录"
|
||||
reuseResetLoadTv.setOnClickListener {
|
||||
CheckLoginUtils.checkLogin(requireContext(), "我的光环-预约") {}
|
||||
}
|
||||
} else {
|
||||
reuseNoneDataTv.text = com.gh.gamecenter.common.R.string.game_empty.toResString()
|
||||
reuseResetLoadTv.isVisible = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun observeData() {
|
||||
viewModel.itemDataListLiveData.observe(viewLifecycleOwner) {
|
||||
reservationAdapter.submitList(binding.listRv, it)
|
||||
}
|
||||
|
||||
viewModel.downloadableLoadStatus.observe(viewLifecycleOwner) {
|
||||
reservationAdapter.notifyFooterItemOnLoadStatusChanged(true, it)
|
||||
}
|
||||
|
||||
viewModel.reservableLoadStatus.observe(viewLifecycleOwner) {
|
||||
reservationAdapter.notifyFooterItemOnLoadStatusChanged(false, it)
|
||||
}
|
||||
|
||||
myHaloViewModel.managementStateLiveData.observe(viewLifecycleOwner) {
|
||||
if (isSupportVisible && !it) {
|
||||
reservationAdapter.changeStatus(MyReservationFragment.ReservePageState.Normal)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.enableAutoDownloadSuccessfully.observe(viewLifecycleOwner, EventObserver {
|
||||
if (it) {
|
||||
ToastUtils.showToast(R.string.has_enabled_for_auto_download_in_wifi.toResString())
|
||||
} else {
|
||||
ToastUtils.showToast(R.string.has_unable_for_auto_download_in_wifi.toResString())
|
||||
}
|
||||
})
|
||||
|
||||
viewModel.changePageStateAction.observe(viewLifecycleOwner, EventObserver {
|
||||
if (it == MyReservationFragment.ReservePageState.Normal && myHaloViewModel.managementStateLiveData.value == true) {
|
||||
myHaloViewModel.changeManagementState()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
if (CheckLoginUtils.isLogin()) {
|
||||
viewModel.initData()
|
||||
} else {
|
||||
onLoadEmpty()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onManageClick() {
|
||||
ReservationBatchManageDialogFragment.show(requireContext(), {
|
||||
reservationAdapter.changeStatus(MyReservationFragment.ReservePageState.EnableAutoDownload)
|
||||
binding.listRv.requestLayout()
|
||||
myHaloViewModel.changeManagementState()
|
||||
}, {
|
||||
reservationAdapter.changeStatus(MyReservationFragment.ReservePageState.CancelReserve)
|
||||
binding.listRv.requestLayout()
|
||||
myHaloViewModel.changeManagementState()
|
||||
})
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(reuse: EBReuse) {
|
||||
if (reuse.type == Constants.LOGIN_TAG || reuse.type == Constants.LOGOUT_TAG) {
|
||||
onLoadRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
//安装、卸载事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(busFour: EBPackage) {
|
||||
if (busFour.isInstalledOrUninstalled()) {
|
||||
reservationAdapter.notifyItemRangeChanged(0, reservationAdapter.itemCount)
|
||||
}
|
||||
}
|
||||
|
||||
//下载被删除事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(status: EBDownloadStatus) {
|
||||
if ("delete" == status.status) {
|
||||
reservationAdapter.notifyItemAndRemoveDownload(status)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,26 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
data class ReservationGameItemData(
|
||||
var gameEntity: GameEntity? = null,
|
||||
var isHead: Boolean = false,
|
||||
var title: String = "",
|
||||
var gameCount: Int = 0,
|
||||
var isMore: Boolean = false,
|
||||
var isFooter: Boolean = false,
|
||||
var isDownloadable: Boolean = false,
|
||||
var roundBottomOnly: Boolean = false,
|
||||
) {
|
||||
private val isGame
|
||||
get() = gameEntity != null
|
||||
|
||||
fun areItemsTheSame(other: ReservationGameItemData) =
|
||||
isHead == other.isHead && isFooter == other.isFooter && isMore == other.isMore && isGame == other.isGame
|
||||
|
||||
fun areContentsTheSame(other: ReservationGameItemData) = when {
|
||||
isHead -> title == other.title && gameCount == other.gameCount
|
||||
isGame -> gameEntity == other.gameEntity && roundBottomOnly == other.roundBottomOnly
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.core.view.isVisible
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.toResString
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.databinding.DialogReservationManageBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
class ReservationManageDialogFragment : BaseBottomDialogFragment<DialogReservationManageBinding>() {
|
||||
private var gameEntity: GameEntity? = null
|
||||
|
||||
private var onWifiAutoDownloadClickListener: ((Boolean) -> Unit)? = null
|
||||
private var onCancelReservationClickListener: (() -> Unit)? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
gameEntity = arguments?.getParcelable(EntranceConsts.KEY_GAME_ENTITY)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
gameEntity?.let { mBinding.gameIconView.displayGameIcon(it) }
|
||||
mBinding.gameNameTv.text = gameEntity?.name
|
||||
mBinding.wifiAutoDownloadTv.isVisible = gameEntity?.wifiAutoDownloadEnable == true
|
||||
mBinding.wifiAutoDownloadTv.text =
|
||||
if (gameEntity?.wifiAutoDownload == true) R.string.cancel_auto_download_with_wifi.toResString() else R.string.enable_automatic_downloading_with_wifi.toResString()
|
||||
mBinding.wifiAutoDownloadTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onWifiAutoDownloadClickListener?.invoke(gameEntity?.wifiAutoDownload == false)
|
||||
}
|
||||
mBinding.cancelReservationTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onCancelReservationClickListener?.invoke()
|
||||
}
|
||||
mBinding.cancelTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun show(
|
||||
context: Context,
|
||||
gameEntity: GameEntity,
|
||||
onWifiAutoDownloadClickListener: (Boolean) -> Unit,
|
||||
onCancelReservationClickListener: () -> Unit,
|
||||
) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = ReservationManageDialogFragment().apply {
|
||||
arguments = bundleOf(
|
||||
EntranceConsts.KEY_GAME_ENTITY to gameEntity
|
||||
)
|
||||
}
|
||||
fragment.onWifiAutoDownloadClickListener = onWifiAutoDownloadClickListener
|
||||
fragment.onCancelReservationClickListener = onCancelReservationClickListener
|
||||
fragment.show(it, fragment::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,43 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import com.gh.common.repository.ReservationRepository
|
||||
import com.gh.common.util.ReservationHelper
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.halo.assistant.fragment.reserve.ReserveReminderRepository
|
||||
import io.reactivex.Single
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class ReservationRepository {
|
||||
private val newApi = RetrofitManager.getInstance().newApi
|
||||
|
||||
private val reminderRepository by lazy { ReserveReminderRepository.newInstance() }
|
||||
|
||||
fun deleteReservation(gameId: String, game: GameEntity): Single<ResponseBody> {
|
||||
return newApi.deleteGameReservation(
|
||||
gameId,
|
||||
ReservationHelper.getReserveRequestBody(game, HaloApp.getInstance().application)
|
||||
)
|
||||
}
|
||||
|
||||
fun cancelReservation(gameId: String, game: GameEntity): Single<ResponseBody> {
|
||||
return newApi.cancelGameReservation(
|
||||
gameId,
|
||||
ReservationHelper.getReserveRequestBody(game, HaloApp.getInstance().application)
|
||||
)
|
||||
}
|
||||
|
||||
fun enableAutoDownloadInBatches(params: RequestBody): Single<ResponseBody> {
|
||||
return reminderRepository.enableAutoDownloadInBatches(params)
|
||||
}
|
||||
|
||||
fun cancelReserveInBatch(params: RequestBody): Single<ResponseBody> {
|
||||
return reminderRepository.cancelReserveInBatch(params)
|
||||
}
|
||||
|
||||
fun refreshReservations() {
|
||||
ReservationRepository.refreshReservations()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,307 @@
|
||||
package com.gh.gamecenter.personal.reservation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.utils.ApkActiveUtils
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.mygame.MyReservationFragment
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class ReservationViewModel(application: Application): AndroidViewModel(application) {
|
||||
private val api = RetrofitManager.getInstance().newApi
|
||||
|
||||
var isDownloadableExpand = false
|
||||
|
||||
private val downloadableTotal = AtomicInteger(0)
|
||||
private val reservableTotal = AtomicInteger(0)
|
||||
|
||||
private var downloadablePage = 1
|
||||
private var reservablePage = 1
|
||||
|
||||
// 记录地区屏蔽游戏数量
|
||||
private val downloadableRemovedGameCount = AtomicInteger(0)
|
||||
private val reservableRemovedGameCount = AtomicInteger(0)
|
||||
|
||||
private val isDownloadableFirstPage
|
||||
get() = downloadablePage == 1
|
||||
private val isReservableFirstPage
|
||||
get() = reservablePage == 1
|
||||
|
||||
val itemDataListLiveData = MutableLiveData<List<ReservationGameItemData>>()
|
||||
private val downloadableItemDataList = mutableListOf<ReservationGameItemData>()
|
||||
private val reservableItemDataList = mutableListOf<ReservationGameItemData>()
|
||||
|
||||
val downloadableLoadStatus = MutableLiveData<LoadStatus>()
|
||||
val reservableLoadStatus = MutableLiveData<LoadStatus>()
|
||||
val loadStatus = MediatorLiveData<LoadStatus>().apply {
|
||||
fun determineLoadStatus(source: LoadStatus?, other: LoadStatus?) = when {
|
||||
source == LoadStatus.INIT_FAILED && other == LoadStatus.INIT_FAILED -> LoadStatus.INIT_FAILED
|
||||
source == LoadStatus.INIT_EMPTY && other == LoadStatus.INIT_EMPTY -> LoadStatus.INIT_EMPTY
|
||||
else -> LoadStatus.INIT_LOADED
|
||||
}
|
||||
|
||||
addSource(downloadableLoadStatus) {
|
||||
val finalLoadStatus = determineLoadStatus(it, reservableLoadStatus.value)
|
||||
if (finalLoadStatus != value) value = finalLoadStatus
|
||||
}
|
||||
|
||||
addSource(reservableLoadStatus) {
|
||||
val finalLoadStatus = determineLoadStatus(it, downloadableLoadStatus.value)
|
||||
if (finalLoadStatus != value) value = finalLoadStatus
|
||||
}
|
||||
}
|
||||
|
||||
var positionAndPackageMap = HashMap<String, Int>() // key: packageName + position, value: position
|
||||
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private val manageReservationUseCase = ManageReservationUseCase<ReservationGameItemData>()
|
||||
|
||||
fun initData() {
|
||||
loadStatus.value = LoadStatus.INIT
|
||||
resetPageData()
|
||||
loadFirst()
|
||||
}
|
||||
|
||||
private fun resetPageData() {
|
||||
isDownloadableExpand = false
|
||||
downloadableTotal.set(0)
|
||||
reservableTotal.set(0)
|
||||
downloadablePage = 1
|
||||
reservablePage = 1
|
||||
downloadableRemovedGameCount.set(0)
|
||||
reservableRemovedGameCount.set(0)
|
||||
downloadableItemDataList.clear()
|
||||
reservableItemDataList.clear()
|
||||
}
|
||||
|
||||
private fun createRequest(
|
||||
status: String,
|
||||
page: Int,
|
||||
total: AtomicInteger,
|
||||
removedCount: AtomicInteger,
|
||||
itemDataList: MutableList<ReservationGameItemData>
|
||||
): Single<List<GameEntity>> = api.getMyHaloReservation(status, page, PAGE_SIZE, Utils.getTime(getApplication()))
|
||||
.subscribeOn(Schedulers.io())
|
||||
.map {
|
||||
total.set(it.headers()["Total"]?.toInt() ?: 0)
|
||||
it.body()
|
||||
}
|
||||
.map {
|
||||
val filterGameList = RegionSettingHelper.filterGame(it)
|
||||
removedCount.addAndGet(it.size - filterGameList.size)
|
||||
total.addAndGet(-removedCount.get())
|
||||
filterGameList
|
||||
}
|
||||
.map(ApkActiveUtils.filterMapperList)
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.doOnSuccess { gameList ->
|
||||
itemDataList.addAll(gameList.map {
|
||||
ReservationGameItemData(
|
||||
gameEntity = it,
|
||||
isDownloadable = status == STATUS_DOWNLOAD
|
||||
)
|
||||
})
|
||||
updateLoadStatus(status, gameList.size)
|
||||
}
|
||||
.doOnError {
|
||||
updateLoadStatus(status, FAILURE_SIZE)
|
||||
}
|
||||
.onErrorReturn { emptyList() }
|
||||
|
||||
private val downloadableRequest
|
||||
get() = createRequest(STATUS_DOWNLOAD, downloadablePage, downloadableTotal, downloadableRemovedGameCount, downloadableItemDataList)
|
||||
|
||||
private val reservableRequest
|
||||
get() = createRequest(STATUS_RESERVATION, reservablePage, reservableTotal, reservableRemovedGameCount, reservableItemDataList)
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun loadFirst() {
|
||||
Single.zip(downloadableRequest, reservableRequest) { _, _ -> }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({
|
||||
decorateListAndPost()
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadDownloadableGame() {
|
||||
downloadableRequest
|
||||
.subscribe({
|
||||
decorateListAndPost()
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadReservableGame() {
|
||||
reservableRequest
|
||||
.subscribe({
|
||||
decorateListAndPost()
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
fun onDownloadableLoadMore() {
|
||||
if (isDownloadableExpand && downloadableLoadStatus.value != LoadStatus.LIST_OVER) {
|
||||
downloadableLoadStatus.value = LoadStatus.LIST_LOADING
|
||||
loadDownloadableGame()
|
||||
}
|
||||
}
|
||||
|
||||
fun onReservableLoadMore() {
|
||||
if (reservableLoadStatus.value != LoadStatus.LIST_OVER) {
|
||||
reservableLoadStatus.value = LoadStatus.LIST_LOADING
|
||||
loadReservableGame()
|
||||
}
|
||||
}
|
||||
|
||||
fun onMoreClick() {
|
||||
isDownloadableExpand = !isDownloadableExpand
|
||||
decorateListAndPost()
|
||||
}
|
||||
|
||||
private fun updateLoadStatus(status: String, size: Int) {
|
||||
val loadStatusLiveData = if (status == STATUS_DOWNLOAD) downloadableLoadStatus else reservableLoadStatus
|
||||
val isFirstPage = if (status == STATUS_DOWNLOAD) isDownloadableFirstPage else isReservableFirstPage
|
||||
if (size == 0) {
|
||||
loadStatusLiveData.value = if (isFirstPage) LoadStatus.INIT_EMPTY else LoadStatus.LIST_OVER
|
||||
} else if (size == FAILURE_SIZE) {
|
||||
loadStatusLiveData.value = if (isFirstPage) LoadStatus.INIT_FAILED else LoadStatus.LIST_FAILED
|
||||
} else {
|
||||
loadStatusLiveData.value = if (isFirstPage) LoadStatus.INIT_LOADED else LoadStatus.LIST_LOADED
|
||||
if (status == STATUS_DOWNLOAD) {
|
||||
downloadablePage++
|
||||
} else {
|
||||
reservablePage++
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun decorateListAndPost() {
|
||||
val itemDataList = mutableListOf<ReservationGameItemData>()
|
||||
if (downloadableItemDataList.isNotEmpty()) {
|
||||
itemDataList.add(ReservationGameItemData(isHead = true, title = DOWNLOADABLE_TITLE, gameCount = downloadableTotal.get(), isDownloadable = true))
|
||||
if (isDownloadableExpand) {
|
||||
itemDataList.addAll(downloadableItemDataList)
|
||||
itemDataList.add(ReservationGameItemData(isMore = true, roundBottomOnly = true))
|
||||
if (downloadableLoadStatus.value != LoadStatus.LIST_OVER || reservableItemDataList.isEmpty()) {
|
||||
itemDataList.add(ReservationGameItemData(isFooter = true, isDownloadable = true))
|
||||
}
|
||||
} else {
|
||||
itemDataList.add(downloadableItemDataList.first())
|
||||
itemDataList.add(ReservationGameItemData(isMore = true, roundBottomOnly = true))
|
||||
}
|
||||
}
|
||||
|
||||
if (reservableItemDataList.isNotEmpty()) {
|
||||
itemDataList.add(ReservationGameItemData(isHead = true, title = RESERVABLE_TITLE, gameCount = reservableTotal.get(), isDownloadable = false))
|
||||
itemDataList.addAll(reservableItemDataList.onEachIndexed { index, itemData ->
|
||||
itemData.roundBottomOnly = index == reservableItemDataList.size - 1
|
||||
})
|
||||
itemDataList.add(ReservationGameItemData(isFooter = true, isDownloadable = false))
|
||||
}
|
||||
|
||||
positionAndPackageMap.clear()
|
||||
itemDataList.forEachIndexed { index, itemData ->
|
||||
itemData.gameEntity?.let {
|
||||
addGamePositionAndPackage(it, index)
|
||||
}
|
||||
}
|
||||
|
||||
itemDataListLiveData.postValue(itemDataList)
|
||||
}
|
||||
|
||||
private fun addGamePositionAndPackage(game: GameEntity, position: Int) {
|
||||
var packages = ""
|
||||
for (apkEntity in game.getApk()) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
packages += game.simulator?.apk?.packageName
|
||||
positionAndPackageMap[packages + position] = position
|
||||
game.setEntryMap(DownloadManager.getInstance().getEntryMap(game.name))
|
||||
}
|
||||
|
||||
fun cancelReservation(game: GameEntity) {
|
||||
manageReservationUseCase.deleteOrCancelReservation(game, false) {
|
||||
downloadableItemDataList.removeAll { game.id == it.gameEntity?.id }
|
||||
reservableItemDataList.removeAll { game.id == it.gameEntity?.id }
|
||||
decorateListAndPost()
|
||||
}
|
||||
}
|
||||
|
||||
val enableAutoDownloadSuccessfully: LiveData<Event<Boolean>> = manageReservationUseCase.enableAutoDownloadSuccessfully
|
||||
fun enableAutoDownload(enable: Boolean, games: Set<GameEntity>) {
|
||||
manageReservationUseCase.enableAutoDownload(enable, games, {
|
||||
val gameIds = games.map { it.id }.toSet()
|
||||
val updateFn: (ReservationGameItemData) -> (ReservationGameItemData) = { item ->
|
||||
item.gameEntity?.takeIf { it.id in gameIds }?.let { game ->
|
||||
item.copy(gameEntity = game.copy(_wifiAutoDownload = enable))
|
||||
} ?: item
|
||||
}
|
||||
updateListItems(downloadableItemDataList, updateFn)
|
||||
updateListItems(reservableItemDataList, updateFn)
|
||||
itemDataListLiveData.value?.map(updateFn) ?: emptyList()
|
||||
}) { data ->
|
||||
itemDataListLiveData.postValue(data)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateListItems(
|
||||
list: MutableList<ReservationGameItemData>,
|
||||
updateFn: (ReservationGameItemData) -> ReservationGameItemData
|
||||
) {
|
||||
val iterator = list.listIterator()
|
||||
while (iterator.hasNext()) {
|
||||
val item = iterator.next()
|
||||
iterator.set(updateFn(item))
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelReserveInBatch(games: Set<GameEntity>) {
|
||||
manageReservationUseCase.cancelReserveInBatch(games) {
|
||||
downloadableItemDataList.removeAll { games.any { game -> game.id == it.gameEntity?.id } }
|
||||
reservableItemDataList.removeAll { games.any { game -> game.id == it.gameEntity?.id } }
|
||||
decorateListAndPost()
|
||||
}
|
||||
}
|
||||
|
||||
private val _changePageStateAction = MutableLiveData<Event<MyReservationFragment.ReservePageState>>()
|
||||
val changePageStateAction: LiveData<Event<MyReservationFragment.ReservePageState>> = _changePageStateAction
|
||||
fun changeReserveStatus(state: MyReservationFragment.ReservePageState) {
|
||||
_changePageStateAction.value = Event(state)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
manageReservationUseCase.onCleared()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_SIZE = 20
|
||||
const val FAILURE_SIZE = -1
|
||||
const val STATUS_DOWNLOAD = "download"
|
||||
const val STATUS_RESERVATION = "appointment"
|
||||
|
||||
const val DOWNLOADABLE_TITLE = "可下载"
|
||||
const val RESERVABLE_TITLE = "即将上线"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,531 @@
|
||||
package com.gh.gamecenter.personal.vgame
|
||||
|
||||
import android.content.Context
|
||||
import android.view.Gravity
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.DiffUtil.ItemCallback
|
||||
import androidx.recyclerview.widget.ListAdapter
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.NewFlatLogUtils
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.view.DrawableView
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.SpeedUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.databinding.ItemInstalledVgameSquareBinding
|
||||
import com.gh.gamecenter.databinding.PopupHistoryOptionBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.provider.IBindingAdaptersProvider
|
||||
import com.gh.gamecenter.feature.view.DownloadButton
|
||||
import com.gh.gamecenter.history.ManageOption
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.personal.installed.InstalledAdapter
|
||||
import com.gh.gamecenter.personal.installed.InstalledAdapter.InstalledGameItemViewHolder
|
||||
import com.gh.gamecenter.personal.installed.InstalledGameItemData
|
||||
import com.gh.vspace.VHelper
|
||||
import com.gh.vspace.shortcut.OnCreateShortcutResult
|
||||
import com.gh.vspace.shortcut.ShortcutManager
|
||||
import com.gh.vspace.shortcut.ShortcutPermissionDialog
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import com.lightgame.utils.Utils
|
||||
import com.muugi.shortcut.core.Executor
|
||||
import com.therouter.TheRouter
|
||||
import org.json.JSONArray
|
||||
|
||||
class InstalledVGameAdapter(private val viewModel: InstalledVGameViewModel): ListAdapter<InstalledGameItemData, RecyclerView.ViewHolder>(createDiffCallback()) {
|
||||
private var selectItems = arrayListOf<String>()
|
||||
private var popWindow: PopupWindow? = null
|
||||
private var currentOption = ManageOption.OPTION_MANAGER
|
||||
private var popupBinding: PopupHistoryOptionBinding? = null
|
||||
private val positionAndPackageMap = HashMap<String, Int>()
|
||||
|
||||
override fun submitList(list: List<InstalledGameItemData>?) {
|
||||
positionAndPackageMap.clear()
|
||||
// 记录游戏位置
|
||||
list?.forEachIndexed { i, item ->
|
||||
item.gameEntity?.let { gameEntity ->
|
||||
val key = buildString {
|
||||
append(gameEntity.id)
|
||||
gameEntity.getApk().forEach { apk ->
|
||||
append(apk.packageName)
|
||||
}
|
||||
append(i)
|
||||
}
|
||||
positionAndPackageMap[key] = i
|
||||
}
|
||||
}
|
||||
|
||||
super.submitList(list)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val itemData = currentList.getOrNull(position) ?: return ITEM_NORMAL
|
||||
return if (itemData.isHead) ITEM_HEAD else if (itemData.isFooter) ITEM_FOOTER else ITEM_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
when (viewType) {
|
||||
ITEM_HEAD -> {
|
||||
InstalledVGameHeadItemViewHolder(parent.toBinding())
|
||||
}
|
||||
ITEM_FOOTER -> {
|
||||
FooterViewHolder(parent.layoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
else -> {
|
||||
InstalledGameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
val itemData = currentList.getOrNull(position) ?: return
|
||||
if (holder is InstalledVGameHeadItemViewHolder) {
|
||||
holder.binding.run {
|
||||
root.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(root.context))
|
||||
container.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
root.setOnClickListener {
|
||||
VHelper.startVSpaceSquare(root.context)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is InstalledGameItemViewHolder) {
|
||||
val gameEntity = itemData.gameEntity ?: return
|
||||
holder.binding.run {
|
||||
root.setCardBackgroundColor(com.gh.gamecenter.common.R.color.ui_surface.toColor(root.context))
|
||||
myHaloGame.run {
|
||||
multiVersionContainer.isVisible = false
|
||||
selectIv.setImageDrawable(DrawableView.getCheckSelectorDrawable(root.context))
|
||||
root.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
selectIv.goneIf(currentOption == ManageOption.OPTION_MANAGER)
|
||||
selectIv.isChecked = selectItems.contains(gameEntity.id)
|
||||
|
||||
gameInfo.run {
|
||||
wifiAutoDownloadTv.isVisible = false
|
||||
tvSpeed.isVisible = false
|
||||
arrowIv.isVisible = false
|
||||
zoneDivider.isVisible = false
|
||||
tagTv.isVisible = false
|
||||
gameNameTv.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(root.context))
|
||||
val provider = TheRouter.get(IBindingAdaptersProvider::class.java)
|
||||
provider?.setGameName(gameNameTv, gameEntity, false)
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
|
||||
moreIv.isVisible = currentOption == ManageOption.OPTION_MANAGER
|
||||
moreIv.setOnClickListener {
|
||||
InstalledVGameMoreDialogFragment.show(
|
||||
root.context,
|
||||
gameEntity,
|
||||
onAddIconToLauncherClickListener = {
|
||||
addShortcutToLauncher(root.context, gameEntity)
|
||||
}, onClearGameDataClickListener = {
|
||||
showClearGameDataDialog(root.context, gameEntity)
|
||||
}, onDeleteGameClickListener = {
|
||||
showDelGameDialog(root.context, gameEntity)
|
||||
})
|
||||
}
|
||||
|
||||
gameDesTv.text = gameEntity.des
|
||||
|
||||
val entityFromDownloadManager = DownloadManager.getInstance().getDownloadEntitySnapshot(gameEntity)
|
||||
val entityFromVGame = VHelper.getVDownloadEntitySnapshot(gameEntity.id, gameEntity.getUniquePackageName())
|
||||
|
||||
val downloadEntity =
|
||||
if (entityFromDownloadManager != null && entityFromDownloadManager.status != DownloadStatus.cancel) {
|
||||
entityFromDownloadManager
|
||||
} else {
|
||||
entityFromVGame
|
||||
}
|
||||
updateDownloadBtnAndDes(root.context, downloadEntity, downloadBtn, gameDesTv, gameEntity) {
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
true,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
holder.bindingAdapterPosition,
|
||||
profile = "按钮",
|
||||
text = downloadBtn.text
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InstalledAdapter.bindAnnouncement(InstalledAdapter.TYPE_V_GAME, this, gameEntity, holder.bindingAdapterPosition)
|
||||
InstalledAdapter.bindService(InstalledAdapter.TYPE_V_GAME, this, gameEntity, holder.bindingAdapterPosition, "我的光环-畅玩")
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
if (currentOption == ManageOption.OPTION_MANAGER) {
|
||||
GameDetailActivity.startGameDetailActivity(root.context, gameEntity.id, "我的光环-畅玩")
|
||||
SensorsBridge.trackHaloSelfGameInstalledClick(
|
||||
true,
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
gameEntity.categoryChinese,
|
||||
holder.bindingAdapterPosition,
|
||||
profile = "游戏",
|
||||
text = "游戏"
|
||||
)
|
||||
} else {
|
||||
if (selectItems.contains(gameEntity.id)) {
|
||||
selectItems.remove(gameEntity.id)
|
||||
} else {
|
||||
selectItems.add(gameEntity.id)
|
||||
}
|
||||
checkSelectItems()
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is FooterViewHolder) {
|
||||
holder.initFooterViewHolder(false, false, true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateDownloadBtnAndDes(
|
||||
context: Context,
|
||||
downloadEntity: DownloadEntity?,
|
||||
downloadBtn: DownloadButton,
|
||||
downloadDes: TextView,
|
||||
gameEntity: GameEntity,
|
||||
clickCallback: () -> Unit
|
||||
) {
|
||||
// 青少年模式显示查看
|
||||
if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) {
|
||||
downloadBtn.text = "查看"
|
||||
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.TEENAGER_MODE
|
||||
return
|
||||
}
|
||||
|
||||
downloadBtn.goneIf(currentOption != ManageOption.OPTION_MANAGER)
|
||||
|
||||
if (downloadEntity != null) {
|
||||
val status = downloadEntity.status
|
||||
var btnText = context.getText(com.gh.gamecenter.feature.R.string.downloading)
|
||||
var backgroundType = DownloadButton.ButtonStyle.NORMAL
|
||||
|
||||
downloadBtn.apply {
|
||||
when (status) {
|
||||
DownloadStatus.downloading -> {
|
||||
btnText = "${downloadEntity.percent}%"
|
||||
downloadDes.visibility = View.VISIBLE
|
||||
val speedText = SpeedUtils.getSpeed(downloadEntity.speed)
|
||||
val remainTimeText = SpeedUtils.getRemainTime(
|
||||
downloadEntity.size,
|
||||
downloadEntity.progress,
|
||||
downloadEntity.speed * 1024
|
||||
)
|
||||
downloadDes.text = "$speedText(剩$remainTimeText)"
|
||||
downloadDes.setTextColor(com.gh.gamecenter.common.R.color.text_theme.toColor(context))
|
||||
backgroundType = DownloadButton.ButtonStyle.DOWNLOADING_NORMAL
|
||||
progress = (downloadEntity.percent * 10).toInt()
|
||||
setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
DownloadManager.getInstance().pause(downloadEntity.url)
|
||||
}
|
||||
}
|
||||
DownloadStatus.waiting -> {
|
||||
btnText = context.getString(com.gh.gamecenter.feature.R.string.waiting)
|
||||
backgroundType = DownloadButton.ButtonStyle.WAITING
|
||||
downloadDes.visibility = View.VISIBLE
|
||||
downloadDes.text = context.getString(com.gh.gamecenter.feature.R.string.waiting)
|
||||
downloadDes.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
Utils.toast(context, "最多只能同时下载三个任务,请稍等")
|
||||
}
|
||||
}
|
||||
DownloadStatus.pause,
|
||||
DownloadStatus.timeout,
|
||||
DownloadStatus.neterror,
|
||||
DownloadStatus.subscribe,
|
||||
DownloadStatus.diskioerror,
|
||||
DownloadStatus.diskisfull,
|
||||
DownloadStatus.overflow -> {
|
||||
btnText = context.getString(com.gh.gamecenter.feature.R.string.resume)
|
||||
downloadDes.visibility = View.VISIBLE
|
||||
downloadDes.text = if (status == DownloadStatus.subscribe) "等待WIFI" else "已暂停"
|
||||
downloadDes.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(context))
|
||||
setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
DownloadManager.getInstance().resume(downloadEntity, false)
|
||||
}
|
||||
}
|
||||
DownloadStatus.done -> {
|
||||
if (PackagesManager.isCanUpdate(
|
||||
gameId = gameEntity.id,
|
||||
packageName = gameEntity.getApk().firstOrNull()?.packageName,
|
||||
asVGame = true
|
||||
)
|
||||
) {
|
||||
btnText = context.getString(com.gh.gamecenter.feature.R.string.update)
|
||||
setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
PackagesManager.getUpdateList()
|
||||
.firstOrNull { it.id == downloadEntity.gameId }?.let {
|
||||
VHelper.updateOrReDownload(downloadEntity, it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
btnText = context.getString(com.gh.gamecenter.feature.R.string.launch)
|
||||
setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
CurrentActivityHolder.getCurrentActivity()?.let {
|
||||
VHelper.installOrLaunch(it, downloadEntity, "畅玩管理")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
// do nothing
|
||||
}
|
||||
}
|
||||
|
||||
text = btnText.toString()
|
||||
buttonStyle = backgroundType
|
||||
}
|
||||
} else {
|
||||
downloadBtn.buttonStyle = DownloadButton.ButtonStyle.NORMAL
|
||||
downloadBtn.text = "下载"
|
||||
downloadBtn.setOnClickListener {
|
||||
clickCallback.invoke()
|
||||
ToastUtils.toast("不应该出现状态为'下载'的按钮")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeOption(context: Context, option: ManageOption) {
|
||||
currentOption = option
|
||||
when (currentOption) {
|
||||
ManageOption.OPTION_MANAGER -> {
|
||||
selectItems.clear()
|
||||
popWindow?.dismiss()
|
||||
popWindow = null
|
||||
}
|
||||
else -> {
|
||||
if (popWindow == null || popWindow?.isShowing == false) {
|
||||
showOptionWindow(context)
|
||||
}
|
||||
}
|
||||
}
|
||||
notifyItemRangeChanged(0, currentList.size)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示添加图标到桌面的对话框
|
||||
*/
|
||||
private fun addShortcutToLauncher(context: Context, gameEntity: GameEntity) {
|
||||
ShortcutManager.getInstance()
|
||||
.tryCreateShortCut(
|
||||
context, gameEntity,
|
||||
object : OnCreateShortcutResult {
|
||||
override fun showPermissionDialog(executor: Executor?) {
|
||||
NewFlatLogUtils.logHaloFunShortcutPermissionDialogShow(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: ""
|
||||
)
|
||||
ShortcutPermissionDialog(context, gameEntity, executor).show()
|
||||
}
|
||||
|
||||
override fun exist() {
|
||||
ToastUtils.showToast(context.getString(R.string.shortcut_exist))
|
||||
}
|
||||
|
||||
override fun success() {
|
||||
ToastUtils.showToast(context.getString(R.string.shortcut_create_success))
|
||||
}
|
||||
|
||||
override fun failed() {
|
||||
ToastUtils.showToast(context.getString(R.string.shortcut_create_failed))
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示清除游戏数据对话框
|
||||
*/
|
||||
private fun showClearGameDataDialog(context: Context, gameEntity: GameEntity) {
|
||||
NewFlatLogUtils.logHaloFunCleanDataDialogShow(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: ""
|
||||
)
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
"提示",
|
||||
"清除后游戏的所有记录都将被清空,无法恢复!您确定要清除吗?",
|
||||
"确定",
|
||||
"取消",
|
||||
confirmClickCallback = { //清除游戏数据
|
||||
viewModel.cleanVGameData(gameEntity.getUniquePackageName())
|
||||
NewFlatLogUtils.logHaloFunCleanDataDialogButtonClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
"确定"
|
||||
)
|
||||
},
|
||||
cancelClickCallback = {
|
||||
NewFlatLogUtils.logHaloFunCleanDataDialogButtonClick(
|
||||
gameEntity.id,
|
||||
gameEntity.name ?: "",
|
||||
"取消"
|
||||
)
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true)
|
||||
)
|
||||
}
|
||||
|
||||
/**
|
||||
* 显示删除游戏的对话框
|
||||
*/
|
||||
private fun showDelGameDialog(
|
||||
context: Context,
|
||||
gameEntity: GameEntity
|
||||
) {
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
"删除游戏",
|
||||
"单机类游戏被删除将可能导致本地存档、充值数据丢失,请确认后操作(网游类游戏删除不会影响游戏存档和充值数据)",
|
||||
"再等等",
|
||||
"删除",
|
||||
{},
|
||||
{
|
||||
//删除对应的桌面快捷方式
|
||||
ShortcutManager.getInstance().removeShortcut(context, gameEntity)
|
||||
runOnIoThread {
|
||||
val apk = gameEntity.getApk().firstOrNull()
|
||||
viewModel.deleteVGame(apk?.url, apk?.packageName)
|
||||
|
||||
AppExecutor.uiExecutor.executeWithDelay({
|
||||
viewModel.refresh()
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
uiModificationCallback = {
|
||||
it.cancelTv.setTextColor(com.gh.gamecenter.common.R.color.secondary_red.toColor(it.root.context))
|
||||
it.confirmTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(it.root.context))
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun showOptionWindow(context: Context) {
|
||||
popupBinding = PopupHistoryOptionBinding.inflate(LayoutInflater.from(context))
|
||||
popupBinding?.root?.isFocusable = true
|
||||
popupBinding?.root?.isFocusableInTouchMode = true
|
||||
|
||||
popWindow =
|
||||
PopupWindow(popupBinding?.root, LinearLayout.LayoutParams.MATCH_PARENT, 56F.dip2px())
|
||||
popWindow?.showAtLocation(
|
||||
(context as AppCompatActivity).window.decorView, Gravity.BOTTOM, 0, 0
|
||||
)
|
||||
|
||||
popupBinding?.itemDelete?.setOnClickListener {
|
||||
NewFlatLogUtils.logHaloFunEvent("halo_fun_manage_game_delete_dialog_show")
|
||||
DialogHelper.showDialog(
|
||||
context,
|
||||
"删除游戏",
|
||||
"单机类游戏被删除将可能导致本地存档、充值数据丢失,请确认后操作(网游类游戏删除不会影响游戏存档和充值数据)",
|
||||
"再等等",
|
||||
"删除",
|
||||
{
|
||||
NewFlatLogUtils.logHaloFunManageGameDeleteDialogClick("再看看", JSONArray(selectItems))
|
||||
},
|
||||
{
|
||||
NewFlatLogUtils.logHaloFunManageGameDeleteDialogClick("删除", JSONArray(selectItems))
|
||||
|
||||
val selectItemCopy = ArrayList(selectItems)
|
||||
|
||||
selectItems.clear()
|
||||
checkSelectItems()
|
||||
changeOption(context, ManageOption.OPTION_MANAGER)
|
||||
|
||||
runOnIoThread {
|
||||
viewModel.deleteVGames(selectItemCopy)
|
||||
AppExecutor.uiExecutor.executeWithDelay({
|
||||
viewModel.refresh()
|
||||
}, 200)
|
||||
}
|
||||
},
|
||||
uiModificationCallback = {
|
||||
it.cancelTv.setTextColor(com.gh.gamecenter.common.R.color.secondary_red.toColor(it.root.context))
|
||||
it.confirmTv.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(it.root.context))
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true)
|
||||
)
|
||||
}
|
||||
popupBinding?.checkAllCb?.setCompoundDrawablesWithIntrinsicBounds(
|
||||
DrawableView.getCheckSelectorDrawable(
|
||||
context
|
||||
), null, null, null
|
||||
)
|
||||
popupBinding?.checkAllCb?.setOnClickListener {
|
||||
if (popupBinding?.checkAllCb?.isChecked == true) {
|
||||
selectItems.clear()
|
||||
selectItems.addAll(currentList.mapNotNull { it.gameEntity?.id }.toList())
|
||||
} else {
|
||||
selectItems.clear()
|
||||
}
|
||||
checkSelectItems()
|
||||
notifyItemRangeChanged(0, currentList.size)
|
||||
}
|
||||
checkSelectItems()
|
||||
}
|
||||
|
||||
private fun checkSelectItems() {
|
||||
popupBinding?.run {
|
||||
selectNumTv.text = if (selectItems.isEmpty()) "" else "(${selectItems.size})"
|
||||
itemDelete.background = if (selectItems.isEmpty()) com.gh.gamecenter.feature.R.drawable.button_round_gray_light.toDrawable(root.context) else com.gh.gamecenter.common.R.drawable.download_button_normal_style.toDrawable(root.context)
|
||||
itemDelete.setTextColor(if (selectItems.isEmpty()) com.gh.gamecenter.common.R.color.text_secondary.toColor(root.context) else com.gh.gamecenter.common.R.color.white.toColor(root.context))
|
||||
itemDelete.isEnabled = selectItems.isNotEmpty()
|
||||
checkAllCb.isChecked = selectItems.isNotEmpty() && selectItems.size == currentList.size - 2
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemByDownload(download: DownloadEntity) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(download.packageName) && key.contains(download.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && position < currentList.size) {
|
||||
currentList[position].gameEntity?.getEntryMap()?.set(download.platform, download)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class InstalledVGameHeadItemViewHolder(val binding: ItemInstalledVgameSquareBinding): RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
const val ITEM_HEAD = 0
|
||||
const val ITEM_NORMAL = 1
|
||||
const val ITEM_FOOTER = 2
|
||||
|
||||
fun createDiffCallback() = object : ItemCallback<InstalledGameItemData>() {
|
||||
override fun areItemsTheSame(oldItem: InstalledGameItemData, newItem: InstalledGameItemData): Boolean {
|
||||
return oldItem.areItemsTheSame(newItem)
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: InstalledGameItemData, newItem: InstalledGameItemData): Boolean {
|
||||
return oldItem.areContentsTheSame(newItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.gh.gamecenter.personal.vgame
|
||||
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.fragment.app.activityViewModels
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.lifecycle.repeatOnLifecycle
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.gh.common.iinterface.IBatchManage
|
||||
import com.gh.common.iinterface.IInstalledSortType
|
||||
import com.gh.common.iinterface.InstalledSortType
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.history.ManageOption
|
||||
import com.gh.gamecenter.livedata.Event
|
||||
import com.gh.gamecenter.personal.MyHaloBaseListFragment
|
||||
import com.gh.gamecenter.personal.MyHaloViewModel
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import kotlinx.coroutines.flow.catch
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class InstalledVGameFragment: MyHaloBaseListFragment(), IInstalledSortType, IBatchManage {
|
||||
private val viewModel by viewModels<InstalledVGameViewModel>()
|
||||
private val myHaloViewModel by activityViewModels<MyHaloViewModel>()
|
||||
private val installedVGameAdapter by lazy { InstalledVGameAdapter(viewModel) }
|
||||
|
||||
private val dataWatcher: DataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
installedVGameAdapter.notifyItemByDownload(downloadEntity)
|
||||
}
|
||||
|
||||
override fun onDataInit(downloadEntity: DownloadEntity) {
|
||||
installedVGameAdapter.notifyItemByDownload(downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
binding.run {
|
||||
listRv.run {
|
||||
itemAnimator = null
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = installedVGameAdapter
|
||||
}
|
||||
}
|
||||
|
||||
observeData()
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewLifecycleOwner.lifecycle.repeatOnLifecycle(Lifecycle.State.STARTED) {
|
||||
viewModel.vGames
|
||||
.catch {
|
||||
ToastUtils.showToast(it.message ?: "")
|
||||
}
|
||||
.collectLatest {
|
||||
viewModel.loadData(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideLoadStatusLiveData(): LiveData<LoadStatus> = viewModel.loadStatusLiveData
|
||||
|
||||
private fun observeData() {
|
||||
myHaloViewModel.managementStateLiveData.observe(viewLifecycleOwner) {
|
||||
if (isSupportVisible) {
|
||||
installedVGameAdapter.changeOption(requireContext(), if (it) ManageOption.OPTION_CANCEL_SELECT else ManageOption.OPTION_MANAGER)
|
||||
}
|
||||
}
|
||||
|
||||
viewModel.itemDataListLiveData.observe(viewLifecycleOwner) {
|
||||
installedVGameAdapter.submitList(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentResume() {
|
||||
super.onFragmentResume()
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
myHaloViewModel.sortTypeLiveData.postValue(Event(getCurrentSortType().des))
|
||||
}
|
||||
|
||||
override fun onFragmentPause() {
|
||||
super.onFragmentPause()
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun getCurrentSortType(): InstalledSortType = viewModel.sortedType
|
||||
|
||||
override fun changeSortType(sortType: InstalledSortType) {
|
||||
viewModel.sortedType = sortType
|
||||
}
|
||||
|
||||
override fun onManageClick() {
|
||||
myHaloViewModel.changeManagementState()
|
||||
}
|
||||
|
||||
override fun refresh() {
|
||||
viewModel.refresh()
|
||||
}
|
||||
|
||||
override fun showEmpty(isGranted: Boolean) {
|
||||
super.showEmpty(isGranted)
|
||||
binding.listRv.isVisible = true
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,71 @@
|
||||
package com.gh.gamecenter.personal.vgame
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.databinding.DialogInstalledVgameMoreBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
class InstalledVGameMoreDialogFragment : BaseBottomDialogFragment<DialogInstalledVgameMoreBinding>() {
|
||||
private var gameEntity: GameEntity? = null
|
||||
private var onAddIconToLauncherClickListener: (() -> Unit)? = null
|
||||
private var onClearGameDataClickListener: (() -> Unit)? = null
|
||||
private var onDeleteGameClickListener: (() -> Unit)? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
gameEntity = arguments?.getParcelable(EntranceConsts.KEY_GAME_ENTITY)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
gameEntity?.let { mBinding.gameIconView.displayGameIcon(it) }
|
||||
mBinding.gameNameTv.text = gameEntity?.name
|
||||
mBinding.addIconToDesktopTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onAddIconToLauncherClickListener?.invoke()
|
||||
}
|
||||
mBinding.cleanGameDataTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onClearGameDataClickListener?.invoke()
|
||||
}
|
||||
mBinding.deleteGameTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onDeleteGameClickListener?.invoke()
|
||||
}
|
||||
mBinding.cancelTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun show(
|
||||
context: Context,
|
||||
gameEntity: GameEntity,
|
||||
onAddIconToLauncherClickListener: () -> Unit,
|
||||
onClearGameDataClickListener: () -> Unit,
|
||||
onDeleteGameClickListener: () -> Unit,
|
||||
) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = InstalledVGameMoreDialogFragment().apply {
|
||||
arguments = bundleOf(
|
||||
EntranceConsts.KEY_GAME_ENTITY to gameEntity
|
||||
)
|
||||
}
|
||||
fragment.onAddIconToLauncherClickListener = onAddIconToLauncherClickListener
|
||||
fragment.onClearGameDataClickListener = onClearGameDataClickListener
|
||||
fragment.onDeleteGameClickListener = onDeleteGameClickListener
|
||||
fragment.show(it, fragment::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,240 @@
|
||||
package com.gh.gamecenter.personal.vgame
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.annotation.WorkerThread
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.iinterface.InstalledSortType
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.download.PackageObserver
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.common.utils.toProperReadableSize
|
||||
import com.gh.gamecenter.common.utils.toRequestBody
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.core.runOnUiThread
|
||||
import com.gh.gamecenter.core.utils.NumberUtils
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.core.utils.ToastUtils
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.ApkEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.utils.ApkActiveUtils
|
||||
import com.gh.gamecenter.personal.installed.InstalledGameItemData
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.vspace.VHelper
|
||||
import com.gh.vspace.db.VGameEntity
|
||||
import com.lightgame.download.DownloadStatus
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.subjects.PublishSubject
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
class InstalledVGameViewModel(application: Application): AndroidViewModel(application) {
|
||||
private val newApi = RetrofitManager.getInstance().newApi
|
||||
|
||||
val itemDataListLiveData = MutableLiveData<List<InstalledGameItemData>>()
|
||||
val loadStatusLiveData = MutableLiveData<LoadStatus>()
|
||||
|
||||
var sortedType = InstalledSortType.valueOf(
|
||||
SPUtils.getString(
|
||||
Constants.SP_MY_HALO_INSTALLED_V_GAME_ORDER,
|
||||
InstalledSortType.RECENTLY_PLAYED.name
|
||||
) ?: InstalledSortType.RECENTLY_PLAYED.name
|
||||
)
|
||||
set(value) {
|
||||
if (field != value) {
|
||||
field = value
|
||||
changeSortedType()
|
||||
SPUtils.setString(Constants.SP_MY_HALO_INSTALLED_V_GAME_ORDER, value.name)
|
||||
}
|
||||
}
|
||||
|
||||
private val sortedItemDataList = ArrayList<InstalledGameItemData>()
|
||||
|
||||
val vGames = VHelper.vGameDao.getAllGames()
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
private var loadPublishSubject: PublishSubject<List<VGameEntity>> = PublishSubject.create()
|
||||
|
||||
init {
|
||||
loadPublishSubject.debounce(300, TimeUnit.MILLISECONDS)
|
||||
.distinctUntilChanged { p1, p2 -> p1.map { it.packageName }.sorted() == p2.map { it.packageName }.sorted() }
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe {
|
||||
initData(it)
|
||||
}.let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun changeSortedType() {
|
||||
if (sortedItemDataList.isNotEmpty()) {
|
||||
when (sortedType) {
|
||||
InstalledSortType.RECENTLY_PLAYED -> sortedItemDataList.sortByDescending { it.gameEntity?.lastPlayedTime }
|
||||
InstalledSortType.LATEST_UPDATED -> sortedItemDataList.sortByDescending { it.gameEntity?.updateTime }
|
||||
InstalledSortType.MOST_PLAYED -> sortedItemDataList.sortWith(compareByDescending<InstalledGameItemData> { it.gameEntity?.playedTime }.thenByDescending { it.gameEntity?.packageInstallTime })
|
||||
InstalledSortType.LEAST_PLAYED -> sortedItemDataList.sortWith(compareBy<InstalledGameItemData> { it.gameEntity?.playedTime }.thenByDescending { it.gameEntity?.packageInstallTime })
|
||||
}
|
||||
decorateListAndPost()
|
||||
}
|
||||
}
|
||||
|
||||
fun loadData(vGameList: List<VGameEntity>) {
|
||||
loadPublishSubject.onNext(vGameList)
|
||||
}
|
||||
|
||||
fun refresh() {
|
||||
runOnIoThread {
|
||||
initData(VHelper.getAllVGame())
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData(vGameList: List<VGameEntity>) {
|
||||
val installedVGameList = vGameList.filterNot { vGame ->
|
||||
DownloadManager.getInstance().allVDownloadTaskSnapshots.any {
|
||||
it.gameId == vGame.downloadEntity.gameId && it.status != DownloadStatus.done
|
||||
}
|
||||
}
|
||||
|
||||
if (installedVGameList.isEmpty()) {
|
||||
decorateListAndPost()
|
||||
} else {
|
||||
loadGamesData(installedVGameList)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadGamesData(vGameList: List<VGameEntity>) {
|
||||
val vGameIdList = vGameList.map { it.downloadEntity.gameId }
|
||||
val paramsMap = mapOf(
|
||||
"game_id" to vGameIdList
|
||||
)
|
||||
newApi.postMyHaloGame(TAB_V_GAME, 1, vGameIdList.size, paramsMap.toRequestBody())
|
||||
.map(ApkActiveUtils.filterMapperList)
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
processingData(it, vGameList)
|
||||
}, {
|
||||
loadStatusLiveData.postValue(LoadStatus.INIT_FAILED)
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
private fun processingData(gameList: List<GameEntity>, vGameList: List<VGameEntity>) {
|
||||
val sortedGameList = mutableListOf<GameEntity>()
|
||||
for (entity in gameList) {
|
||||
val newEntity = entity.copy()
|
||||
val vGameEntity = vGameList.find { it.downloadEntity.gameId == entity.id } ?: continue
|
||||
|
||||
val gameEntity = VHelper.toGameEntity(vGameEntity.downloadEntity)
|
||||
newEntity.lastPlayedTime = gameEntity.lastPlayedTime
|
||||
if (gameEntity.playedTime != 0L) {
|
||||
newEntity.playedTime = gameEntity.playedTime
|
||||
newEntity.des =
|
||||
"已畅玩${NumberUtils.transSimpleUsageTime(gameEntity.playedTime / 1000)}"
|
||||
} else {
|
||||
var occupiedSpace =
|
||||
VHelper.getAppOccupiedSpace(vGameEntity.downloadEntity.packageName)
|
||||
if (occupiedSpace == 0L) {
|
||||
occupiedSpace = vGameEntity.downloadEntity.size
|
||||
}
|
||||
|
||||
newEntity.des = "已占用 ${occupiedSpace.toProperReadableSize()}"
|
||||
}
|
||||
|
||||
sortedGameList.add(newEntity)
|
||||
}
|
||||
|
||||
when (sortedType) {
|
||||
InstalledSortType.RECENTLY_PLAYED -> sortedGameList.sortByDescending { it.lastPlayedTime }
|
||||
InstalledSortType.LATEST_UPDATED -> sortedGameList.sortByDescending { it.updateTime }
|
||||
InstalledSortType.MOST_PLAYED -> sortedGameList.sortWith(compareByDescending<GameEntity> { it.playedTime }.thenByDescending { it.packageInstallTime })
|
||||
InstalledSortType.LEAST_PLAYED -> sortedGameList.sortWith(compareBy<GameEntity> { it.playedTime }.thenByDescending { it.packageInstallTime })
|
||||
}
|
||||
|
||||
sortedItemDataList.clear()
|
||||
sortedItemDataList.addAll(sortedGameList.map { InstalledGameItemData(gameEntity = it) })
|
||||
|
||||
decorateListAndPost()
|
||||
}
|
||||
|
||||
private fun decorateListAndPost() {
|
||||
val itemDataList = mutableListOf<InstalledGameItemData>()
|
||||
itemDataList.add(InstalledGameItemData(isHead = true))
|
||||
itemDataList.addAll(sortedItemDataList)
|
||||
if (sortedItemDataList.isNotEmpty()) {
|
||||
itemDataList.add(InstalledGameItemData(isFooter = true))
|
||||
}
|
||||
itemDataListLiveData.postValue(itemDataList)
|
||||
loadStatusLiveData.postValue(if (sortedItemDataList.isEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_LOADED)
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun deleteVGame(url: String?, packageName: String?) {
|
||||
DownloadManager.getInstance().pause(url)
|
||||
DownloadManager.getInstance().cancel(url)
|
||||
|
||||
VHelper.uninstall(packageName ?: "")
|
||||
|
||||
runOnUiThread {
|
||||
val event = EBPackage("卸载", packageName ?: "", "unknown").also {
|
||||
it.isVGame = true
|
||||
}
|
||||
EventBus.getDefault().post(event)
|
||||
PackageObserver.onPackageChanged(event)
|
||||
|
||||
ToastUtils.toast("已删除 1 款游戏")
|
||||
}
|
||||
}
|
||||
|
||||
@WorkerThread
|
||||
fun deleteVGames(idList: ArrayList<String>) {
|
||||
val packageList = arrayListOf<String>()
|
||||
val apkList = arrayListOf<ApkEntity>()
|
||||
|
||||
for (id in idList) {
|
||||
val apkEntity =
|
||||
sortedItemDataList.firstOrNull { id == it.gameEntity?.id }?.gameEntity?.getApk()?.firstOrNull() ?: continue
|
||||
|
||||
apkList.add(apkEntity)
|
||||
}
|
||||
|
||||
for (apkEntity in apkList) {
|
||||
DownloadManager.getInstance().pause(apkEntity.url)
|
||||
DownloadManager.getInstance().cancel(apkEntity.url)
|
||||
|
||||
packageList.add(apkEntity.packageName)
|
||||
|
||||
VHelper.uninstall(apkEntity.packageName)
|
||||
}
|
||||
|
||||
runOnUiThread {
|
||||
for (packageName in packageList) {
|
||||
val event = EBPackage("卸载", packageName, "unknown").also {
|
||||
it.isVGame = true
|
||||
}
|
||||
EventBus.getDefault().post(event)
|
||||
PackageObserver.onPackageChanged(event)
|
||||
}
|
||||
|
||||
ToastUtils.toast("已删除 ${packageList.size} 款游戏")
|
||||
}
|
||||
}
|
||||
|
||||
fun cleanVGameData(packageName: String?) {
|
||||
runOnIoThread {
|
||||
VHelper.cleanGameData(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
|
||||
compositeDisposable.clear()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAB_V_GAME = "smooth"
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,32 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
|
||||
/**
|
||||
* 玩过的游戏
|
||||
*/
|
||||
class PlayedGameActivity: ToolBarActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, PlayedGameActivity::class.java, PlayedGameFragment::class.java)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(com.gh.gamecenter.common.R.color.ui_surface, com.gh.gamecenter.common.R.color.ui_surface)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context): Intent {
|
||||
return getTargetIntent(context, PlayedGameActivity::class.java, PlayedGameFragment::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,230 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.content.Context
|
||||
import android.util.TypedValue
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewGroup.MarginLayoutParams
|
||||
import android.widget.TextView
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.view.updateLayoutParams
|
||||
import androidx.core.view.updateMargins
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.databind.BindingAdapters
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.constant.ItemViewType
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.core.AppExecutor
|
||||
import com.gh.gamecenter.core.utils.NumberUtils
|
||||
import com.gh.gamecenter.core.utils.StringUtils
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.utils.PlatformUtils
|
||||
import com.gh.gamecenter.mygame.PlayedGameAdapter.MyPlayedGameViewHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
class PlayedGameAdapter(
|
||||
context: Context,
|
||||
private val viewModel: PlayedGameViewModel
|
||||
): ListAdapter<GameEntity>(context) {
|
||||
private val downloadText by lazy { mContext.getString(com.gh.gamecenter.feature.R.string.download) }
|
||||
private val tryText by lazy { mContext.getString(com.gh.gamecenter.feature.R.string.attempt) }
|
||||
private val positionAndPackageMap = HashMap<String, Int>()
|
||||
|
||||
override fun setListData(updateData: MutableList<GameEntity>?) {
|
||||
// 记录游戏位置
|
||||
if (updateData != null) {
|
||||
for (i in 0 until updateData.size) {
|
||||
val gameEntity = updateData[i]
|
||||
|
||||
// 一些数据置换
|
||||
val apk = gameEntity.getApk()
|
||||
if (apk.size > 0 && !apk[0].ghVersion.isNullOrEmpty()) {
|
||||
gameEntity.brief = PlatformUtils.getInstance(mContext).getPlatformName(apk[0].getPlatform())
|
||||
}
|
||||
|
||||
var packages = gameEntity.id
|
||||
for (apkEntity in apk) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
positionAndPackageMap[packages + i] = i
|
||||
}
|
||||
}
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int = if (position == itemCount - 1) {
|
||||
ItemViewType.ITEM_FOOTER
|
||||
} else {
|
||||
ItemViewType.GAME_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder =
|
||||
when (viewType) {
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
MyPlayedGameViewHolder(parent.toBinding())
|
||||
}
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(com.gh.gamecenter.common.R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is MyPlayedGameViewHolder) {
|
||||
val gameEntity = mEntityList.getOrNull(position) ?: return
|
||||
holder.binding.run {
|
||||
uninstalledTv.updateLayoutParams<MarginLayoutParams> {
|
||||
updateMargins(top = 8F.dip2px())
|
||||
}
|
||||
holder.initServerType(gameEntity)
|
||||
gameItemIncluded.run {
|
||||
root.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px())
|
||||
root.background = com.gh.gamecenter.common.R.drawable.reuse_listview_item_style.toDrawable(root.context)
|
||||
gameName.setTextColor(com.gh.gamecenter.common.R.color.text_primary.toColor(root.context))
|
||||
gameDes.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(root.context))
|
||||
recommendStarPref.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(root.context))
|
||||
|
||||
downloadBtn.layoutParams = downloadBtn.layoutParams.apply {
|
||||
width = 56F.dip2px()
|
||||
height = 28F.dip2px()
|
||||
}
|
||||
gameIconView.displayGameIcon(gameEntity)
|
||||
gameRating.textSize = if (gameEntity.commentCount > 3) 12F else 10F
|
||||
BindingAdapters.setGameName(gameName, gameEntity, false)
|
||||
BindingAdapters.setGameTags(labelList, gameEntity, "")
|
||||
gameRating.setDrawableStart(if (gameEntity.commentCount > 3) com.gh.gamecenter.feature.R.drawable.game_horizontal_rating.toDrawable() else null)
|
||||
gameRating.setPadding(0, 0, if (gameEntity.commentCount > 3) 8F.dip2px() else 0, 0)
|
||||
gameRating.text = if (gameEntity.commentCount > 3) {
|
||||
if (gameEntity.star == 10.0F) "10" else gameEntity.star.toString()
|
||||
} else ""
|
||||
gameRating.setTextColor(
|
||||
if (gameEntity.commentCount > 3) com.gh.gamecenter.common.R.color.text_theme.toColor(mContext) else com.gh.gamecenter.common.R.color.primary_theme.toColor(
|
||||
mContext
|
||||
)
|
||||
)
|
||||
gameDes.text = gameEntity.decoratedDes
|
||||
recommendStar.rating = gameEntity.recommendStar.toFloat()
|
||||
GameItemViewHolder.initGameSubtitleAndAdLabel(gameEntity, gameSubtitleTv)
|
||||
|
||||
gameDes.updateLayoutParams<MarginLayoutParams> {
|
||||
topMargin = 8F.dip2px()
|
||||
}
|
||||
ConstraintSet().apply {
|
||||
clone(root)
|
||||
if (gameEntity.playedTime == 0L) {
|
||||
connect(gameDes.id, ConstraintSet.BOTTOM, gameDesSpace.id, ConstraintSet.BOTTOM)
|
||||
} else {
|
||||
clear(gameDes.id, ConstraintSet.BOTTOM)
|
||||
}
|
||||
}.applyTo(root)
|
||||
if (gameEntity.playedTime == 0L) {
|
||||
gameDes.post {
|
||||
gameDesSpace.updateLayoutParams<MarginLayoutParams> {
|
||||
height = gameDes.height + 8F.dip2px()
|
||||
}
|
||||
}
|
||||
labelList.visibility = View.GONE
|
||||
} else {
|
||||
gameDesSpace.updateLayoutParams<MarginLayoutParams> {
|
||||
height = 28F.dip2px()
|
||||
}
|
||||
labelList.visibility = View.VISIBLE
|
||||
labelList.removeAllViews()
|
||||
val runTimeView = TextView(mContext)
|
||||
runTimeView.isSingleLine = true
|
||||
runTimeView.setTextSize(TypedValue.COMPLEX_UNIT_SP, 11F)
|
||||
runTimeView.setTextColor(com.gh.gamecenter.common.R.color.text_secondary.toColor(mContext))
|
||||
runTimeView.text = "游戏时长 ${NumberUtils.transSimpleUsageTime(gameEntity.playedTime)}"
|
||||
labelList.addView(runTimeView)
|
||||
}
|
||||
}
|
||||
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
mContext,
|
||||
gameItemIncluded.downloadBtn,
|
||||
gameEntity,
|
||||
position,
|
||||
this@PlayedGameAdapter,
|
||||
"玩过的游戏",
|
||||
location = StringUtils.buildString("玩过的游戏", ":", gameEntity.name)
|
||||
)
|
||||
DownloadItemUtils.updateItem(
|
||||
mContext,
|
||||
gameEntity,
|
||||
GameViewHolder(gameItemIncluded),
|
||||
true
|
||||
)
|
||||
|
||||
gameItemIncluded.downloadBtn.putWidgetBusinessName("玩过的游戏")
|
||||
|
||||
root.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(mContext, gameEntity.id, "玩过的游戏", null)
|
||||
}
|
||||
|
||||
root.setOnLongClickListener {
|
||||
val adapterPosition = holder.bindingAdapterPosition
|
||||
if (adapterPosition != RecyclerView.NO_POSITION) {
|
||||
val game = mEntityList.getOrNull(adapterPosition) ?: return@setOnLongClickListener true
|
||||
PlayedGameMoreDialogFragment.show(mContext, game) {
|
||||
viewModel.deletePlayedGame(game)
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
// 很丑陋,但因为上面的下载按钮文案和状态是异步获取更新的,所以这里也只能异步更新了 :(
|
||||
AppExecutor.lightWeightIoExecutor.execute {
|
||||
AppExecutor.uiExecutor.execute {
|
||||
tryWithDefaultCatch {
|
||||
val downloadBtnText = gameItemIncluded.downloadBtn.text
|
||||
val isDownloadable =
|
||||
downloadBtnText.contains(downloadText) || downloadBtnText.contains(tryText)
|
||||
uninstalledTv.visibleIf(isDownloadable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (holder is FooterViewHolder) {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(viewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
holder.hint.setTextColor(com.gh.gamecenter.common.R.color.text_tertiary.toColor(mContext))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = if (mEntityList.isNullOrEmpty()) 0 else mEntityList.size + 1
|
||||
|
||||
fun notifyItemByDownload(download: DownloadEntity) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(download.packageName) && key.contains(download.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && mEntityList != null && position < mEntityList.size) {
|
||||
mEntityList[position].getEntryMap()[download.platform] = download
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemAndRemoveDownload(status: EBDownloadStatus) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
if (key.contains(status.packageName) && key.contains(status.gameId)) {
|
||||
val position = positionAndPackageMap[key]
|
||||
if (position != null && mEntityList != null && position < mEntityList.size) {
|
||||
mEntityList[position].getEntryMap().remove(status.platform)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun clearPositionAndPackageMap() {
|
||||
positionAndPackageMap.clear()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,231 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.content.Intent
|
||||
import android.graphics.Typeface
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import androidx.core.view.isVisible
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.UsageStatsHelper
|
||||
import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LazyListFragment
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.databinding.FragmentPlayedGameBinding
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.GameInstall
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.personalhome.InstalledGameDialog
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import kotlin.math.abs
|
||||
|
||||
class PlayedGameFragment: LazyListFragment<GameEntity, PlayedGameViewModel>() {
|
||||
private lateinit var binding: FragmentPlayedGameBinding
|
||||
private val viewModel by lazy { viewModelProvider<PlayedGameViewModel>() }
|
||||
private val playedGameAdapter by lazy { PlayedGameAdapter(requireContext(), viewModel) }
|
||||
|
||||
private val dataWatcher = object : DataWatcher() {
|
||||
override fun onDataChanged(downloadEntity: DownloadEntity) {
|
||||
playedGameAdapter.notifyItemByDownload(downloadEntity)
|
||||
}
|
||||
|
||||
override fun onDataInit(downloadEntity: DownloadEntity) {
|
||||
onDataChanged(downloadEntity)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRealLayoutId(): Int = R.layout.fragment_played_game
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<*> = playedGameAdapter
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration? = null
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
super.onRealLayoutInflated(inflatedView)
|
||||
binding = FragmentPlayedGameBinding.bind(inflatedView)
|
||||
binding.run {
|
||||
appbar.addOnOffsetChangedListener { _, verticalOffset ->
|
||||
listRefresh.isEnabled = abs(verticalOffset) < 2
|
||||
}
|
||||
playedGameDataTv.typeface = Typeface.createFromAsset(requireContext().assets, Constants.DIN_FONT_PATH)
|
||||
mostPlayedContainer.run {
|
||||
titleTv.text = "玩的最多"
|
||||
enableUsageStatsView.setOnClickListener {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1 && !UsageStatsHelper.checkForPermission()) {
|
||||
DialogUtils.showUsageStatsDialog(
|
||||
requireContext(), {
|
||||
UsageStatsHelper.skipToUsageStats(
|
||||
requireContext(),
|
||||
UsageStatsHelper.USAGE_STATUS_REQUEST_CODE
|
||||
)
|
||||
}, null
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
recentlyPlayedContainer.run {
|
||||
titleTv.text = "最近在玩"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
setNavigationTitle("玩过的游戏")
|
||||
|
||||
// 提交用户使用数据
|
||||
runOnIoThread {
|
||||
UsageStatsHelper.checkAndPostUsageStats()
|
||||
}
|
||||
|
||||
viewModel.totalGameLiveData.observe(viewLifecycleOwner) {
|
||||
binding.playedGameDataTv.text = it
|
||||
}
|
||||
|
||||
viewModel.mostPlayedGameListLiveData.observe(viewLifecycleOwner) {
|
||||
updateMostPlayedGame(it)
|
||||
}
|
||||
|
||||
viewModel.recentlyPlayedGameListLiveData.observe(viewLifecycleOwner) {
|
||||
updateRecentlyPlayedGame(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateMostPlayedGame(gameList: List<GameEntity>) {
|
||||
val isUsageStatsEnabled = Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP_MR1 || UsageStatsHelper.checkForPermission()
|
||||
binding.mostPlayedContainer.run {
|
||||
if (isUsageStatsEnabled) {
|
||||
emptyTv.isVisible = gameList.isEmpty()
|
||||
enableUsageStatsTv.isVisible = false
|
||||
enableUsageStatsView.isVisible = false
|
||||
} else {
|
||||
emptyTv.isVisible = false
|
||||
enableUsageStatsTv.isVisible = gameList.isEmpty()
|
||||
enableUsageStatsView.isVisible = gameList.isEmpty()
|
||||
}
|
||||
|
||||
val gameIconList = listOf(gameIcon1, gameIcon2, gameIcon3)
|
||||
gameList.forEachIndexed { index, game ->
|
||||
gameIconList[index].run {
|
||||
displayGameIcon(game)
|
||||
setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(requireContext(), game.id, "玩过的游戏-玩的最多", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
gameList.size.let {
|
||||
gameIcon1.goneIf(it == 0)
|
||||
rank1Iv.goneIf(it == 0)
|
||||
gameIcon2.goneIf(it < 2)
|
||||
rank2Iv.goneIf(it < 2)
|
||||
gameIcon3.goneIf(it < 3)
|
||||
rank3Iv.goneIf(it < 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateRecentlyPlayedGame(gameList: List<GameEntity>) {
|
||||
binding.recentlyPlayedContainer.run {
|
||||
if (gameList.isEmpty()) {
|
||||
emptyTv.isVisible = true
|
||||
} else {
|
||||
emptyTv.isVisible = false
|
||||
|
||||
val gameIconList = listOf(gameIcon1, gameIcon2, gameIcon3)
|
||||
gameList.forEachIndexed { index, game ->
|
||||
gameIconList[index].run {
|
||||
displayGameIcon(game)
|
||||
setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(requireContext(),
|
||||
game.id, "玩过的游戏-玩的最多", null)
|
||||
}
|
||||
}
|
||||
}
|
||||
gameList.size.let {
|
||||
gameIcon1.goneIf(it == 0)
|
||||
gameIcon2.goneIf(it < 2)
|
||||
gameIcon3.goneIf(it < 3)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFragmentPause() {
|
||||
super.onFragmentPause()
|
||||
DownloadManager.getInstance().removeObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun onFragmentResume() {
|
||||
super.onFragmentResume()
|
||||
DownloadManager.getInstance().addObserver(dataWatcher)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
playedGameAdapter.clearPositionAndPackageMap()
|
||||
super.onRefresh()
|
||||
}
|
||||
|
||||
override fun onChanged(ts: MutableList<GameEntity>?) {
|
||||
super.onChanged(ts)
|
||||
val installedList =
|
||||
PackagesManager.filterSameApk(PackagesManager.filterDownloadBlackPackage(PackagesManager.getInstalledList()))
|
||||
val simulatorDownloadEntityList = DownloadManager.getInstance().allSimulatorDownloadEntity
|
||||
simulatorDownloadEntityList.forEach { entity ->
|
||||
installedList.add(
|
||||
GameInstall(
|
||||
entity.gameId,
|
||||
packageName = entity.packageName,
|
||||
name = entity.name,
|
||||
icon = entity.icon
|
||||
)
|
||||
)
|
||||
}
|
||||
// val count = SPUtils.getInt(Constants.SP_MARK_INSTALLED_GAME, 0)
|
||||
val isCancel = SPUtils.getBoolean(Constants.SP_MARK_INSTALLED_GAME_MY_GAME, false)
|
||||
if (!isCancel && ts.isNullOrEmpty() && installedList.isNotEmpty()) {
|
||||
val dialog = InstalledGameDialog(requireContext(), installedList, "我的光环_新", "我的游戏-标记玩过弹窗")
|
||||
dialog.show()
|
||||
dialog.onConfirmClickListener = {
|
||||
onLoadRefresh()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 下载被删除事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(status: EBDownloadStatus) {
|
||||
if ("delete" == status.status) {
|
||||
playedGameAdapter.notifyItemAndRemoveDownload(status)
|
||||
}
|
||||
}
|
||||
|
||||
// 安装/卸载 事件
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(busFour: EBPackage) {
|
||||
if (busFour.isInstalledOrUninstalled()) {
|
||||
playedGameAdapter.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (requestCode == UsageStatsHelper.USAGE_STATUS_REQUEST_CODE && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
viewModel.mostPlayedGameListLiveData.value?.let {
|
||||
updateMostPlayedGame(it)
|
||||
}
|
||||
SPUtils.setBoolean(UsageStatsHelper.USAGE_STATUS_SP_KEY, UsageStatsHelper.checkForPermission())
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,57 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.core.os.bundleOf
|
||||
import com.gh.gamecenter.common.base.fragment.BaseBottomDialogFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.core.utils.CurrentActivityHolder
|
||||
import com.gh.gamecenter.databinding.DialogPlayedGameMoreBinding
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
|
||||
class PlayedGameMoreDialogFragment : BaseBottomDialogFragment<DialogPlayedGameMoreBinding>() {
|
||||
private var gameEntity: GameEntity? = null
|
||||
private var onClearClickListener: (() -> Unit)? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
gameEntity = arguments?.getParcelable(EntranceConsts.KEY_GAME_ENTITY)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
gameEntity?.let { mBinding.gameIconView.displayGameIcon(it) }
|
||||
mBinding.gameNameTv.text = gameEntity?.name
|
||||
mBinding.clearTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
onClearClickListener?.invoke()
|
||||
}
|
||||
mBinding.cancelTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun show(
|
||||
context: Context,
|
||||
gameEntity: GameEntity,
|
||||
onClearClickListener: () -> Unit,
|
||||
) {
|
||||
if (context is AppCompatActivity) {
|
||||
context.supportFragmentManager
|
||||
} else {
|
||||
(CurrentActivityHolder.getCurrentActivity() as? AppCompatActivity)?.supportFragmentManager
|
||||
}?.let {
|
||||
val fragment = PlayedGameMoreDialogFragment().apply {
|
||||
arguments = bundleOf(
|
||||
EntranceConsts.KEY_GAME_ENTITY to gameEntity
|
||||
)
|
||||
}
|
||||
fragment.onClearClickListener = onClearClickListener
|
||||
fragment.show(it, fragment::class.java.simpleName)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.baselist.LoadType
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.CompositeDisposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class PlayedGameViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
private val recentlyPlayedGameRepository = RecentlyPlayedGameRepository()
|
||||
private val compositeDisposable = CompositeDisposable()
|
||||
|
||||
val totalGameLiveData = MutableLiveData<String>()
|
||||
val mostPlayedGameListLiveData = MutableLiveData<List<GameEntity>>()
|
||||
val recentlyPlayedGameListLiveData = recentlyPlayedGameRepository.recentlyPlayedGameList
|
||||
|
||||
override fun load(loadType: LoadType?) {
|
||||
if (mLoadStatusLiveData.value == LoadStatus.INIT || loadType == LoadType.REFRESH) {
|
||||
getMostPlayedGame()
|
||||
recentlyPlayedGameRepository.refreshRecentlyPlayedGame()
|
||||
}
|
||||
super.load(loadType)
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<GameEntity>>? = null
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<GameEntity>> =
|
||||
api.getPlayedGamesWithResponse(UserManager.getInstance().userId, page, Utils.getTime(getApplication()), mapOf())
|
||||
.map {
|
||||
totalGameLiveData.postValue(it.headers()["Total"] ?: "0")
|
||||
it.body()
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
it.forEach { game ->
|
||||
game.hideSizeInsideDes = true
|
||||
game.tagStyle.clear()
|
||||
}
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun getMostPlayedGame() {
|
||||
api.getMostPlayedGames(UserManager.getInstance().userId, Utils.getTime(getApplication()))
|
||||
.map { it.body() }
|
||||
.compose(singleToMain())
|
||||
.subscribe({
|
||||
mostPlayedGameListLiveData.postValue(it.take(3))
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
mostPlayedGameListLiveData.postValue(emptyList())
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun deletePlayedGame(gameEntity: GameEntity) {
|
||||
api.deletePlayedGame(UserManager.getInstance().userId, gameEntity.playedGameId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
mListLiveData.value?.let {
|
||||
for (game in it) {
|
||||
if (gameEntity.id == game.id) {
|
||||
it.remove(game)
|
||||
mListLiveData.postValue(it)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
Utils.toast(getApplication(), exception.localizedMessage)
|
||||
}
|
||||
}).let(compositeDisposable::add)
|
||||
}
|
||||
|
||||
override fun onCleared() {
|
||||
super.onCleared()
|
||||
compositeDisposable.clear()
|
||||
recentlyPlayedGameRepository.onClear()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,107 @@
|
||||
package com.gh.gamecenter.playedgame
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.concurrent.atomic.AtomicInteger
|
||||
|
||||
class RecentlyPlayedGameRepository {
|
||||
private var disposable: Disposable? = null
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
|
||||
val recentlyPlayedGameList = MutableLiveData<List<GameEntity>>()
|
||||
|
||||
fun refreshRecentlyPlayedGame() {
|
||||
onClear()
|
||||
loadData()
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadData() {
|
||||
disposable = loadAllPlayedGames()
|
||||
.map { allGames ->
|
||||
val heap = java.util.PriorityQueue<GameEntity>(RECENTLY_PLAYED_GAME_COUNT, compareBy { it.recentlyPlayedTime })
|
||||
|
||||
allGames.forEach { game ->
|
||||
if (heap.size < RECENTLY_PLAYED_GAME_COUNT) {
|
||||
heap.offer(game)
|
||||
} else if (game.recentlyPlayedTime > (heap.peek()?.recentlyPlayedTime ?: 0L)) {
|
||||
heap.poll()
|
||||
heap.offer(game)
|
||||
}
|
||||
}
|
||||
|
||||
heap.sortedByDescending { it.recentlyPlayedTime }
|
||||
}
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe({ topThree ->
|
||||
recentlyPlayedGameList.postValue(topThree)
|
||||
}, {
|
||||
it.printStackTrace()
|
||||
recentlyPlayedGameList.postValue(emptyList())
|
||||
})
|
||||
}
|
||||
|
||||
private fun loadAllPlayedGames(): Single<List<GameEntity>> {
|
||||
return Single.create { emitter ->
|
||||
val allGames = mutableListOf<GameEntity>()
|
||||
val currentPage = AtomicInteger(1)
|
||||
var hasMore = true
|
||||
|
||||
while (hasMore && !emitter.isDisposed) {
|
||||
try {
|
||||
val page = currentPage.getAndIncrement()
|
||||
val games = loadPlayedGame(page).blockingGet()
|
||||
|
||||
if (games.isEmpty()) {
|
||||
hasMore = false
|
||||
} else {
|
||||
allGames.addAll(games)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (!emitter.isDisposed) {
|
||||
emitter.onError(e)
|
||||
}
|
||||
return@create
|
||||
}
|
||||
}
|
||||
|
||||
if (!emitter.isDisposed) {
|
||||
emitter.onSuccess(allGames)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun loadPlayedGame(page: Int): Single<List<GameEntity>> {
|
||||
val paramsMap = mapOf("page_size" to 50)
|
||||
|
||||
return api.getPlayedGames(
|
||||
UserManager.getInstance().userId,
|
||||
page,
|
||||
Utils.getTime(HaloApp.getInstance()),
|
||||
paramsMap
|
||||
)
|
||||
.doOnSuccess { list ->
|
||||
list.forEach { it.updateRecentlyPlayedTime() }
|
||||
}
|
||||
.onErrorReturn { emptyList() }
|
||||
.subscribeOn(Schedulers.io())
|
||||
}
|
||||
|
||||
fun onClear() {
|
||||
disposable?.dispose()
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val RECENTLY_PLAYED_GAME_COUNT = 3
|
||||
}
|
||||
}
|
||||
@ -50,6 +50,7 @@ import com.gh.gamecenter.entity.GameColumnCollection;
|
||||
import com.gh.gamecenter.entity.GameData;
|
||||
import com.gh.gamecenter.entity.GameDigestEntity;
|
||||
import com.gh.gamecenter.entity.GameGuidePopupEntity;
|
||||
import com.gh.gamecenter.entity.GameServerTestDisplaySetting;
|
||||
import com.gh.gamecenter.entity.GameServerTestTopGame;
|
||||
import com.gh.gamecenter.entity.GameServerTestV2Entity;
|
||||
import com.gh.gamecenter.entity.GameVideoInfo;
|
||||
@ -62,6 +63,7 @@ import com.gh.gamecenter.entity.ImageInfoEntity;
|
||||
import com.gh.gamecenter.entity.InterestedGameEntity;
|
||||
import com.gh.gamecenter.entity.LibaoDetailEntity;
|
||||
import com.gh.gamecenter.entity.MultiTabNav;
|
||||
import com.gh.gamecenter.entity.MyHaloContentCard;
|
||||
import com.gh.gamecenter.entity.MyVideoEntity;
|
||||
import com.gh.gamecenter.entity.NewApiSettingsEntity;
|
||||
import com.gh.gamecenter.entity.NewSettingsEntity;
|
||||
@ -77,6 +79,7 @@ import com.gh.gamecenter.entity.RatingReplyEntity;
|
||||
import com.gh.gamecenter.entity.RecommendPopupEntity;
|
||||
import com.gh.gamecenter.entity.ReserveModifyEntity;
|
||||
import com.gh.gamecenter.entity.ReserveReminderEntity;
|
||||
import com.gh.gamecenter.entity.SearchGameUnionEntity;
|
||||
import com.gh.gamecenter.entity.SearchSubjectEntity;
|
||||
import com.gh.gamecenter.entity.ServerPublishEntity;
|
||||
import com.gh.gamecenter.entity.ServerSubscriptionEntity;
|
||||
@ -326,6 +329,18 @@ public interface ApiService {
|
||||
@GET
|
||||
Observable<List<GameEntity>> getSearchGame(@Url String url);
|
||||
|
||||
/**
|
||||
* 搜索游戏(新:整合了 cpm dsp 广告位置排序问题)
|
||||
*/
|
||||
@GET("./games:search?view=union")
|
||||
Observable<List<SearchGameUnionEntity>> getSearchGameUnionData(
|
||||
@Query("keyword") String keyword,
|
||||
@Query("page") int page,
|
||||
@Query("version") String version,
|
||||
@Query("channel") String channel,
|
||||
@Query("AD") String ad
|
||||
);
|
||||
|
||||
/**
|
||||
* 搜索CPM微信小游戏
|
||||
*/
|
||||
@ -1778,6 +1793,9 @@ public interface ApiService {
|
||||
@GET("users/{user_id}/played_games")
|
||||
Single<List<GameEntity>> getPlayedGames(@Path("user_id") String userId, @Query("page") int page, @Query("timestamp") long timestamp, @QueryMap Map<String, Object> params);
|
||||
|
||||
@GET("users/{user_id}/played_games")
|
||||
Single<Response<List<GameEntity>>> getPlayedGamesWithResponse(@Path("user_id") String userId, @Query("page") int page, @Query("timestamp") long timestamp, @QueryMap Map<String, Object> params);
|
||||
|
||||
/**
|
||||
* 获取开服过滤标签
|
||||
*/
|
||||
@ -3092,6 +3110,12 @@ public interface ApiService {
|
||||
@GET("columns/tests/v2")
|
||||
Observable<GameServerTestV2Entity> getServerTestV2(@Query("filter") String filter);
|
||||
|
||||
/**
|
||||
* 新游开测-显示配置
|
||||
*/
|
||||
@GET("app/column_test_v2/{link_id}/display_setting")
|
||||
Single<GameServerTestDisplaySetting> getGameServerTestDisplaySetting(@Path("link_id") String linkId);
|
||||
|
||||
/**
|
||||
* 新游开测-详情列表
|
||||
*/
|
||||
@ -3528,4 +3552,27 @@ public interface ApiService {
|
||||
@POST("/devices/{device_id}/installed_games")
|
||||
Single<ResponseBody> postInstalledPackages(@Path("device_id") String deviceId, @Body RequestBody body);
|
||||
|
||||
/**
|
||||
* 我的光环-会员卡片+常用功能+合规开关
|
||||
*/
|
||||
@GET("my_halo/content_card")
|
||||
Single<MyHaloContentCard> getMyHaloContentCard();
|
||||
|
||||
/**
|
||||
* 我的光环-获取游戏列表
|
||||
*/
|
||||
@POST("my_halo/game")
|
||||
Single<List<GameEntity>> postMyHaloGame(@Query("tab_type") String tabType, @Query("page") int page, @Query("page_size") int pageSize, @Body RequestBody body);
|
||||
|
||||
/**
|
||||
* 我的光环-游戏预约
|
||||
*/
|
||||
@GET("users/appointments")
|
||||
Single<Response<List<GameEntity>>> getMyHaloReservation(@Query("status") String status, @Query("page") int page, @Query("page_size") int pageSize, @Query("timestamp") long timestamp);
|
||||
|
||||
/**
|
||||
* 我的光环-用户玩得最多的游戏(时长从高到低)
|
||||
*/
|
||||
@GET("users/{user_id}/played_games_more")
|
||||
Single<Response<List<GameEntity>>> getMostPlayedGames(@Path("user_id") String userId, @Query("timestamp") long timestamp);
|
||||
}
|
||||
@ -10,8 +10,8 @@ interface ArticleHistoryDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addArticle(answer: ArticleEntity)
|
||||
|
||||
@Query("select * from ArticleEntity order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getArticleWithOffset(pageSize: Int, offset: Int): Single<List<ArticleEntity>>
|
||||
@Query("select * from ArticleEntity where orderTag >= :startTimestamp order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getArticleWithOffset(pageSize: Int, offset: Int, startTimestamp: Long): Single<List<ArticleEntity>>
|
||||
|
||||
@Delete
|
||||
fun deleteArticle(article: ArticleEntity)
|
||||
|
||||
@ -3,6 +3,7 @@ package com.gh.gamecenter.room.dao
|
||||
import androidx.room.*
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import io.reactivex.Single
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
|
||||
@Dao
|
||||
interface GameDao {
|
||||
@ -10,8 +11,11 @@ interface GameDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addGame(game: HistoryGameEntity)
|
||||
|
||||
@Query("select * from HistoryGameEntity order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getGamesWithOffset(pageSize: Int, offset: Int): Single<List<HistoryGameEntity>>
|
||||
@Query("select * from HistoryGameEntity where orderTag >= :startTimestamp order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getGamesWithOffset(pageSize: Int, offset: Int, startTimestamp: Long): Single<List<HistoryGameEntity>>
|
||||
|
||||
@Query("SELECT COUNT(*) FROM HistoryGameEntity")
|
||||
fun getGameCount(): Flow<Int>
|
||||
|
||||
@Delete
|
||||
fun deleteGame(game: HistoryGameEntity)
|
||||
|
||||
@ -10,8 +10,8 @@ interface GamesCollectionDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addGamesCollection(gamesCollection: GamesCollectionEntity)
|
||||
|
||||
@Query("select * from GamesCollectionEntity order by orderTag desc limit :pageSize offset :offset")
|
||||
fun getGamesCollectionWithOffset(pageSize: Int, offset: Int): Single<MutableList<GamesCollectionEntity>>
|
||||
@Query("select * from GamesCollectionEntity where orderTag >= :startTimestamp order by orderTag desc limit :pageSize offset :offset")
|
||||
fun getGamesCollectionWithOffset(pageSize: Int, offset: Int, startTimestamp: Long): Single<MutableList<GamesCollectionEntity>>
|
||||
|
||||
@Delete
|
||||
fun deleteGamesCollection(gamesCollection: GamesCollectionEntity)
|
||||
|
||||
@ -10,8 +10,8 @@ interface NewsHistoryDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addNews(news: NewsEntity)
|
||||
|
||||
@Query("select * from NewsEntity order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getNewsWithOffset(pageSize: Int, offset: Int): Single<List<NewsEntity>>
|
||||
@Query("select * from NewsEntity where orderTag >= :startTimestamp order by orderTag desc limit :pageSize offset :offset ")
|
||||
fun getNewsWithOffset(pageSize: Int, offset: Int, startTimestamp: Long): Single<List<NewsEntity>>
|
||||
|
||||
@Delete
|
||||
fun deleteNews(news: NewsEntity)
|
||||
|
||||
@ -9,8 +9,8 @@ interface VideoHistoryDao {
|
||||
@Insert(onConflict = OnConflictStrategy.REPLACE)
|
||||
fun addVideo(video: MyVideoEntity)
|
||||
|
||||
@Query("select * from MyVideoEntity where user!='' order by time desc limit :pageSize offset :offset")
|
||||
fun getVideoWithOffset(pageSize: Int, offset: Int): Single<MutableList<MyVideoEntity>>
|
||||
@Query("select * from MyVideoEntity where user!='' and time >= :startTimestamp order by time desc limit :pageSize offset :offset")
|
||||
fun getVideoWithOffset(pageSize: Int, offset: Int, startTimestamp: Long): Single<MutableList<MyVideoEntity>>
|
||||
|
||||
@Query("select id from MyVideoEntity where videoStreamRecord=2 order by time desc")
|
||||
fun getAttentionVideoRecord(): Single<MutableList<String>>
|
||||
|
||||
@ -1,18 +1,7 @@
|
||||
package com.gh.gamecenter.search
|
||||
|
||||
import com.gh.gamecenter.entity.SearchSubjectEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
interface ISearchGameResultRepository {
|
||||
fun getSearchGame(key: String?, page: Int): Observable<List<GameEntity>>
|
||||
|
||||
fun getSearchMiniGameCPM(key: String?): Observable<List<GameEntity>>
|
||||
|
||||
fun getSearchSubject(key: String?, page: Int): Observable<List<SearchSubjectEntity>>
|
||||
|
||||
fun getWGameCPMGameList(size: Int): Single<MutableList<GameEntity>>
|
||||
|
||||
fun getDspGameList(columnType: String, showDownload: Boolean, size: Int): Single<List<GameEntity>>
|
||||
fun getSearchGame(key: String?, page: Int): Observable<List<SearchItemData>>
|
||||
}
|
||||
@ -31,6 +31,7 @@ import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.SettingsEntity
|
||||
import com.gh.gamecenter.feature.exposure.addExposureHelper
|
||||
import com.gh.gamecenter.help.HelpAndFeedbackBridge
|
||||
import com.gh.gamecenter.search.viewmodel.SearchGameResultViewModel
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
@ -67,7 +68,6 @@ class SearchGameIndexFragment : ListFragment<GameEntity, SearchGameResultViewMod
|
||||
SearchGameResultViewModel.Factory(
|
||||
HaloApp.getInstance(),
|
||||
mKey,
|
||||
false,
|
||||
SearchGameResultRepository(),
|
||||
mType,
|
||||
activity?.intent?.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: ""
|
||||
@ -198,7 +198,6 @@ class SearchGameIndexFragment : ListFragment<GameEntity, SearchGameResultViewMod
|
||||
this.mType = type
|
||||
mAdapter?.key = key
|
||||
mListViewModel?.updateSearchKeyWithType(key, type)
|
||||
mListViewModel?.clearSearchSubjects()
|
||||
mListViewModel?.load(LoadType.REFRESH)
|
||||
}
|
||||
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
package com.gh.gamecenter.search
|
||||
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
@ -60,6 +59,7 @@ import com.gh.gamecenter.gamedetail.accelerator.dialog.StartingAcceleratorDialog
|
||||
import com.gh.gamecenter.gamedetail.entity.GameDetailTabEntity
|
||||
import com.gh.gamecenter.help.HelpAndFeedbackBridge
|
||||
import com.gh.gamecenter.minigame.MiniGameSearchResultFragment
|
||||
import com.gh.gamecenter.search.viewmodel.SearchGameResultViewModel
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Util_System_Keyboard
|
||||
@ -1119,12 +1119,12 @@ class SearchGameResultAdapter(
|
||||
!hasMutualityZone -> {
|
||||
// 单区服,直接启动
|
||||
val zoneInfo = gameEntity.serviceArea.firstOrNull() ?: AcctGameInfo.ZoneInfo(0)
|
||||
startAccelerating(gameEntity, context, zoneInfo, false)
|
||||
startAccelerating(gameEntity, context, zoneInfo, false, SOURCE_ENTRANCE_SEARCH)
|
||||
}
|
||||
|
||||
lastAcctGame != null -> {
|
||||
// 多区服,有缓存的加速记录
|
||||
startAccelerating(gameEntity, context, lastAcctGame.zoneInfo, true)
|
||||
startAccelerating(gameEntity, context, lastAcctGame.zoneInfo, true, SOURCE_ENTRANCE_SEARCH)
|
||||
}
|
||||
|
||||
else -> {
|
||||
@ -1167,15 +1167,16 @@ class SearchGameResultAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun startAccelerating(
|
||||
fun startAccelerating(
|
||||
gameEntity: GameEntity,
|
||||
context: Context,
|
||||
zoneInfo: AcctGameInfo.ZoneInfo,
|
||||
hasMultiZone: Boolean
|
||||
hasMultiZone: Boolean,
|
||||
sourceEntrance: String
|
||||
) {
|
||||
val request = AcceleratorValidator.Request(
|
||||
gameEntity,
|
||||
SOURCE_ENTRANCE_SEARCH
|
||||
sourceEntrance
|
||||
)
|
||||
AcceleratorClient.newInstance()
|
||||
.execute(context, request, object : AcceleratorValidator.ValidateListener {
|
||||
@ -1186,7 +1187,7 @@ class SearchGameResultAdapter(
|
||||
gameEntity,
|
||||
true,
|
||||
hasMultiZone,
|
||||
SOURCE_ENTRANCE_SEARCH
|
||||
sourceEntrance
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -13,7 +13,6 @@ import android.view.animation.TranslateAnimation
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.DefaultExposureStateChangeListener
|
||||
import com.gh.common.util.*
|
||||
@ -41,10 +40,10 @@ import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.eventbus.EBStartupAcceleration
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.exposure.addExposureHelper
|
||||
import com.gh.gamecenter.feature.entity.VipEntity
|
||||
import com.gh.gamecenter.help.HelpAndFeedbackBridge
|
||||
import com.gh.gamecenter.livedata.EventObserver
|
||||
import com.gh.gamecenter.minigame.MiniGameSearchResultFragment
|
||||
import com.gh.gamecenter.search.viewmodel.SearchGameResultViewModel
|
||||
import com.gh.gamecenter.search.viewmodel.SearchTabViewModel
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.halo.assistant.accelerator.repository.AcceleratorDataHolder
|
||||
@ -126,7 +125,6 @@ open class SearchGameResultFragment : ListFragment<GameEntity, SearchGameResultV
|
||||
SearchGameResultViewModel.Factory(
|
||||
HaloApp.getInstance(),
|
||||
mKey,
|
||||
true,
|
||||
SearchGameResultRepository(),
|
||||
mType,
|
||||
activity?.intent?.getStringExtra(EntranceConsts.KEY_SOURCE_ENTRANCE) ?: "",
|
||||
@ -444,7 +442,6 @@ open class SearchGameResultFragment : ListFragment<GameEntity, SearchGameResultV
|
||||
this.mKey = key
|
||||
mAdapter?.key = key
|
||||
mAdapter?.clearAdIdSet()
|
||||
mListViewModel?.clearSearchSubjects()
|
||||
mListViewModel?.load(LoadType.REFRESH)
|
||||
}
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user