Compare commits
22 Commits
dev
...
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 |
@ -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
|
After Width: | Height: | Size: 18 KiB |
9
app/src/main/assets/myhalo/ic_game_event.svg
Normal file
|
After Width: | Height: | Size: 15 KiB |
@ -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)) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
/*
|
||||
* 返回光环助手的版本信息
|
||||
*/
|
||||
|
||||
@ -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
|
||||
}
|
||||
}
|
||||
@ -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()) {
|
||||
|
||||
@ -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)
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -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") {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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()) {
|
||||
|
||||
@ -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
@ -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
@ -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
|
||||
}
|
||||
}
|
||||
@ -63,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;
|
||||
@ -1792,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);
|
||||
|
||||
/**
|
||||
* 获取开服过滤标签
|
||||
*/
|
||||
@ -3548,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>>
|
||||
|
||||
@ -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
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
@ -17,9 +17,7 @@ import androidx.lifecycle.ProcessLifecycleOwner;
|
||||
import androidx.multidex.MultiDexApplication;
|
||||
import androidx.webkit.WebViewCompat;
|
||||
|
||||
import com.gh.common.util.AdHelper;
|
||||
import com.gh.gamecenter.login.interceptor.LoginInterceptor;
|
||||
import com.therouter.TheRouter;
|
||||
import com.facebook.drawee.backends.pipeline.DraweeConfig;
|
||||
import com.facebook.imageformat.DefaultImageFormats;
|
||||
import com.facebook.imagepipeline.core.ImagePipelineConfig;
|
||||
import com.facebook.imagepipeline.core.ImagePipelineFactory;
|
||||
@ -33,6 +31,7 @@ import com.gh.common.filter.RegionSettingHelper;
|
||||
import com.gh.common.interceptor.MainInterceptor;
|
||||
import com.gh.common.prioritychain.GlobalPriorityChainHelper;
|
||||
import com.gh.common.util.ActivationHelper;
|
||||
import com.gh.common.util.AdHelper;
|
||||
import com.gh.common.util.DataUtils;
|
||||
import com.gh.common.util.DeviceTokenUtils;
|
||||
import com.gh.common.util.DownloadNotificationHelper;
|
||||
@ -53,6 +52,7 @@ import com.gh.gamecenter.common.constant.Constants;
|
||||
import com.gh.gamecenter.common.entity.LinkEntity;
|
||||
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
|
||||
import com.gh.gamecenter.common.image.EmptyDecoder;
|
||||
import com.gh.gamecenter.common.image.SvgDecoder;
|
||||
import com.gh.gamecenter.common.tracker.Tracker;
|
||||
import com.gh.gamecenter.common.utils.DarkModeUtils;
|
||||
import com.gh.gamecenter.common.utils.DeviceUtils;
|
||||
@ -602,6 +602,12 @@ public class HaloApp extends MultiDexApplication {
|
||||
// 所以这里尝试在 5.0 & 5.1 设备上关闭 fresco 的 native 解码
|
||||
ImagePipelineConfig.Builder pipelineConfigBuilder = ImagePipelineConfig.newBuilder(this);
|
||||
ImageDecoderConfig.Builder decodeConfigBuilder = ImageDecoderConfig.newBuilder();
|
||||
|
||||
// 添加SVG支持
|
||||
decodeConfigBuilder.addDecodingCapability(SvgDecoder.SVG_FORMAT, new SvgDecoder.SvgFormatChecker(), new SvgDecoder.SvgImageDecoder());
|
||||
DraweeConfig.Builder draweeConfigBuilder = DraweeConfig.newBuilder();
|
||||
draweeConfigBuilder.addCustomDrawableFactory(new SvgDecoder.SvgDrawableFactory());
|
||||
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
|
||||
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
|
||||
pipelineConfigBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
|
||||
@ -621,10 +627,12 @@ public class HaloApp extends MultiDexApplication {
|
||||
.setImageDecoderConfig(decodeConfigBuilder.build())
|
||||
.experiment()
|
||||
.setNativeCodeDisabled(true);
|
||||
} else {
|
||||
pipelineConfigBuilder.setImageDecoderConfig(decodeConfigBuilder.build());
|
||||
}
|
||||
|
||||
try {
|
||||
BigImageViewer.initialize(FrescoImageLoader.with(this, pipelineConfigBuilder.build()));
|
||||
BigImageViewer.initialize(FrescoImageLoader.with(this, pipelineConfigBuilder.build(), draweeConfigBuilder.build()));
|
||||
} catch (Throwable e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
BIN
app/src/main/res/drawable-night-xxxhdpi/bg_accelerator_vip.webp
Normal file
|
After Width: | Height: | Size: 178 KiB |
|
After Width: | Height: | Size: 51 KiB |
BIN
app/src/main/res/drawable-night-xxxhdpi/play_time_head.webp
Normal file
|
After Width: | Height: | Size: 56 KiB |
|
Before Width: | Height: | Size: 9.6 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/accelerator_logo.webp
Normal file
|
After Width: | Height: | Size: 6.6 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/bg_accelerator_vip.webp
Normal file
|
After Width: | Height: | Size: 157 KiB |
|
Before Width: | Height: | Size: 194 KiB After Width: | Height: | Size: 280 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/guide_grant_usage_stats.webp
Normal file
|
After Width: | Height: | Size: 41 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/my_halo_guide_1.webp
Normal file
|
After Width: | Height: | Size: 73 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/my_halo_guide_2.webp
Normal file
|
After Width: | Height: | Size: 78 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/my_halo_guide_3.webp
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/my_halo_guide_pic.webp
Normal file
|
After Width: | Height: | Size: 60 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/play_time_background.webp
Normal file
|
After Width: | Height: | Size: 287 KiB |
BIN
app/src/main/res/drawable-xxxhdpi/play_time_head.webp
Normal file
|
After Width: | Height: | Size: 59 KiB |
8
app/src/main/res/drawable/bg_accelerator_vip_action.xml
Normal file
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<solid android:color="@color/white_alpha_10" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/white_alpha_10" />
|
||||
<corners android:radius="999dp" />
|
||||
</shape>
|
||||
10
app/src/main/res/drawable/bg_personal_bottom_gradient.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="@color/ui_surface_55"
|
||||
android:startColor="@color/ui_surface_0" />
|
||||
|
||||
</shape>
|
||||
@ -5,6 +5,6 @@
|
||||
<gradient
|
||||
android:angle="90"
|
||||
android:endColor="@color/ui_surface"
|
||||
android:startColor="@color/ui_background" />
|
||||
android:startColor="@color/ui_surface_55" />
|
||||
|
||||
</shape>
|
||||
@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="999dp" />
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="@color/ui_divider_2" />
|
||||
</shape>
|
||||
@ -1,9 +1,11 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
|
||||
<corners android:radius="999dp" />
|
||||
|
||||
<solid android:color="#6640577A" />
|
||||
|
||||
<stroke
|
||||
android:width="1dp"
|
||||
android:color="#1A5A74CC" />
|
||||
<gradient
|
||||
android:endColor="#1AA6B9F5"
|
||||
android:startColor="#33A6B9F5" />
|
||||
</shape>
|
||||
12
app/src/main/res/drawable/divider_installed_game_service.xml
Normal file
@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<layer-list xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:bottom="8dp"
|
||||
android:top="8dp">
|
||||
<shape android:shape="rectangle">
|
||||
<solid android:color="@color/ui_divider" />
|
||||
<size android:width="1dp" />
|
||||
<corners android:radius="999dp" />
|
||||
</shape>
|
||||
</item>
|
||||
</layer-list>
|
||||
@ -8,6 +8,6 @@
|
||||
android:pathData="M7,4.5L4,1.5L1,4.5"
|
||||
android:strokeLineJoin="round"
|
||||
android:fillColor="#00000000"
|
||||
android:strokeColor="@color/text_primary"
|
||||
android:strokeColor="@color/text_neutral"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
||||
16
app/src/main/res/drawable/ic_basic_addicon.xml
Normal file
@ -0,0 +1,16 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h20v20h-20z"/>
|
||||
<path
|
||||
android:pathData="M5,2.5C3.619,2.5 2.5,3.619 2.5,5V14.167C2.5,15.547 3.619,16.667 5,16.667H9.167C9.627,16.667 10,16.294 10,15.833C10,15.373 9.627,15 9.167,15H5C4.54,15 4.167,14.627 4.167,14.167V5C4.167,4.54 4.54,4.167 5,4.167H14.167C14.627,4.167 15,4.54 15,5V9.167C15,9.627 15.373,10 15.833,10C16.294,10 16.667,9.627 16.667,9.167V5C16.667,3.619 15.547,2.5 14.167,2.5H5Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M15.833,12.5C15.833,12.04 15.46,11.667 15,11.667C14.54,11.667 14.167,12.04 14.167,12.5V14.167H12.5C12.04,14.167 11.667,14.54 11.667,15C11.667,15.46 12.04,15.833 12.5,15.833H14.167V17.5C14.167,17.96 14.54,18.333 15,18.333C15.46,18.333 15.833,17.96 15.833,17.5V15.833H17.5C17.96,15.833 18.333,15.46 18.333,15C18.333,14.54 17.96,14.167 17.5,14.167H15.833V12.5Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
</group>
|
||||
</vector>
|
||||
14
app/src/main/res/drawable/ic_basic_delete.xml
Normal file
@ -0,0 +1,14 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="20"
|
||||
android:viewportHeight="20">
|
||||
<group>
|
||||
<clip-path
|
||||
android:pathData="M0,0h20v20h-20z"/>
|
||||
<path
|
||||
android:pathData="M7.917,3.333C7.457,3.333 7.083,3.706 7.083,4.167V5H12.917V4.167C12.917,3.706 12.544,3.333 12.083,3.333H7.917ZM14.583,5L17.083,5C17.544,5 17.917,5.373 17.917,5.833C17.917,6.294 17.544,6.667 17.083,6.667H2.917C2.457,6.667 2.083,6.294 2.083,5.833C2.083,5.373 2.457,5 2.917,5L5.417,5V4.167C5.417,2.786 6.536,1.667 7.917,1.667H12.083C13.464,1.667 14.583,2.786 14.583,4.167V5ZM4.167,8.333C4.627,8.333 5,8.706 5,9.167V15.833C5,16.294 5.373,16.667 5.833,16.667H14.167C14.627,16.667 15,16.294 15,15.833V9.167C15,8.706 15.373,8.333 15.833,8.333C16.294,8.333 16.667,8.706 16.667,9.167V15.833C16.667,17.214 15.547,18.333 14.167,18.333H5.833C4.453,18.333 3.333,17.214 3.333,15.833V9.167C3.333,8.706 3.707,8.333 4.167,8.333ZM8.958,9.167C8.958,8.706 8.585,8.333 8.125,8.333C7.665,8.333 7.292,8.706 7.292,9.167V12.5C7.292,12.96 7.665,13.333 8.125,13.333C8.585,13.333 8.958,12.96 8.958,12.5V9.167ZM11.875,8.333C12.335,8.333 12.708,8.706 12.708,9.167V12.5C12.708,12.96 12.335,13.333 11.875,13.333C11.415,13.333 11.042,12.96 11.042,12.5V9.167C11.042,8.706 11.415,8.333 11.875,8.333Z"
|
||||
android:fillColor="@color/text_secondary"
|
||||
android:fillType="evenOdd"/>
|
||||
</group>
|
||||
</vector>
|
||||
10
app/src/main/res/drawable/ic_basic_more.xml
Normal file
@ -0,0 +1,10 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="16dp"
|
||||
android:height="16dp"
|
||||
android:viewportWidth="16"
|
||||
android:viewportHeight="16">
|
||||
<path
|
||||
android:fillColor="@color/text_neutral"
|
||||
android:fillType="evenOdd"
|
||||
android:pathData="M8,11.333C8.736,11.333 9.333,11.93 9.333,12.667C9.333,13.403 8.736,14 8,14C7.263,14 6.667,13.403 6.667,12.667C6.667,11.93 7.263,11.333 8,11.333ZM8,6.667C8.736,6.667 9.333,7.264 9.333,8C9.333,8.736 8.736,9.333 8,9.333C7.263,9.333 6.667,8.736 6.667,8C6.667,7.264 7.263,6.667 8,6.667ZM8,2C8.736,2 9.333,2.597 9.333,3.333C9.333,4.07 8.736,4.667 8,4.667C7.263,4.667 6.667,4.07 6.667,3.333C6.667,2.597 7.263,2 8,2Z" />
|
||||
</vector>
|
||||
24
app/src/main/res/drawable/ic_basic_multipleselection.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="14dp"
|
||||
android:height="14dp"
|
||||
android:viewportWidth="14"
|
||||
android:viewportHeight="14">
|
||||
<path
|
||||
android:pathData="M5.662,1.921C5.435,1.693 5.065,1.693 4.838,1.921L3.5,3.258L2.746,2.504C2.518,2.276 2.149,2.276 1.921,2.504C1.693,2.732 1.693,3.101 1.921,3.329L3.088,4.496C3.315,4.724 3.685,4.724 3.912,4.496L5.662,2.746C5.89,2.518 5.89,2.149 5.662,1.921Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
<path
|
||||
android:pathData="M7.292,3.208C7.292,2.886 7.553,2.625 7.875,2.625H11.667C11.989,2.625 12.25,2.886 12.25,3.208C12.25,3.53 11.989,3.792 11.667,3.792H7.875C7.553,3.792 7.292,3.53 7.292,3.208Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
<path
|
||||
android:pathData="M4.958,7C4.958,6.678 5.22,6.417 5.542,6.417H11.667C11.989,6.417 12.25,6.678 12.25,7C12.25,7.322 11.989,7.583 11.667,7.583H5.542C5.22,7.583 4.958,7.322 4.958,7Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
<path
|
||||
android:pathData="M4.958,10.792C4.958,10.469 5.22,10.208 5.542,10.208H11.667C11.989,10.208 12.25,10.469 12.25,10.792C12.25,11.114 11.989,11.375 11.667,11.375H5.542C5.22,11.375 4.958,11.114 4.958,10.792Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
<path
|
||||
android:pathData="M2.917,7.875C3.4,7.875 3.792,7.483 3.792,7C3.792,6.517 3.4,6.125 2.917,6.125C2.433,6.125 2.042,6.517 2.042,7C2.042,7.483 2.433,7.875 2.917,7.875Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
<path
|
||||
android:pathData="M2.917,11.667C3.4,11.667 3.792,11.275 3.792,10.792C3.792,10.308 3.4,9.917 2.917,9.917C2.433,9.917 2.042,10.308 2.042,10.792C2.042,11.275 2.433,11.667 2.917,11.667Z"
|
||||
android:fillColor="@color/text_tertiary"/>
|
||||
</vector>
|
||||
24
app/src/main/res/drawable/ic_basic_multipleselection_20.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="20dp"
|
||||
android:height="20dp"
|
||||
android:viewportWidth="14"
|
||||
android:viewportHeight="14">
|
||||
<path
|
||||
android:pathData="M5.662,1.921C5.435,1.693 5.065,1.693 4.838,1.921L3.5,3.258L2.746,2.504C2.518,2.276 2.149,2.276 1.921,2.504C1.693,2.732 1.693,3.101 1.921,3.329L3.088,4.496C3.315,4.724 3.685,4.724 3.912,4.496L5.662,2.746C5.89,2.518 5.89,2.149 5.662,1.921Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M7.292,3.208C7.292,2.886 7.553,2.625 7.875,2.625H11.667C11.989,2.625 12.25,2.886 12.25,3.208C12.25,3.53 11.989,3.792 11.667,3.792H7.875C7.553,3.792 7.292,3.53 7.292,3.208Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M4.958,7C4.958,6.678 5.22,6.417 5.542,6.417H11.667C11.989,6.417 12.25,6.678 12.25,7C12.25,7.322 11.989,7.583 11.667,7.583H5.542C5.22,7.583 4.958,7.322 4.958,7Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M4.958,10.792C4.958,10.469 5.22,10.208 5.542,10.208H11.667C11.989,10.208 12.25,10.469 12.25,10.792C12.25,11.114 11.989,11.375 11.667,11.375H5.542C5.22,11.375 4.958,11.114 4.958,10.792Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M2.917,7.875C3.4,7.875 3.792,7.483 3.792,7C3.792,6.517 3.4,6.125 2.917,6.125C2.433,6.125 2.042,6.517 2.042,7C2.042,7.483 2.433,7.875 2.917,7.875Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
<path
|
||||
android:pathData="M2.917,11.667C3.4,11.667 3.792,11.275 3.792,10.792C3.792,10.308 3.4,9.917 2.917,9.917C2.433,9.917 2.042,10.308 2.042,10.792C2.042,11.275 2.433,11.667 2.917,11.667Z"
|
||||
android:fillColor="@color/text_secondary"/>
|
||||
</vector>
|
||||