Compare commits
2 Commits
legacy-rel
...
feat/jzcs
| Author | SHA1 | Date | |
|---|---|---|---|
| 0a9b49c330 | |||
| 5b212bec2a |
@ -72,7 +72,6 @@ android_build:
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-6644
|
||||
|
||||
# 代码检查
|
||||
sonarqube_analysis:
|
||||
@ -157,5 +156,4 @@ oss-upload&send-email:
|
||||
- /usr/local/bin/python /ci-android-mail-jira-comment.py
|
||||
only:
|
||||
- dev
|
||||
- release
|
||||
- feat/GHZSCY-6644
|
||||
- release
|
||||
@ -333,6 +333,14 @@
|
||||
android:screenOrientation="portrait"
|
||||
android:windowSoftInputMode="stateHidden" />
|
||||
|
||||
<activity
|
||||
android:name=".category.CategoryDirectoryActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".category.CategoryListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name="com.gh.gamecenter.UserInfoActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
@ -554,6 +562,14 @@
|
||||
android:name=".simulatorgame.SimulatorManagementActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".catalog.CatalogActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".catalog.NewCatalogListActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
<activity
|
||||
android:name=".forum.search.ForumOrUserSearchActivity"
|
||||
android:screenOrientation="portrait" />
|
||||
|
||||
@ -543,14 +543,15 @@ object AdDelegateHelper {
|
||||
), null, null
|
||||
)
|
||||
|
||||
adImage.visibleIf(true)
|
||||
ImageUtils.display(adImage, ad.img)
|
||||
|
||||
if (ad.isImageType) {
|
||||
adImage.visibleIf(true)
|
||||
adVideo.visibleIf(false)
|
||||
ImageUtils.display(adImage, ad.img)
|
||||
} else {
|
||||
adImage.visibleIf(false)
|
||||
adVideo.visibleIf(true)
|
||||
adVideo.startPlay(ad.video.url)
|
||||
adVideo.startPlay(ad.img, ad.video.url)
|
||||
}
|
||||
startAdContainer.setOnClickListener {
|
||||
// 拦截点击事件传递
|
||||
|
||||
@ -1,11 +1,12 @@
|
||||
package com.gh.ad
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.WindowManager
|
||||
import com.facebook.drawee.view.SimpleDraweeView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.video.detail.CustomManager
|
||||
import com.shuyu.gsyvideoplayer.utils.Debuger
|
||||
import com.shuyu.gsyvideoplayer.utils.GSYVideoType
|
||||
import com.shuyu.gsyvideoplayer.utils.GSYVideoType.SCREEN_TYPE_FULL
|
||||
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
||||
@ -17,14 +18,18 @@ class SplashAdVideoView @JvmOverloads constructor(
|
||||
) :
|
||||
StandardGSYVideoPlayer(context, attrs) {
|
||||
|
||||
fun startPlay(url: String) {
|
||||
|
||||
fun startPlay(cover: String, url: String) {
|
||||
GSYVideoType.setShowType(SCREEN_TYPE_FULL)
|
||||
GSYVideoType.setRenderType(GSYVideoType.SUFRACE)
|
||||
CustomManager.getCustomManager(getKey()).isNeedMute = true
|
||||
|
||||
setUp(url, true, "")
|
||||
|
||||
val ivCover = findViewById<SimpleDraweeView>(R.id.thumbImage)
|
||||
ImageUtils.display(ivCover, cover)
|
||||
setNeedAutoAdaptation(false)
|
||||
isLooping = true
|
||||
startPlayLogic()
|
||||
}
|
||||
|
||||
@ -44,31 +49,6 @@ class SplashAdVideoView @JvmOverloads constructor(
|
||||
return R.layout.layout_splash_ad_video
|
||||
}
|
||||
|
||||
override fun onAutoCompletion() {
|
||||
setStateAndUi(CURRENT_STATE_AUTO_COMPLETE);
|
||||
|
||||
mSaveChangeViewTIme = 0
|
||||
mCurrentPosition = 0
|
||||
|
||||
if (!mIfCurrentIsFullscreen) {
|
||||
getGSYVideoManager().setLastListener(null)
|
||||
}
|
||||
mAudioManager.abandonAudioFocus(onAudioFocusChangeListener);
|
||||
if (mContext is Activity) {
|
||||
try {
|
||||
(mContext as Activity).getWindow().clearFlags(WindowManager.LayoutParams.FLAG_KEEP_SCREEN_ON)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
releaseNetWorkState()
|
||||
|
||||
if (mVideoAllCallBack != null && isCurrentMediaListener()) {
|
||||
mVideoAllCallBack.onAutoComplete(mOriginUrl, mTitle, this)
|
||||
}
|
||||
mHadPlay = false
|
||||
}
|
||||
|
||||
fun clearAll() {
|
||||
GSYVideoType.setShowType(GSYVideoType.SCREEN_TYPE_DEFAULT)
|
||||
GSYVideoType.setRenderType(GSYVideoType.TEXTURE)
|
||||
|
||||
@ -268,6 +268,10 @@ object DefaultUrlHandler {
|
||||
directToGameVideo(context, id, entrance, "")
|
||||
}
|
||||
|
||||
EntranceConsts.HOST_CATEGORY -> {
|
||||
val title = uri.getQueryParameter("title")
|
||||
DirectUtils.directCategoryDirectory(context, id, title ?: "", entrance, "")
|
||||
}
|
||||
EntranceConsts.HOST_COLUMN_COLLECTION -> {
|
||||
val name = uri.getQueryParameter("name")
|
||||
DirectUtils.directToColumnCollection(context, id, -1, entrance, name ?: "")
|
||||
|
||||
@ -1,16 +0,0 @@
|
||||
package com.gh.common.prioritychain
|
||||
|
||||
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
|
||||
|
||||
class VideoHandler(priority: Int, val scrollCalculatorHelper: ScrollCalculatorHelper): PriorityChainHandler(priority) {
|
||||
|
||||
init {
|
||||
updateStatus(STATUS_VALID)
|
||||
}
|
||||
|
||||
override fun onProcess(): Boolean {
|
||||
scrollCalculatorHelper.enableAndPlayIfValid()
|
||||
return true
|
||||
}
|
||||
|
||||
}
|
||||
@ -2,15 +2,12 @@ package com.gh.common.provider
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import androidx.fragment.app.FragmentActivity
|
||||
import com.alibaba.android.arouter.facade.annotation.Route
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.gamecenter.common.avoidcallback.Callback
|
||||
import com.gh.gamecenter.common.constant.RouteConsts
|
||||
import com.gh.gamecenter.core.provider.IEntranceUtilsProvider
|
||||
import com.lightgame.utils.AppManager
|
||||
|
||||
@Route(path = RouteConsts.provider.entranceUtils, name = "EntranceUtils暴露服务")
|
||||
class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
|
||||
@ -23,16 +20,11 @@ class EntranceUtilsProviderImpl : IEntranceUtilsProvider {
|
||||
}
|
||||
|
||||
override fun jumpActivityWithCallback(context: Context, bundle: Bundle, callback: () -> Unit) {
|
||||
if (context is FragmentActivity && !context.supportFragmentManager.isDestroyed) {
|
||||
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
|
||||
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
} else {
|
||||
EntranceUtils.jumpActivity(AppManager.getInstance().currentActivity(), bundle)
|
||||
}
|
||||
|
||||
EntranceUtils.jumpActivity(context, null, bundle, object : Callback {
|
||||
override fun onActivityResult(resultCode: Int, data: Intent?) {
|
||||
callback()
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun init(context: Context?) {
|
||||
|
||||
@ -19,6 +19,8 @@ import com.gh.common.util.EntranceUtils.jumpActivity
|
||||
import com.gh.common.util.EntranceUtils.jumpActivityCompat
|
||||
import com.gh.gamecenter.*
|
||||
import com.gh.gamecenter.amway.AmwayActivity
|
||||
import com.gh.gamecenter.catalog.CatalogActivity
|
||||
import com.gh.gamecenter.category.CategoryDirectoryActivity
|
||||
import com.gh.gamecenter.category2.CategoryV2Activity
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity
|
||||
import com.gh.gamecenter.common.base.activity.BaseActivity_TabLayout
|
||||
@ -149,12 +151,14 @@ object DirectUtils {
|
||||
"qqqun",
|
||||
"tag",
|
||||
"all_community_article",
|
||||
"category",
|
||||
"block",
|
||||
"column_collection",
|
||||
"server",
|
||||
"top_game_comment",
|
||||
"wechat_bind",
|
||||
"video",
|
||||
"catalog",
|
||||
"category_v2",
|
||||
"common_collection",
|
||||
"game_list",
|
||||
@ -281,6 +285,10 @@ object DirectUtils {
|
||||
)
|
||||
)
|
||||
|
||||
"category", "分类" -> directCategoryDirectory(context, linkEntity.link!!, linkEntity.text!!)
|
||||
|
||||
"catalog" -> directCatalog(context, linkEntity.link!!, linkEntity.text!!, entrance, path)
|
||||
|
||||
"category_v2" -> directCategoryV2(
|
||||
context,
|
||||
linkEntity.link!!,
|
||||
@ -1415,6 +1423,47 @@ object DirectUtils {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转分类
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directCategoryDirectory(
|
||||
context: Context,
|
||||
categoryId: String,
|
||||
categoryTitle: String,
|
||||
entrance: String? = null,
|
||||
path: String? = ""
|
||||
) {
|
||||
if (categoryId.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, CategoryDirectoryActivity::class.java.name)
|
||||
bundle.putString(KEY_CATEGORY_ID, categoryId)
|
||||
bundle.putString(KEY_CATEGORY_TITLE, categoryTitle)
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_PATH, path)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转新分类
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directCatalog(
|
||||
context: Context,
|
||||
catalogId: String,
|
||||
catalogTitle: String,
|
||||
entrance: String? = null,
|
||||
path: String? = "",
|
||||
) {
|
||||
if (catalogId.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, CatalogActivity::class.java.name)
|
||||
bundle.putString(KEY_CATALOG_ID, catalogId)
|
||||
bundle.putString(KEY_CATALOG_TITLE, catalogTitle)
|
||||
bundle.putString(KEY_ENTRANCE, BaseActivity.mergeEntranceAndPath(entrance, path))
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转新分类2.0
|
||||
*/
|
||||
|
||||
@ -37,10 +37,10 @@ import com.gh.gamecenter.core.utils.*
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.feature.entity.PluginLocation
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
|
||||
import com.gh.gamecenter.feature.view.DownloadButton
|
||||
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
|
||||
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
|
||||
import com.gh.vspace.VHelper
|
||||
import com.lightgame.download.DownloadConfig
|
||||
@ -697,7 +697,7 @@ object DownloadItemUtils {
|
||||
// 为 downloadButton 添加游戏实体,供点击的时候上报用
|
||||
downloadBtn.putObject(gameEntity)
|
||||
|
||||
val gamePermissionDialogFragment = (context as? AppCompatActivity)?.supportFragmentManager?.findFragmentByTag(
|
||||
val gamePermissionDialogFragment = (context as AppCompatActivity).supportFragmentManager.findFragmentByTag(
|
||||
GamePermissionDialogFragment::class.java.name
|
||||
) as GamePermissionDialogFragment?
|
||||
gamePermissionDialogFragment?.dismissAllowingStateLoss()
|
||||
|
||||
@ -105,14 +105,9 @@ public class EntranceUtils {
|
||||
//TODO:添加FLAG_ACTIVITY_NEW_TASK会导致一跳转页面callback就被调用
|
||||
//intent1.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
|
||||
intent1.putExtras(bundle);
|
||||
if (context instanceof AppCompatActivity) {
|
||||
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
|
||||
.startForResult(intent1, callback);
|
||||
} else {
|
||||
// 不要回调,正常跳转
|
||||
context.startActivity(intent1);
|
||||
}
|
||||
|
||||
AvoidOnResultManager.Companion.getInstance((AppCompatActivity) context)
|
||||
.startForResult(intent1, callback);
|
||||
}
|
||||
} else {
|
||||
// 应用未在运行
|
||||
|
||||
@ -7,7 +7,6 @@ import com.gh.gamecenter.common.utils.toObject
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectEntity
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
@ -112,14 +111,6 @@ object GameSubstituteRepositoryHelper {
|
||||
}
|
||||
}
|
||||
|
||||
// 检查是否已安装该游戏 id
|
||||
if (PackagesManager.getInstalledDataByGameId(game.id) != null) {
|
||||
// 将该位置的游戏标记为需要替换
|
||||
positionOfGameToBeReplacedList.add(index)
|
||||
thisPositionNeedToBeReplaced = true
|
||||
break
|
||||
}
|
||||
|
||||
// 若此游戏所包含的 apk 没有已安装,那么再检查是否已安装有预设相关包名
|
||||
if (!thisPositionNeedToBeReplaced) {
|
||||
var relatedPackageList = arrayListOf<String>()
|
||||
|
||||
@ -308,7 +308,7 @@ object GameUtils {
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否默认以畅玩游戏的形式来处理
|
||||
* 是否以畅玩游戏的形式来处理
|
||||
*/
|
||||
@JvmStatic
|
||||
fun shouldPerformAsVGame(gameEntity: GameEntity): Boolean {
|
||||
|
||||
@ -57,10 +57,6 @@ object HomeBottomBarHelper {
|
||||
return BottomTab(name = "我的光环", jsCode = animationCode, iconSelector = R.drawable.selector_ic_user, link = LinkEntity(type = TYPE_MY_HALO))
|
||||
}
|
||||
|
||||
fun isDefaultHomeBottomTabDataExist(): Boolean {
|
||||
return SPUtils.getString(KEY_HOME_BOTTOM_TAB).isNotEmpty()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getDefaultHomeBottomTabData(): List<BottomTab> {
|
||||
try {
|
||||
|
||||
@ -734,6 +734,18 @@ public class LogUtils {
|
||||
LoghubUtils.log(object, LOG_STORE_EVENT, false);
|
||||
}
|
||||
|
||||
public static void logNewCatalogAppearanceEvent(String entrance, String key) {
|
||||
logCatalogEvent("access_to_classification", entrance, key, -1, -1, -1, -1);
|
||||
}
|
||||
|
||||
public static void logSubCatalogClickEvent(String entrance, String key, int seq1) {
|
||||
logCatalogEvent("click_first_classification", entrance, key, seq1, -1, -1, -1);
|
||||
}
|
||||
|
||||
public static void logSubCatalogContentClickEvent(String entrance, String key, int seq1, int seq2) {
|
||||
logCatalogEvent("click_secondary_classification", entrance, key, seq1, seq2, -1, -1);
|
||||
}
|
||||
|
||||
public static void logSpecialCatalogContentClickEvent(String entrance, String key, int seq1, int seqContent) {
|
||||
logCatalogEvent("click_content", entrance, key, seq1, -1, seqContent, -1);
|
||||
}
|
||||
|
||||
@ -90,7 +90,7 @@ object PackageChangeHelper : DefaultLifecycleObserver {
|
||||
pendingPackageTriple = null
|
||||
pendingGhId = null
|
||||
|
||||
PackageRepository.addInstalledGame(PackageRepository.packageFilterManager, packageName)
|
||||
PackageRepository.addInstalledGame(packageName)
|
||||
|
||||
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
|
||||
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
|
||||
|
||||
@ -81,28 +81,23 @@ object PackageHelper {
|
||||
|
||||
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
|
||||
private var _commentPackageNameBlackList = arrayListOf<String>()
|
||||
val commentPackageNameBlackList: ArrayList<String>
|
||||
get() = _commentPackageNameBlackList
|
||||
val commentPackageNameBlackList: ArrayList<String> = _commentPackageNameBlackList
|
||||
|
||||
// 关闭下载的包列表
|
||||
private var _downloadPackageNameBlackList = arrayListOf<String>()
|
||||
val downloadPackageNameBlackList: ArrayList<String>
|
||||
get() = _downloadPackageNameBlackList
|
||||
val downloadPackageNameBlackList: ArrayList<String> = _downloadPackageNameBlackList
|
||||
|
||||
// 本地已安装的包去掉关闭下载的包后的列表
|
||||
private var _validLocalPackageNameSet = hashSetOf<String>()
|
||||
val validLocalPackageNameSet: HashSet<String>
|
||||
get() = _validLocalPackageNameSet
|
||||
val validLocalPackageNameSet: HashSet<String> = _validLocalPackageNameSet
|
||||
|
||||
// 游戏包名匹配列表
|
||||
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
|
||||
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages>
|
||||
get() = _relatedPackageList
|
||||
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
|
||||
|
||||
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
|
||||
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
|
||||
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean>
|
||||
get() = _installedPackageApiSwitchStatusLiveData
|
||||
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
|
||||
|
||||
// 本地已安装包的列表
|
||||
var localPackageNameSet = hashSetOf<String>()
|
||||
@ -546,7 +541,6 @@ object PackageHelper {
|
||||
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
|
||||
|
||||
PackageRepository.addInstalledGames(
|
||||
packageFilterManager = PackageRepository.packageFilterManager,
|
||||
pkgNameList = ArrayList(installedPackageNameSet),
|
||||
updateInstallStatus = true
|
||||
)
|
||||
|
||||
@ -14,7 +14,6 @@ import android.os.Build;
|
||||
import android.os.Bundle;
|
||||
import android.os.PowerManager;
|
||||
import android.text.TextUtils;
|
||||
import android.util.AndroidException;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
@ -223,19 +222,8 @@ public class PackageUtils {
|
||||
if (metaDate != null) {
|
||||
return metaDate.get(name);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (e instanceof AndroidException) {
|
||||
// 有些设备会出现 DeadSystemException
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"GET_META_DATA_ERROR",
|
||||
"packageName",
|
||||
packageName,
|
||||
"exception_digest",
|
||||
e.getLocalizedMessage()
|
||||
);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// e.printStackTrace();
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -639,19 +627,8 @@ public class PackageUtils {
|
||||
try {
|
||||
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
||||
if (e instanceof AndroidException) {
|
||||
// 有些设备会出现 DeadSystemException
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"GET_VERSION_NAME_ERROR",
|
||||
"packageName",
|
||||
packageName,
|
||||
"exception_digest",
|
||||
e.getLocalizedMessage()
|
||||
);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// do nothing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -663,18 +640,8 @@ public class PackageUtils {
|
||||
try {
|
||||
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
|
||||
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (e instanceof AndroidException) {
|
||||
// 有些设备会出现 DeadSystemException
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"GET_VERSION_CODE_ERROR",
|
||||
"packageName",
|
||||
packageName,
|
||||
"exception_digest",
|
||||
e.getLocalizedMessage()
|
||||
);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// do nothing
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
@ -687,18 +654,8 @@ public class PackageUtils {
|
||||
try {
|
||||
PackageManager packageManager = context.getApplicationContext().getPackageManager();
|
||||
return packageManager.getApplicationIcon(packageName);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
if (e instanceof AndroidException) {
|
||||
// 有些设备会出现 DeadSystemException
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"GET_ICON_ERROR",
|
||||
"packageName",
|
||||
packageName,
|
||||
"exception_digest",
|
||||
e.getLocalizedMessage()
|
||||
);
|
||||
}
|
||||
} catch (NameNotFoundException e) {
|
||||
// do nothing
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -749,19 +706,8 @@ public class PackageUtils {
|
||||
jsonObject.put("version", packageInfo.versionName);
|
||||
}
|
||||
return jsonObject;
|
||||
} catch (Exception e) {
|
||||
} catch (JSONException | NameNotFoundException e) {
|
||||
e.printStackTrace();
|
||||
if (e instanceof AndroidException) {
|
||||
// 有些设备会出现 DeadSystemException
|
||||
SentryHelper.INSTANCE.onEvent(
|
||||
"GET_APP_BASIC_INFO_BY_PACKAGE_NAME",
|
||||
"packageName",
|
||||
packageName,
|
||||
"exception_digest",
|
||||
e.getLocalizedMessage()
|
||||
);
|
||||
}
|
||||
|
||||
return jsonObject;
|
||||
}
|
||||
}
|
||||
|
||||
331
app/src/main/java/com/gh/common/view/CatalogFilterView.kt
Normal file
331
app/src/main/java/com/gh/common/view/CatalogFilterView.kt
Normal file
@ -0,0 +1,331 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.AttributeSet
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.ImageView
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.ColorInt
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.setDrawableEnd
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.visibleIf
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.google.android.flexbox.FlexboxLayout
|
||||
|
||||
class CatalogFilterView @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
private var mTypeTv: TextView
|
||||
private var mCatalogTv: TextView
|
||||
private var mSizeTv: TextView
|
||||
private var mTypeContainer: View
|
||||
private var mCatalogContainer: View
|
||||
private var mSizeContainer: View
|
||||
|
||||
private var mTypeFilterArray = ArrayList<SortType>()
|
||||
private var mCatalogFilterArray = ArrayList<CatalogEntity.SubCatalogEntity>()
|
||||
private var sizeFilterArray: ArrayList<SubjectSettingEntity.Size>? = null
|
||||
|
||||
private var mOnCatalogFilterSetupListener: OnCatalogFilterSetupListener? = null
|
||||
|
||||
private var mTypePopupWindow: PopupWindow? = null
|
||||
private var mCatalogPopupWindow: PopupWindow? = null
|
||||
private var mSizePopupWindow: PopupWindow? = null
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.layout_catalog_filter, this)
|
||||
|
||||
mTypeTv = findViewById(R.id.type_tv)
|
||||
mCatalogTv = findViewById(R.id.catalog_tv)
|
||||
mSizeTv = findViewById(R.id.size_tv)
|
||||
mTypeContainer = findViewById(R.id.container_type)
|
||||
mCatalogContainer = findViewById(R.id.container_catalog)
|
||||
mSizeContainer = findViewById(R.id.container_size)
|
||||
|
||||
mTypeContainer.setOnClickListener {
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
}
|
||||
|
||||
mCatalogContainer.setOnClickListener {
|
||||
showSelectCatalogPopupWindow(this, mCatalogTv, mCatalogTv.text.toString())
|
||||
}
|
||||
|
||||
mSizeContainer.setOnClickListener {
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
fun setTypeList(switch: CatalogEntity.CatalogSwitch) {
|
||||
switch.run {
|
||||
if ("on" == hotSort) mTypeFilterArray.add(SortType.RECOMMENDED)
|
||||
if ("on" == newSort) mTypeFilterArray.add(SortType.NEWEST)
|
||||
if ("on" == starSort) mTypeFilterArray.add(SortType.RATING)
|
||||
}
|
||||
if (mTypeFilterArray.isNotEmpty()) mTypeTv.text = mTypeFilterArray[0].value
|
||||
}
|
||||
|
||||
fun setCatalogList(subCatalogList: List<CatalogEntity.SubCatalogEntity>, initCatalogName: String) {
|
||||
mCatalogFilterArray = ArrayList(subCatalogList)
|
||||
mCatalogTv.text = initCatalogName
|
||||
}
|
||||
|
||||
fun setOnConfigSetupListener(onCatalogFilterSetupListener: OnCatalogFilterSetupListener) {
|
||||
mOnCatalogFilterSetupListener = onCatalogFilterSetupListener
|
||||
}
|
||||
|
||||
private fun toggleHighlightedTextView(targetTextView: TextView, highlightIt: Boolean) {
|
||||
if (highlightIt) {
|
||||
targetTextView.background = ContextCompat.getDrawable(targetTextView.context, R.drawable.bg_tag_text)
|
||||
targetTextView.setTextColor(Color.WHITE)
|
||||
} else {
|
||||
targetTextView.background = null
|
||||
targetTextView.setTextColor(R.color.text_757575.toColor(context))
|
||||
}
|
||||
}
|
||||
|
||||
private fun showSelectTypePopupWindow(containerView: View, typeTv: TextView, typeText: String) {
|
||||
typeTv.setTextColor(R.color.text_theme.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
|
||||
val inflater = LayoutInflater.from(typeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
).apply { mTypePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (type in mTypeFilterArray) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = typeTv.context.resources.displayMetrics.widthPixels / 3
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = type.value
|
||||
|
||||
toggleHighlightedTextView(tv, typeText == type.value)
|
||||
|
||||
tv.tag = type.value
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
typeTv.text = type.value
|
||||
|
||||
mOnCatalogFilterSetupListener?.onSetupSortType(type)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
typeTv.setTextColor(R.color.text_757575.toColor(context))
|
||||
typeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
mTypePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
}
|
||||
|
||||
private fun showSelectCatalogPopupWindow(containerView: View, catalogTv: TextView, catalogText: String) {
|
||||
catalogTv.setTextColor(R.color.text_theme.toColor(context))
|
||||
catalogTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
|
||||
val inflater = LayoutInflater.from(catalogTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
).apply { mCatalogPopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (entity in mCatalogFilterArray) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = catalogTv.context.resources.displayMetrics.widthPixels / 3
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
val iv = item.findViewById<ImageView>(R.id.recommend_iv)
|
||||
tv.text = entity.name
|
||||
iv.visibleIf(entity.recommended)
|
||||
|
||||
toggleHighlightedTextView(tv, catalogText == entity.name)
|
||||
|
||||
tv.tag = entity.name
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
catalogTv.text = entity.name
|
||||
|
||||
mOnCatalogFilterSetupListener?.onSetupSortCatalog(entity)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
catalogTv.setTextColor(R.color.text_757575.toColor(context))
|
||||
catalogTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
mCatalogPopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
}
|
||||
|
||||
private fun showSelectSizePopupWindow(containerView: View, sizeTv: TextView, sizeText: String) {
|
||||
sizeTv.setTextColor(R.color.text_theme.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_up)
|
||||
|
||||
val inflater = LayoutInflater.from(sizeTv.context)
|
||||
val layout = inflater.inflate(R.layout.layout_filter_size, null)
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
LayoutParams.MATCH_PARENT,
|
||||
LayoutParams.WRAP_CONTENT
|
||||
).apply { mSizePopupWindow = this }
|
||||
|
||||
val flexboxLayout = layout.findViewById<FlexboxLayout>(R.id.flexbox)
|
||||
val backgroundView = layout.findViewById<View>(R.id.background)
|
||||
|
||||
sizeFilterArray = if (sizeFilterArray == null) {
|
||||
getDefaultSizeFilterArray()
|
||||
} else {
|
||||
sizeFilterArray?.apply {
|
||||
if (firstOrNull()?.text != "全部大小") {
|
||||
add(0, SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
backgroundView.setOnClickListener {
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
|
||||
for (size in sizeFilterArray!!) {
|
||||
val item = inflater.inflate(R.layout.item_filter_size, flexboxLayout, false)
|
||||
|
||||
// 单列 3 个,强行设置宽度为屏幕的 1/3
|
||||
val width = sizeTv.context.resources.displayMetrics.widthPixels / 3
|
||||
val height = item.layoutParams.height
|
||||
|
||||
item.layoutParams = ViewGroup.LayoutParams(width, height)
|
||||
flexboxLayout.addView(item)
|
||||
|
||||
val tv = item.findViewById<TextView>(R.id.size_tv)
|
||||
tv.text = size.text
|
||||
|
||||
toggleHighlightedTextView(tv, sizeText == size.text)
|
||||
|
||||
tv.tag = size.text
|
||||
|
||||
item.setOnClickListener {
|
||||
toggleHighlightedTextView(tv, true)
|
||||
popupWindow.dismiss()
|
||||
sizeTv.text = size.text
|
||||
|
||||
mOnCatalogFilterSetupListener?.onSetupSortSize(size)
|
||||
}
|
||||
}
|
||||
|
||||
popupWindow.setOnDismissListener {
|
||||
sizeTv.setTextColor(R.color.text_757575.toColor(context))
|
||||
sizeTv.setDrawableEnd(R.drawable.ic_filter_arrow_down)
|
||||
mSizePopupWindow = null
|
||||
}
|
||||
|
||||
popupWindow.isTouchable = true
|
||||
popupWindow.isFocusable = true
|
||||
popupWindow.animationStyle = 0
|
||||
popupWindow.showAsDropDown(containerView, 0, 0)
|
||||
}
|
||||
|
||||
private fun getDefaultSizeFilterArray(): ArrayList<SubjectSettingEntity.Size> {
|
||||
return arrayListOf<SubjectSettingEntity.Size>().apply {
|
||||
add(SubjectSettingEntity.Size(min = -1, max = -1, text = "全部大小"))
|
||||
add(SubjectSettingEntity.Size(min = -1, max = 100, text = "100M以下"))
|
||||
add(SubjectSettingEntity.Size(min = 100, max = 300, text = "100-300M"))
|
||||
add(SubjectSettingEntity.Size(min = 300, max = 500, text = "300-500M"))
|
||||
add(SubjectSettingEntity.Size(min = 500, max = 1000, text = "500M-1G"))
|
||||
add(SubjectSettingEntity.Size(min = 1000, max = -1, text = "1G以上"))
|
||||
}
|
||||
}
|
||||
|
||||
fun setRootBackgroundColor(@ColorInt color: Int) {
|
||||
findViewById<View>(R.id.config_controller).setBackgroundColor(color)
|
||||
}
|
||||
|
||||
fun setItemTextColor(@ColorInt color: Int) {
|
||||
mTypeTv.setTextColor(color)
|
||||
mCatalogTv.setTextColor(color)
|
||||
mSizeTv.setTextColor(color)
|
||||
}
|
||||
|
||||
fun updatePopupWindow() {
|
||||
when {
|
||||
mTypePopupWindow != null && mTypePopupWindow!!.isShowing -> {
|
||||
mTypePopupWindow?.dismiss()
|
||||
showSelectTypePopupWindow(this, mTypeTv, mTypeTv.text.toString())
|
||||
}
|
||||
|
||||
mCatalogPopupWindow != null && mCatalogPopupWindow!!.isShowing -> {
|
||||
mCatalogPopupWindow?.dismiss()
|
||||
showSelectCatalogPopupWindow(this, mCatalogTv, mCatalogTv.text.toString())
|
||||
}
|
||||
|
||||
mSizePopupWindow != null && mSizePopupWindow!!.isShowing -> {
|
||||
mSizePopupWindow?.dismiss()
|
||||
showSelectSizePopupWindow(this, mSizeTv, mSizeTv.text.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
interface OnCatalogFilterSetupListener {
|
||||
fun onSetupSortSize(sortSize: SubjectSettingEntity.Size)
|
||||
fun onSetupSortType(sortType: SortType)
|
||||
fun onSetupSortCatalog(sortCatalog: CatalogEntity.SubCatalogEntity)
|
||||
}
|
||||
|
||||
enum class SortType(val value: String) {
|
||||
RECOMMENDED("热门推荐"),
|
||||
NEWEST("最新上线"),
|
||||
RATING("最高评分")
|
||||
}
|
||||
}
|
||||
64
app/src/main/java/com/gh/common/view/SubCategoryView.kt
Normal file
64
app/src/main/java/com/gh/common/view/SubCategoryView.kt
Normal file
@ -0,0 +1,64 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.category.CategoryListActivity
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
|
||||
class SubCategoryView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null, defStyleAttr: Int = 0) :
|
||||
LinearLayout(context, attrs, defStyleAttr) {
|
||||
|
||||
var leftTv: TextView
|
||||
var centerTv: TextView
|
||||
var rightTv: TextView
|
||||
|
||||
var leftDivider: View
|
||||
var rightDivider: View
|
||||
|
||||
var primeCategory: CategoryEntity? = null
|
||||
var categoryTitle: String? = ""
|
||||
|
||||
init {
|
||||
View.inflate(context, R.layout.layout_sub_category, this)
|
||||
|
||||
leftTv = findViewById(R.id.tv_left_sub_category)
|
||||
centerTv = findViewById(R.id.tv_center_sub_category)
|
||||
rightTv = findViewById(R.id.tv_right_sub_category)
|
||||
|
||||
leftDivider = findViewById(R.id.divider_left)
|
||||
rightDivider = findViewById(R.id.divider_right)
|
||||
}
|
||||
|
||||
fun setLeftCategory(category: CategoryEntity) {
|
||||
setCategory(leftTv, category)
|
||||
}
|
||||
|
||||
fun setCenterCategory(category: CategoryEntity) {
|
||||
setCategory(centerTv, category)
|
||||
leftDivider.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
fun setRightCategory(category: CategoryEntity) {
|
||||
setCategory(rightTv, category)
|
||||
rightDivider.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun setCategory(tv: TextView, category: CategoryEntity) {
|
||||
tv.text = category.name
|
||||
tv.setOnClickListener {
|
||||
tv.context.startActivity(
|
||||
CategoryListActivity.getIntent(
|
||||
tv.context,
|
||||
categoryTitle!!,
|
||||
primeCategory!!,
|
||||
category.name!!
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -59,8 +59,8 @@ class GameDetailActivity : DownloadToolbarActivity() {
|
||||
}
|
||||
|
||||
override fun getBusinessId(): Pair<String, String> {
|
||||
val fragment = targetFragment as? GameDetailFragment?
|
||||
return if (fragment?.arguments != null) {
|
||||
val fragment = targetFragment as GameDetailFragment
|
||||
return if (fragment.arguments != null) {
|
||||
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAMEID) ?: "", "")
|
||||
} else {
|
||||
super.getBusinessId()
|
||||
|
||||
@ -5,6 +5,7 @@ import static com.gh.gamecenter.common.constant.EntranceConsts.ENTRANCE_PUSH;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ANSWER;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARCHIVE_LOGIN;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_ARTICLE;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_CATEGORY;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COLUMN;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COLUMN_COLLECTION;
|
||||
import static com.gh.gamecenter.common.constant.EntranceConsts.HOST_COMMUNITY;
|
||||
@ -314,6 +315,10 @@ public class SkipActivity extends BaseActivity {
|
||||
DirectUtils.directToCommunityColumn(this, community, columnId, entrance, "");
|
||||
break;
|
||||
|
||||
case HOST_CATEGORY:
|
||||
title = uri.getQueryParameter("title");
|
||||
DirectUtils.directCategoryDirectory(this, path, title, entrance, pathName);
|
||||
break;
|
||||
case HOST_COLUMN_COLLECTION:
|
||||
DirectUtils.directToColumnCollection(this, path, -1, entrance, "", "", "", "", null,false);
|
||||
break;
|
||||
|
||||
@ -0,0 +1,43 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.base.DownloadToolbarActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
|
||||
class CatalogActivity : DownloadToolbarActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setToolbarMenu(R.menu.menu_download)
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun showDownloadMenu(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, CatalogActivity::class.java, CatalogFragment::class.java)
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun isAutoResetViewBackgroundEnabled() = true
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context, catalogId: String, catalogTitle: String, entrance: String): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceConsts.KEY_CATALOG_ID, catalogId)
|
||||
bundle.putString(EntranceConsts.KEY_CATALOG_TITLE, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_ENTRANCE, entrance)
|
||||
return getTargetIntent(context, CatalogActivity::class.java, CatalogFragment::class.java, bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.goneIf
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.CatalogItemBinding
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class CatalogAdapter(
|
||||
context: Context,
|
||||
private val mFragment: CatalogFragment,
|
||||
private val mViewModel: CatalogViewModel,
|
||||
private val mList: List<CatalogEntity.SubCatalogEntity>
|
||||
) : BaseRecyclerAdapter<CatalogAdapter.CatalogItemViewHolder>(context) {
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
CatalogItemViewHolder(parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: CatalogItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
val catalogEntity = mList[position]
|
||||
catalogName.text = catalogEntity.name
|
||||
recommendTag.goneIf(!catalogEntity.recommended)
|
||||
if (catalogEntity.name == mViewModel.selectedCatalogName) {
|
||||
selectedTag.visibility = View.VISIBLE
|
||||
catalogName.setTextColor(R.color.text_theme.toColor(mContext))
|
||||
root.setBackgroundColor(R.color.ui_surface.toColor(mContext))
|
||||
} else {
|
||||
selectedTag.visibility = View.GONE
|
||||
catalogName.setTextColor(R.color.text_primary.toColor(mContext))
|
||||
root.background = null
|
||||
}
|
||||
root.setOnClickListener {
|
||||
if (catalogEntity.name != mViewModel.selectedCatalogName) {
|
||||
mViewModel.selectedCatalogName = catalogEntity.name
|
||||
mViewModel.logSubCatalogClick(position)
|
||||
mFragment.changeCatalog(position)
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class CatalogItemViewHolder(val binding: CatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
}
|
||||
240
app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt
Normal file
240
app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt
Normal file
@ -0,0 +1,240 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.lifecycle.Observer
|
||||
import com.gh.gamecenter.common.base.fragment.LazyFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
|
||||
import com.gh.gamecenter.common.view.FixLinearLayoutManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.FragmentCatalogBinding
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
|
||||
class CatalogFragment : LazyFragment() {
|
||||
|
||||
private var mBinding: FragmentCatalogBinding? = null
|
||||
private var mViewModel: CatalogViewModel? = null
|
||||
private var mEntity: CatalogEntity? = null
|
||||
private var mSpecialCatalogFragment: SpecialCatalogFragment? = null
|
||||
private var mSubCatalogFragment: SubCatalogFragment? = null
|
||||
|
||||
private var mCatalogId: String = ""
|
||||
private var mCatalogTitle: String = ""
|
||||
private var mLastSelectedPosition = -1
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
savedInstanceState?.run {
|
||||
mLastSelectedPosition = getInt(EntranceConsts.KEY_LAST_SELECTED_POSITION)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSaveInstanceState(outState: Bundle) {
|
||||
mViewModel?.run {
|
||||
outState.putInt(EntranceConsts.KEY_LAST_SELECTED_POSITION, selectedCatalogPosition)
|
||||
}
|
||||
super.onSaveInstanceState(outState)
|
||||
}
|
||||
|
||||
override fun getRealLayoutId() = R.layout.fragment_catalog
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
mBinding = FragmentCatalogBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
mCatalogId = arguments?.getString(EntranceConsts.KEY_CATALOG_ID) ?: ""
|
||||
mCatalogTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
|
||||
mViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId)
|
||||
|
||||
mViewModel?.validEntranceName = if (mEntrance.contains("首页")) "首页" else "板块"
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
|
||||
mViewModel?.validEntranceName = "首页Tab栏"
|
||||
}
|
||||
mViewModel?.logAppearance()
|
||||
|
||||
super.onFragmentFirstVisible()
|
||||
}
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
|
||||
setNavigationTitle(mCatalogTitle)
|
||||
mViewModel?.catalogs?.observe(viewLifecycleOwner, Observer {
|
||||
mBinding?.run {
|
||||
reuseLoading.root.visibility = View.GONE
|
||||
if (it != null) {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
if (it.subCatalog.isNotEmpty()) {
|
||||
containerCatalog.visibility = View.VISIBLE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
mEntity = it
|
||||
if (mEntity!!.hasSpecial) {
|
||||
val specialEntity = CatalogEntity.SubCatalogEntity(name = "精选")
|
||||
(mEntity!!.subCatalog as ArrayList).add(0, specialEntity)
|
||||
}
|
||||
initView()
|
||||
} else {
|
||||
containerCatalog.visibility = View.GONE
|
||||
reuseNoneData.root.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
containerCatalog.visibility = View.GONE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
reuseNoConnection.root.visibility = View.VISIBLE
|
||||
reuseNoConnection.root.setOnClickListener {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
reuseLoading.root.visibility = View.VISIBLE
|
||||
mViewModel?.getCatalogs()
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
// 嵌入在首页时特殊处理
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
|
||||
mBinding?.divider?.visibility = View.GONE
|
||||
mBinding?.root?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
mBinding?.root?.setPadding(0, 8F.dip2px(), 0, 0)
|
||||
mBinding?.rvCatalog?.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
if (subCatalog.isNotEmpty()) {
|
||||
if (mLastSelectedPosition != -1) {
|
||||
selectedCatalogPosition = mLastSelectedPosition
|
||||
selectedCatalogName = subCatalog[mLastSelectedPosition].name
|
||||
} else {
|
||||
selectedCatalogPosition = 0
|
||||
selectedCatalogName = subCatalog[0].name
|
||||
}
|
||||
mBinding?.rvCatalog?.layoutManager = FixLinearLayoutManager(requireContext())
|
||||
mBinding?.rvCatalog?.adapter =
|
||||
CatalogAdapter(requireContext(), this@CatalogFragment, this, subCatalog)
|
||||
|
||||
if (hasSpecial && selectedCatalogPosition == 0) {
|
||||
mSpecialCatalogFragment =
|
||||
childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name) as? SpecialCatalogFragment
|
||||
?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to false,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE
|
||||
)
|
||||
)
|
||||
childFragmentManager.beginTransaction().replace(
|
||||
R.id.container_sub_catalog,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
).commitAllowingStateLoss()
|
||||
} else {
|
||||
mSubCatalogFragment =
|
||||
childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.name) as? SubCatalogFragment
|
||||
?: SubCatalogFragment()
|
||||
mSubCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[selectedCatalogPosition].id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
|
||||
)
|
||||
childFragmentManager.beginTransaction().replace(
|
||||
R.id.container_sub_catalog,
|
||||
mSubCatalogFragment!!,
|
||||
SubCatalogFragment::class.java.name
|
||||
).commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun changeCatalog(position: Int) {
|
||||
mEntity?.run {
|
||||
mViewModel?.run {
|
||||
if (hasSpecial) {
|
||||
changeCatalogIfHasSpecial(position)
|
||||
} else {
|
||||
changeSubCatalogInSubCatalogFragment(position)
|
||||
}
|
||||
selectedCatalogPosition = position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeCatalogIfHasSpecial(position: Int) {
|
||||
mViewModel?.run {
|
||||
when {
|
||||
selectedCatalogPosition == 0 -> changeToSubCatalogFragment(position)
|
||||
position == 0 -> changeToSpecialCatalogFragment()
|
||||
else -> changeSubCatalogInSubCatalogFragment(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeToSubCatalogFragment(position: Int) {
|
||||
mEntity?.run {
|
||||
mSubCatalogFragment =
|
||||
childFragmentManager.findFragmentByTag(SubCatalogFragment::class.java.name) as? SubCatalogFragment
|
||||
?: SubCatalogFragment()
|
||||
mSubCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[position].id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
|
||||
)
|
||||
childFragmentManager.beginTransaction().replace(
|
||||
R.id.container_sub_catalog,
|
||||
mSubCatalogFragment!!,
|
||||
SubCatalogFragment::class.java.name
|
||||
).commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeToSpecialCatalogFragment() {
|
||||
mEntity?.run {
|
||||
mSpecialCatalogFragment =
|
||||
childFragmentManager.findFragmentByTag(SpecialCatalogFragment::class.java.name) as? SpecialCatalogFragment
|
||||
?: SpecialCatalogFragment()
|
||||
mSpecialCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_IS_CATEGORY_V2 to false,
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle,
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE to arguments?.getParcelable(
|
||||
EntranceConsts.KEY_EXPOSURE_SOURCE
|
||||
)
|
||||
)
|
||||
childFragmentManager.beginTransaction().replace(
|
||||
R.id.container_sub_catalog,
|
||||
mSpecialCatalogFragment!!,
|
||||
SpecialCatalogFragment::class.java.name
|
||||
).commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeSubCatalogInSubCatalogFragment(position: Int) {
|
||||
mEntity?.run {
|
||||
if (mSubCatalogFragment?.isStateSaved == false) {
|
||||
mSubCatalogFragment?.arguments = bundleOf(
|
||||
EntranceConsts.KEY_CATALOG_ID to id,
|
||||
EntranceConsts.KEY_PRIMARY_CATALOG_ID to subCatalog[position].id,
|
||||
EntranceConsts.KEY_CATALOG_TITLE to mCatalogTitle
|
||||
)
|
||||
}
|
||||
mSubCatalogFragment?.changeSubCatalog(subCatalog[position].id)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.rvCatalog?.adapter?.run {
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,72 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.util.LogUtils
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class CatalogViewModel(application: Application, val catalogId: String, val catalogTitle: String) :
|
||||
AndroidViewModel(application) {
|
||||
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
var catalogs = MutableLiveData<CatalogEntity>()
|
||||
|
||||
var selectedCatalogName: String = ""
|
||||
var selectedCatalogPosition: Int = 0
|
||||
|
||||
var validEntranceName: String = ""
|
||||
|
||||
init {
|
||||
getCatalogs()
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getCatalogs() {
|
||||
api.getCatalogs(catalogId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<CatalogEntity>() {
|
||||
override fun onSuccess(data: CatalogEntity) {
|
||||
catalogs.postValue(data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
catalogs.postValue(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun logAppearance() {
|
||||
LogUtils.logNewCatalogAppearanceEvent(validEntranceName, catalogTitle)
|
||||
}
|
||||
|
||||
fun logSubCatalogClick(position: Int) {
|
||||
LogUtils.logSubCatalogClickEvent(validEntranceName, "${catalogTitle}_${selectedCatalogName}", position)
|
||||
}
|
||||
|
||||
fun logSubCatalogContentClick(itemName: String, listPosition: Int) {
|
||||
LogUtils.logSubCatalogContentClickEvent(
|
||||
validEntranceName,
|
||||
"${catalogTitle}_${selectedCatalogName}_${itemName}",
|
||||
selectedCatalogPosition,
|
||||
listPosition
|
||||
)
|
||||
}
|
||||
|
||||
class Factory(private val catalogId: String, private val catalogTitle: String) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return CatalogViewModel(HaloApp.getInstance().application, catalogId, catalogTitle) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,56 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.base.DownloadToolbarActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
|
||||
class NewCatalogListActivity : DownloadToolbarActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setToolbarMenu(R.menu.menu_download)
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun showDownloadMenu(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun isAutoResetViewBackgroundEnabled() = true
|
||||
|
||||
companion object {
|
||||
fun getIntent(
|
||||
context: Context,
|
||||
primaryCatalogId: String, // 一级分类 id
|
||||
primaryCatalogName: String, // 一级分类名
|
||||
catalogTitle: String,
|
||||
catalog: CatalogEntity,
|
||||
initTitle: String
|
||||
): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(EntranceConsts.KEY_DATA, catalog)
|
||||
bundle.putString(EntranceConsts.KEY_PRIMARY_CATALOG_ID, primaryCatalogId)
|
||||
bundle.putString(EntranceConsts.KEY_PRIMARY_CATALOG_NAME, primaryCatalogName)
|
||||
bundle.putString(EntranceConsts.KEY_NAME, catalog.name)
|
||||
bundle.putString(EntranceConsts.KEY_CATALOG_TITLE, catalogTitle)
|
||||
bundle.putString(EntranceConsts.KEY_CATALOG_INIT_TITLE, initTitle)
|
||||
return getTargetIntent(
|
||||
context,
|
||||
NewCatalogListActivity::class.java,
|
||||
NewCatalogListFragment::class.java,
|
||||
bundle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,192 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.IExposable
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
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.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
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.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
class NewCatalogListAdapter(
|
||||
context: Context,
|
||||
private val mBaseExposureSource: ExposureSource,
|
||||
private val mViewModel: NewCatalogListViewModel,
|
||||
private val mEntrance: String?
|
||||
) : ListAdapter<GameEntity>(context), IExposable {
|
||||
|
||||
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
|
||||
|
||||
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]
|
||||
var packages = gameEntity.id
|
||||
for (apkEntity in gameEntity.getApk()) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
positionAndPackageMap[packages + i] = i
|
||||
}
|
||||
}
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
fun clearPositionAndPackageMap() {
|
||||
positionAndPackageMap.clear()
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == itemCount - 1) {
|
||||
return ItemViewType.ITEM_FOOTER
|
||||
}
|
||||
return ItemViewType.GAME_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
GameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is GameItemViewHolder) {
|
||||
val padTop = if (position == 0) 16F.dip2px() else 8F.dip2px()
|
||||
holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 8F.dip2px())
|
||||
|
||||
val gameEntity = mEntityList[position]
|
||||
|
||||
holder.bindGameItem(gameEntity)
|
||||
holder.initServerType(gameEntity)
|
||||
|
||||
val sortType = mViewModel.sortType.value
|
||||
val sortSize = mViewModel.sortSize.text
|
||||
val toolbarTitle = mViewModel.title
|
||||
val selectedCatalogName = mViewModel.selectedCatalog.name
|
||||
|
||||
val exposureSources = ArrayList<ExposureSource>()
|
||||
exposureSources.add(mBaseExposureSource)
|
||||
exposureSources.add(ExposureSource(toolbarTitle))
|
||||
exposureSources.add(ExposureSource("二级分类详情", "$selectedCatalogName+$sortType+$sortSize"))
|
||||
|
||||
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
|
||||
mExposureEventSparseArray.put(position, event)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
mContext,
|
||||
gameEntity,
|
||||
StringUtils.buildString(
|
||||
mEntrance,
|
||||
"+(",
|
||||
toolbarTitle,
|
||||
":列表[",
|
||||
selectedCatalogName,
|
||||
"=",
|
||||
sortType,
|
||||
"=",
|
||||
(position + 1).toString(),
|
||||
"])"
|
||||
),
|
||||
traceEvent = event
|
||||
)
|
||||
}
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
mContext,
|
||||
holder.binding.downloadBtn,
|
||||
gameEntity,
|
||||
position,
|
||||
this,
|
||||
StringUtils.buildString(
|
||||
StringUtils.buildString(
|
||||
mEntrance,
|
||||
"+(",
|
||||
toolbarTitle,
|
||||
":列表[",
|
||||
selectedCatalogName,
|
||||
"=",
|
||||
sortType,
|
||||
"=",
|
||||
(position + 1).toString(),
|
||||
"])"
|
||||
)
|
||||
),
|
||||
location = StringUtils.buildString(selectedCatalogName, ":", gameEntity.name),
|
||||
traceEvent = event
|
||||
)
|
||||
|
||||
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding), "star&brief")
|
||||
} else if (holder is FooterViewHolder) {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
}
|
||||
}
|
||||
|
||||
fun notifyItemByDownload(download: DownloadEntity) {
|
||||
for (key in positionAndPackageMap.keys) {
|
||||
// sentry上报download.packageName可能为空
|
||||
if (download.packageName != null
|
||||
&& download.gameId != null
|
||||
&& 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEventByPosition(pos: Int): ExposureEvent? {
|
||||
return mExposureEventSparseArray.get(pos)
|
||||
}
|
||||
|
||||
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,209 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.view.CatalogFilterView
|
||||
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.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.observeNonNull
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.databinding.FragmentCatalogListBinding
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class NewCatalogListFragment : ListFragment<GameEntity, NewCatalogListViewModel>() {
|
||||
|
||||
private var mPrimaryCatalogName: String = "" // 一级分类名
|
||||
private var mPrimaryCatalogId: String = "" // 一级分类 id
|
||||
private var mPrimeCatalog: CatalogEntity? = null
|
||||
private var mSubCatalogList = arrayListOf<CatalogEntity.SubCatalogEntity>()
|
||||
private var mInitCatalogName = ""
|
||||
private var mAdapter: NewCatalogListAdapter? = null
|
||||
private val mDataWatcher = 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)
|
||||
}
|
||||
}
|
||||
|
||||
private var mBinding: FragmentCatalogListBinding? = null
|
||||
private lateinit var mExposureListener: ExposureListener
|
||||
private lateinit var mViewModel: NewCatalogListViewModel
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() = FragmentCatalogListBinding.inflate(layoutInflater).apply { mBinding = this }.root
|
||||
|
||||
override fun provideListViewModel() = viewModelProvider<NewCatalogListViewModel>()
|
||||
|
||||
override fun provideListAdapter() = mAdapter
|
||||
?: NewCatalogListAdapter(
|
||||
requireContext(),
|
||||
ExposureSource(mPrimaryCatalogName),
|
||||
mViewModel,
|
||||
mEntrance
|
||||
).apply { mAdapter = this }
|
||||
|
||||
override fun getItemDecoration() = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mViewModel = provideListViewModel()
|
||||
|
||||
mViewModel.title = arguments?.getString(EntranceConsts.KEY_NAME) ?: ""
|
||||
mViewModel.categoryTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
|
||||
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
|
||||
mPrimeCatalog = arguments?.getParcelable(EntranceConsts.KEY_DATA)
|
||||
mSubCatalogList = mPrimeCatalog?.subCatalog as? ArrayList<CatalogEntity.SubCatalogEntity> ?: arrayListOf()
|
||||
|
||||
mInitCatalogName = arguments?.getString(EntranceConsts.KEY_CATALOG_INIT_TITLE) ?: ""
|
||||
mPrimaryCatalogName = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_NAME) ?: ""
|
||||
mPrimaryCatalogId = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_ID) ?: ""
|
||||
|
||||
mViewModel.selectedCatalog = mSubCatalogList.find { entity -> entity.name == mInitCatalogName }
|
||||
?: CatalogEntity.SubCatalogEntity()
|
||||
mViewModel.primaryCatalogId = mPrimaryCatalogId
|
||||
initSortType()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setNavigationTitle(mViewModel.title)
|
||||
|
||||
initFilterView()
|
||||
|
||||
mViewModel.refresh.observeNonNull(this) { onRefresh() }
|
||||
|
||||
mExposureListener = ExposureListener(this, mAdapter!!)
|
||||
mListRv.addOnScrollListener(mExposureListener)
|
||||
|
||||
mSkeletonScreen = Skeleton.bind(mBinding?.listSkeleton)
|
||||
.shimmer(true)
|
||||
.angle(Constants.SHIMMER_ANGLE)
|
||||
.color(R.color.ui_skeleton_highlight)
|
||||
.duration(Constants.SHIMMER_DURATION)
|
||||
.maskWidth(Constants.MASK_WIDTH)
|
||||
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
|
||||
.load(R.layout.fragment_tags_skeleton)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun initSortType() {
|
||||
mPrimeCatalog?.switch?.run {
|
||||
if (hotSort == "on") {
|
||||
mViewModel.sortType = CatalogFilterView.SortType.RECOMMENDED
|
||||
return@run
|
||||
}
|
||||
if (newSort == "on") {
|
||||
mViewModel.sortType = CatalogFilterView.SortType.NEWEST
|
||||
return@run
|
||||
}
|
||||
if (starSort == "on") {
|
||||
mViewModel.sortType = CatalogFilterView.SortType.RATING
|
||||
return@run
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initFilterView() {
|
||||
mBinding?.filterContainer?.run {
|
||||
visibility = View.VISIBLE
|
||||
setTypeList(mPrimeCatalog?.switch ?: CatalogEntity.CatalogSwitch())
|
||||
setCatalogList(mSubCatalogList, mInitCatalogName)
|
||||
setOnConfigSetupListener(object : CatalogFilterView.OnCatalogFilterSetupListener {
|
||||
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
|
||||
mViewModel.updateSortConfig(sortSize = sortSize)
|
||||
}
|
||||
|
||||
override fun onSetupSortType(sortType: CatalogFilterView.SortType) {
|
||||
mViewModel.updateSortConfig(sortType = sortType)
|
||||
}
|
||||
|
||||
override fun onSetupSortCatalog(sortCatalog: CatalogEntity.SubCatalogEntity) {
|
||||
mViewModel.updateSortConfig(sortCatalog = sortCatalog)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
DownloadManager.getInstance().addObserver(mDataWatcher)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance().removeObserver(mDataWatcher)
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
mAdapter?.clearPositionAndPackageMap()
|
||||
|
||||
super.onRefresh()
|
||||
}
|
||||
|
||||
// 下载被删除事件
|
||||
@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.isInstalledOrUninstalled()) {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.filterContainer?.run {
|
||||
setRootBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
setItemTextColor(R.color.text_secondary.toColor(requireContext()))
|
||||
updatePopupWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,98 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.common.entity.ExposureEntity
|
||||
import com.gh.common.exposure.ExposureUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.common.view.CatalogFilterView
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
class NewCatalogListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
var title = "" // 显示在 Toolbar 的标题
|
||||
var categoryTitle = "" // 跳转进来的分类的标题
|
||||
|
||||
val refresh = MutableLiveData<Boolean>()
|
||||
|
||||
var selectedCatalog = CatalogEntity.SubCatalogEntity()
|
||||
var sortType = CatalogFilterView.SortType.RECOMMENDED
|
||||
var sortSize = SubjectSettingEntity.Size()
|
||||
|
||||
var primaryCatalogId = "" // 一级分类 id
|
||||
private val sensitiveApi = RetrofitManager.getInstance().api
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? = null
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
|
||||
return if (selectedCatalog.link.type == "column") { // column(专题)/tag(标签)
|
||||
sensitiveApi.getColumn(selectedCatalog.link.link, getSortType(), getSortSize(), page) // 专题
|
||||
} else {
|
||||
sensitiveApi.getGamesWithSpecificTag(getSortSize(), getSortType(), page) // 标签
|
||||
}
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
it.forEach { game -> game.hideSizeInsideDes = true }
|
||||
ExposureUtils.updateExposureInfo(
|
||||
gameList = it,
|
||||
containerId = primaryCatalogId,
|
||||
containerType = ExposureEntity.CATEGORY_ID
|
||||
)
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSortConfig(
|
||||
sortSize: SubjectSettingEntity.Size? = null,
|
||||
sortType: CatalogFilterView.SortType? = null,
|
||||
sortCatalog: CatalogEntity.SubCatalogEntity? = null
|
||||
) {
|
||||
when {
|
||||
sortSize != null && sortSize != this.sortSize -> {
|
||||
this.sortSize = sortSize
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
sortType != null && sortType != this.sortType -> {
|
||||
this.sortType = sortType
|
||||
refresh.postValue(true)
|
||||
}
|
||||
|
||||
sortCatalog != null && sortCatalog != selectedCatalog -> {
|
||||
selectedCatalog = sortCatalog
|
||||
refresh.postValue(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSortSize(): String? {
|
||||
return if (selectedCatalog.link.type == "column") {
|
||||
UrlFilterUtils.getFilterQuery(
|
||||
"min_size", sortSize.min.toString(),
|
||||
"max_size", sortSize.max.toString()
|
||||
)
|
||||
} else {
|
||||
UrlFilterUtils.getFilterQuery(
|
||||
"tag_id", selectedCatalog.link.link,
|
||||
"min_size", sortSize.min.toString(),
|
||||
"max_size", sortSize.max.toString()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSortType(): String? {
|
||||
return when (sortType) {
|
||||
CatalogFilterView.SortType.RECOMMENDED -> if (selectedCatalog.link.type == "column") "position:1" else "download:-1"
|
||||
CatalogFilterView.SortType.NEWEST -> "publish:-1"
|
||||
CatalogFilterView.SortType.RATING -> "star:-1"
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.*
|
||||
import com.gh.gamecenter.databinding.SubCatalogItemBinding
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class SubCatalogAdapter(
|
||||
context: Context,
|
||||
private val mCatalogViewModel: CatalogViewModel,
|
||||
private val mPrimaryCatalog: CatalogEntity,
|
||||
private var mList: List<CatalogEntity.SubCatalogEntity>
|
||||
) : BaseRecyclerAdapter<SubCatalogAdapter.SubCatalogItemViewHolder>(context) {
|
||||
|
||||
private val mTypes = listOf("专题", "标签")
|
||||
|
||||
override fun getItemCount() = mList.size
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
SubCatalogItemViewHolder(parent.toBinding())
|
||||
|
||||
override fun onBindViewHolder(holder: SubCatalogItemViewHolder, position: Int) {
|
||||
holder.binding.run {
|
||||
val catalogEntity = mList[position]
|
||||
ImageUtils.display(catalogIcon, catalogEntity.icon)
|
||||
catalogName.text = catalogEntity.name
|
||||
catalogName.setTextColor(R.color.text_primary.toColor(mContext))
|
||||
recommendTag.goneIf(!catalogEntity.recommended)
|
||||
root.setOnClickListener {
|
||||
mCatalogViewModel.logSubCatalogContentClick(catalogEntity.name, position)
|
||||
if (mTypes.contains(catalogEntity.type)) {
|
||||
root.context.startActivity(
|
||||
NewCatalogListActivity.getIntent(
|
||||
mContext,
|
||||
mCatalogViewModel.catalogId,
|
||||
mCatalogViewModel.catalogTitle,
|
||||
catalogEntity.name,
|
||||
mPrimaryCatalog,
|
||||
catalogEntity.name
|
||||
)
|
||||
)
|
||||
} else {
|
||||
DialogHelper.showUpgradeDialog(mContext)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class SubCatalogItemViewHolder(val binding: SubCatalogItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
}
|
||||
@ -0,0 +1,94 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.lifecycle.Observer
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.common.utils.viewModelProviderFromParent
|
||||
import com.gh.gamecenter.databinding.FragmentSubCatalogBinding
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
|
||||
|
||||
class SubCatalogFragment : ToolbarFragment() {
|
||||
|
||||
private var mBinding: FragmentSubCatalogBinding? = null
|
||||
private var mViewModel: SubCatalogViewModel? = null
|
||||
private var mCatalogViewModel: CatalogViewModel? = null
|
||||
private var mEntity: CatalogEntity? = null
|
||||
private var mCatalogId: String = ""
|
||||
private var mCatalogTitle: String = ""
|
||||
private var mPrimaryCatalogId: String = ""
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() = FragmentSubCatalogBinding.inflate(layoutInflater).apply { mBinding = this }.root
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mCatalogId = arguments?.getString(EntranceConsts.KEY_CATALOG_ID) ?: ""
|
||||
mCatalogTitle = arguments?.getString(EntranceConsts.KEY_CATALOG_TITLE) ?: ""
|
||||
mPrimaryCatalogId = arguments?.getString(EntranceConsts.KEY_PRIMARY_CATALOG_ID) ?: ""
|
||||
mViewModel = viewModelProvider(SubCatalogViewModel.Factory(mCatalogId))
|
||||
mCatalogViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId)
|
||||
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mViewModel?.catalogs?.observe(viewLifecycleOwner, Observer {
|
||||
mBinding?.run {
|
||||
reuseLoading.root.visibility = View.GONE
|
||||
if (it != null) {
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
if (it.subCatalog.isNotEmpty()) {
|
||||
rvSubCatalog.visibility = View.VISIBLE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
mEntity = it
|
||||
initView()
|
||||
} else {
|
||||
rvSubCatalog.visibility = View.GONE
|
||||
reuseNoneData.root.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
rvSubCatalog.visibility = View.GONE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
reuseNoConnection.root.visibility = View.VISIBLE
|
||||
reuseNoConnection.root.setOnClickListener {
|
||||
reuseLoading.root.visibility = View.VISIBLE
|
||||
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
mEntity?.takeIf { mCatalogViewModel != null }?.run {
|
||||
mBinding?.rvSubCatalog?.layoutManager = GridLayoutManager(requireContext(), 3)
|
||||
mBinding?.rvSubCatalog?.adapter = SubCatalogAdapter(requireContext(), mCatalogViewModel!!, this, subCatalog)
|
||||
}
|
||||
}
|
||||
|
||||
fun changeSubCatalog(primaryCatalogId: String) {
|
||||
mBinding?.run {
|
||||
mPrimaryCatalogId = primaryCatalogId
|
||||
rvSubCatalog.visibility = View.GONE
|
||||
reuseNoneData.root.visibility = View.GONE
|
||||
reuseNoConnection.root.visibility = View.GONE
|
||||
reuseLoading.root.visibility = View.VISIBLE
|
||||
mViewModel?.getSubCatalogs(mPrimaryCatalogId)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding?.rvSubCatalog?.adapter?.run {
|
||||
mBinding?.rvSubCatalog?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.gh.gamecenter.catalog
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.gamecenter.entity.CatalogEntity
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class SubCatalogViewModel(application: Application, private val catalogId: String) : AndroidViewModel(application) {
|
||||
|
||||
private val api = RetrofitManager.getInstance().api
|
||||
var catalogs = MutableLiveData<CatalogEntity>()
|
||||
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getSubCatalogs(primaryCatalogId: String) {
|
||||
api.getSubCatalogs(catalogId, primaryCatalogId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<CatalogEntity>() {
|
||||
override fun onSuccess(data: CatalogEntity) {
|
||||
catalogs.postValue(data)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
catalogs.postValue(null)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class Factory(private val catalogId: String) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return SubCatalogViewModel(HaloApp.getInstance().application, catalogId) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.base.DownloadToolbarActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
|
||||
class CategoryDirectoryActivity : DownloadToolbarActivity() {
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context, categoryId: String, categoryTitle: String): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_ID, categoryId)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, categoryTitle)
|
||||
return getTargetIntent(
|
||||
context,
|
||||
CategoryDirectoryActivity::class.java,
|
||||
CategoryDirectoryFragment::class.java,
|
||||
bundle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setToolbarMenu(R.menu.menu_download)
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun showDownloadMenu(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getActivityNameInChinese(): String {
|
||||
return "游戏分类"
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, CategoryDirectoryActivity::class.java, CategoryDirectoryFragment::class.java)
|
||||
}
|
||||
|
||||
override fun isAutoResetViewBackgroundEnabled() = true
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,157 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.view.SubCategoryView
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.category.CategoryListActivity.Companion.getIntent
|
||||
import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.databinding.ItemCategoryBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import net.cachapa.expandablelayout.ExpandableLayout
|
||||
|
||||
class CategoryDirectoryAdapter(context: Context, var categoryTitle: String) : ListAdapter<CategoryEntity>(context) {
|
||||
|
||||
var recyclerView: RecyclerView? = null
|
||||
var expandStatusMap: HashMap<Int, Boolean> = HashMap()
|
||||
|
||||
private var sixteenDp: Int = 0
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
if (sixteenDp == 0) sixteenDp = DisplayUtils.dip2px(parent.context, 16f)
|
||||
return CategoryViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return mEntityList.size
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CategoryViewHolder -> {
|
||||
val category = mEntityList[position]
|
||||
holder.binding.run {
|
||||
divider.setBackgroundColor(R.color.ui_background.toColor(mContext))
|
||||
containerPrimaryCategory.setOnClickListener {
|
||||
root.context.startActivity(getIntent(root.context, categoryTitle, category, "全部"))
|
||||
}
|
||||
ImageUtils.display(iconIv, category.icon)
|
||||
categoryName.text = category.name
|
||||
}
|
||||
holder.bindCategory(
|
||||
category = mEntityList[position],
|
||||
expandableStatusMap = expandStatusMap,
|
||||
isExpended = expandStatusMap[position] != null && expandStatusMap[position] == true,
|
||||
marginTop = sixteenDp,
|
||||
categoryTitle = categoryTitle,
|
||||
expandedAction = { recyclerView?.smoothScrollToPosition(position) })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class CategoryViewHolder(var binding: ItemCategoryBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindCategory(
|
||||
category: CategoryEntity,
|
||||
expandableStatusMap: HashMap<Int, Boolean>,
|
||||
isExpended: Boolean,
|
||||
marginTop: Int,
|
||||
categoryTitle: String,
|
||||
expandedAction: () -> Unit
|
||||
) {
|
||||
category.data?.let {
|
||||
var subCategoryView: SubCategoryView? = null
|
||||
|
||||
binding.containerUnexpandable.removeAllViews()
|
||||
val unexpandableSize = if (it.size > 6) 6 else it.size
|
||||
val unexpandableCategoryList = it.subList(0, unexpandableSize)
|
||||
unexpandableCategoryList.forEachIndexed { index, c ->
|
||||
when (index % 3) {
|
||||
0 -> {
|
||||
val params = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
if (subCategoryView != null) params.setMargins(0, marginTop, 0, 0)
|
||||
|
||||
subCategoryView = SubCategoryView(binding.root.context)
|
||||
|
||||
subCategoryView?.categoryTitle = categoryTitle
|
||||
subCategoryView?.primeCategory = category
|
||||
subCategoryView?.layoutParams = params
|
||||
|
||||
binding.containerUnexpandable.addView(subCategoryView)
|
||||
subCategoryView?.setLeftCategory(c)
|
||||
}
|
||||
1 -> subCategoryView?.setCenterCategory(c)
|
||||
2 -> subCategoryView?.setRightCategory(c)
|
||||
}
|
||||
}
|
||||
|
||||
if (it.size >= 7) {
|
||||
val extraCategoryList = it.subList(6, it.size)
|
||||
|
||||
binding.ivToggle.visibility = View.VISIBLE
|
||||
binding.ivToggle.setOnClickListener { binding.containerExpandable.toggle() }
|
||||
when {
|
||||
isExpended -> binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_up)
|
||||
else -> binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_down)
|
||||
}
|
||||
|
||||
val subCategoryViewLinearLayout = LinearLayout(binding.root.context)
|
||||
subCategoryViewLinearLayout.orientation = LinearLayout.VERTICAL
|
||||
subCategoryView = SubCategoryView(binding.root.context)
|
||||
|
||||
binding.containerExpandable.removeAllViews()
|
||||
binding.containerExpandable.addView(subCategoryViewLinearLayout)
|
||||
binding.containerExpandable.setExpanded(isExpended, false)
|
||||
binding.containerExpandable.setOnExpansionUpdateListener { _, state ->
|
||||
when (state) {
|
||||
ExpandableLayout.State.COLLAPSED -> {
|
||||
binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_down)
|
||||
expandableStatusMap[adapterPosition] = false
|
||||
}
|
||||
ExpandableLayout.State.EXPANDED -> {
|
||||
binding.ivToggle.setImageResource(R.drawable.ic_category_arrow_up)
|
||||
expandableStatusMap[adapterPosition] = true
|
||||
expandedAction.invoke()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
extraCategoryList.forEachIndexed { index, c ->
|
||||
when (index % 3) {
|
||||
0 -> {
|
||||
subCategoryView = SubCategoryView(binding.root.context)
|
||||
|
||||
val params = LinearLayout.LayoutParams(
|
||||
LinearLayout.LayoutParams.MATCH_PARENT,
|
||||
LinearLayout.LayoutParams.MATCH_PARENT
|
||||
)
|
||||
params.setMargins(0, marginTop, 0, 0)
|
||||
|
||||
subCategoryView?.categoryTitle = categoryTitle
|
||||
subCategoryView?.primeCategory = category
|
||||
subCategoryView?.layoutParams = params
|
||||
|
||||
subCategoryViewLinearLayout.addView(subCategoryView)
|
||||
subCategoryView?.setLeftCategory(c)
|
||||
}
|
||||
1 -> subCategoryView?.setCenterCategory(c)
|
||||
2 -> subCategoryView?.setRightCategory(c)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
binding.ivToggle.visibility = View.GONE
|
||||
binding.containerExpandable.removeAllViews()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,104 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.graphics.Color
|
||||
import android.view.View
|
||||
import androidx.lifecycle.ViewModelProviders
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.baselist.LazyListFragment
|
||||
import com.gh.gamecenter.databinding.FragmentListBaseSkeletonBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
|
||||
class CategoryDirectoryFragment : LazyListFragment<CategoryEntity, CategoryDirectoryListViewModel>() {
|
||||
|
||||
private lateinit var mViewModel: CategoryDirectoryListViewModel
|
||||
private lateinit var mAdapter: CategoryDirectoryAdapter
|
||||
private var mBinding: FragmentListBaseSkeletonBinding? = null
|
||||
|
||||
override fun getRealLayoutId() = R.layout.fragment_list_base_skeleton
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
super.onRealLayoutInflated(inflatedView)
|
||||
mBinding = FragmentListBaseSkeletonBinding.bind(inflatedView)
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
mViewModel = ViewModelProviders.of(this).get(CategoryDirectoryListViewModel::class.java)
|
||||
mViewModel.categoryId = arguments?.getString(EntranceConsts.KEY_CATEGORY_ID) ?: ""
|
||||
mAdapter =
|
||||
CategoryDirectoryAdapter(requireContext(), arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: "")
|
||||
super.onFragmentFirstVisible()
|
||||
}
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
|
||||
setNavigationTitle(arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE))
|
||||
|
||||
mSkeletonScreen = Skeleton.bind(mBinding?.listSkeleton)
|
||||
.shimmer(true)
|
||||
.angle(Constants.SHIMMER_ANGLE)
|
||||
.color(R.color.ui_skeleton_highlight)
|
||||
.duration(Constants.SHIMMER_DURATION)
|
||||
.maskWidth(Constants.MASK_WIDTH)
|
||||
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
|
||||
.load(R.layout.fragment_category_skeleton)
|
||||
.show()
|
||||
onLoadRefresh()
|
||||
|
||||
if (arguments?.getBoolean(EntranceConsts.KEY_IS_HOME) == true) {
|
||||
mListRv?.setPadding(
|
||||
0,
|
||||
context?.resources?.getDimension(R.dimen.home_recyclerview_padding_top)?.toInt() ?: 0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
mListRv?.clipToPadding = false
|
||||
}
|
||||
|
||||
mListRv?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
mAdapter.recyclerView = mListRv
|
||||
mListRefresh?.isEnabled = false
|
||||
}
|
||||
|
||||
override fun isAutomaticLoad() = false
|
||||
|
||||
override fun provideListAdapter() = mAdapter
|
||||
|
||||
override fun provideListViewModel() = mViewModel
|
||||
|
||||
override fun getItemDecoration() = VerticalItemDecoration(context, 0F, false)
|
||||
|
||||
override fun onLoadRefresh() {
|
||||
mCachedView?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
super.onLoadRefresh()
|
||||
}
|
||||
|
||||
override fun onLoadDone() {
|
||||
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
|
||||
super.onLoadDone()
|
||||
}
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
|
||||
super.onLoadEmpty()
|
||||
}
|
||||
|
||||
override fun onLoadError() {
|
||||
mCachedView?.setBackgroundColor(Color.TRANSPARENT)
|
||||
super.onLoadError()
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mListRv?.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
mListRv?.adapter?.run {
|
||||
mListRv?.recycledViewPool?.clear()
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.app.Application
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.Observable
|
||||
|
||||
class CategoryDirectoryListViewModel(application: Application) :
|
||||
ListViewModel<CategoryEntity, CategoryEntity>(application) {
|
||||
|
||||
var categoryId = ""
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData, mResultLiveData::postValue)
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<CategoryEntity>> {
|
||||
return RetrofitManager.getInstance().api.getCategories(categoryId, page)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,53 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.base.DownloadToolbarActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
|
||||
class CategoryListActivity : DownloadToolbarActivity() {
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context, categoryTitle: String, category: CategoryEntity, initTitle: String): Intent {
|
||||
val bundle = Bundle()
|
||||
if (category.data!!.isEmpty() || category.data!![0].name != "全部") {
|
||||
val plainCategory = CategoryEntity(id = category.id, name = "全部")
|
||||
((category.data) as ArrayList).add(0, plainCategory)
|
||||
}
|
||||
bundle.putParcelable(EntranceConsts.KEY_DATA, category)
|
||||
bundle.putString(EntranceConsts.KEY_NAME, category.name)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_TITLE, categoryTitle)
|
||||
bundle.putString(EntranceConsts.KEY_CATEGORY_INIT_TITLE, initTitle)
|
||||
return getTargetIntent(
|
||||
context,
|
||||
CategoryListActivity::class.java,
|
||||
NewCategoryListFragment::class.java,
|
||||
bundle
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setToolbarMenu(R.menu.menu_download)
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
|
||||
override fun showDownloadMenu(): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun getActivityNameInChinese(): String {
|
||||
return "游戏分类详情"
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
updateStatusBarColor(R.color.ui_surface, R.color.ui_surface)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import com.gh.gamecenter.common.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.toDrawable
|
||||
import com.gh.gamecenter.databinding.ItemTagBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class NewCategoryHorizontalAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: NewCategoryListViewModel,
|
||||
private val mCategoryList: List<CategoryEntity>,
|
||||
private val mSmoothScrollAction: (position: Int) -> Unit
|
||||
) : BaseRecyclerAdapter<TagsViewHolder>(context) {
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): TagsViewHolder {
|
||||
return TagsViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: TagsViewHolder, position: Int) {
|
||||
val categoryEntity = mCategoryList[position]
|
||||
|
||||
if (mViewModel.selectedCategory.name == categoryEntity.name) {
|
||||
holder.binding.tagTv.background = R.drawable.bg_tag_text.toDrawable()
|
||||
holder.binding.tagTv.setTextColor(R.color.text_white.toColor(mContext))
|
||||
} else {
|
||||
holder.binding.tagTv.background = null
|
||||
holder.binding.tagTv.setTextColor(R.color.text_757575.toColor(mContext))
|
||||
}
|
||||
|
||||
holder.binding.tagTv.text = categoryEntity.name
|
||||
holder.binding.tagTv.setOnClickListener {
|
||||
mViewModel.changeSelectedCategory(categoryEntity)
|
||||
notifyDataSetChanged()
|
||||
|
||||
mSmoothScrollAction.invoke(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return mCategoryList.size
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class TagsViewHolder(val binding: ItemTagBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
@ -0,0 +1,185 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.content.Context
|
||||
import android.util.SparseArray
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.IExposable
|
||||
import com.gh.common.util.DownloadItemUtils
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
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.exposure.ExposureSource
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.common.utils.toBinding
|
||||
import com.gh.gamecenter.common.viewholder.FooterViewHolder
|
||||
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.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.ExposureType
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.lightgame.download.DownloadEntity
|
||||
|
||||
class NewCategoryListAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: NewCategoryListViewModel,
|
||||
private val mEntrance: String?
|
||||
) : ListAdapter<GameEntity>(context), IExposable {
|
||||
|
||||
private val mExposureEventSparseArray: SparseArray<ExposureEvent> = SparseArray()
|
||||
|
||||
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]
|
||||
var packages = gameEntity.id
|
||||
for (apkEntity in gameEntity.getApk()) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
positionAndPackageMap[packages + i] = i
|
||||
}
|
||||
}
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
fun clearPositionAndPackageMap() {
|
||||
positionAndPackageMap.clear()
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
if (position == itemCount - 1) {
|
||||
return ItemViewType.ITEM_FOOTER
|
||||
}
|
||||
return ItemViewType.GAME_NORMAL
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ItemViewType.GAME_NORMAL -> {
|
||||
GameItemViewHolder(parent.toBinding())
|
||||
}
|
||||
else -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is GameItemViewHolder) {
|
||||
val gameEntity = mEntityList[position]
|
||||
|
||||
holder.bindGameItem(gameEntity)
|
||||
holder.initServerType(gameEntity)
|
||||
|
||||
val padTop = if (position == 0) 16F.dip2px() else 8F.dip2px()
|
||||
holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 8F.dip2px())
|
||||
|
||||
val sortType = if ("download:-1" == mViewModel.getSortType()) "最热" else "最新"
|
||||
val toolbarTitle = mViewModel.title
|
||||
val categoryTitle = mViewModel.categoryTitle
|
||||
val selectedCategoryName = mViewModel.selectedCategory.name ?: ""
|
||||
|
||||
val exposureSources = ArrayList<ExposureSource>()
|
||||
exposureSources.add(ExposureSource(categoryTitle, selectedCategoryName))
|
||||
exposureSources.add(ExposureSource("二级分类", "$selectedCategoryName+$sortType"))
|
||||
|
||||
val event = ExposureEvent.createEvent(gameEntity, exposureSources, null, ExposureType.EXPOSURE)
|
||||
mExposureEventSparseArray.put(position, event)
|
||||
|
||||
holder.itemView.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
mContext,
|
||||
gameEntity,
|
||||
StringUtils.buildString(
|
||||
mEntrance,
|
||||
"+(",
|
||||
toolbarTitle,
|
||||
":列表[",
|
||||
selectedCategoryName,
|
||||
"=",
|
||||
sortType,
|
||||
"=",
|
||||
(position + 1).toString(),
|
||||
"])"
|
||||
),
|
||||
traceEvent = event
|
||||
)
|
||||
}
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
mContext,
|
||||
holder.binding.downloadBtn,
|
||||
gameEntity,
|
||||
position,
|
||||
this,
|
||||
StringUtils.buildString(
|
||||
StringUtils.buildString(
|
||||
mEntrance,
|
||||
"+(",
|
||||
toolbarTitle,
|
||||
":列表[",
|
||||
selectedCategoryName,
|
||||
"=",
|
||||
sortType,
|
||||
"=",
|
||||
(position + 1).toString(),
|
||||
"])"
|
||||
)
|
||||
),
|
||||
location = StringUtils.buildString(selectedCategoryName, ":", gameEntity.name),
|
||||
traceEvent = event
|
||||
)
|
||||
|
||||
DownloadItemUtils.updateItem(mContext, gameEntity, GameViewHolder(holder.binding))
|
||||
} else if (holder is FooterViewHolder) {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEventByPosition(pos: Int): ExposureEvent? {
|
||||
return mExposureEventSparseArray.get(pos)
|
||||
}
|
||||
|
||||
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
|
||||
return null
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,198 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.view.ConfigFilterView
|
||||
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.constant.Constants
|
||||
import com.gh.gamecenter.common.baselist.ListAdapter
|
||||
import com.gh.gamecenter.common.baselist.ListFragment
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.observeNonNull
|
||||
import com.gh.gamecenter.common.utils.toColor
|
||||
import com.gh.gamecenter.common.utils.tryCatchInRelease
|
||||
import com.gh.gamecenter.common.utils.viewModelProvider
|
||||
import com.gh.gamecenter.databinding.FragmentTagsBinding
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class NewCategoryListFragment : ListFragment<GameEntity, NewCategoryListViewModel>() {
|
||||
|
||||
private var mPrimeCategory: CategoryEntity? = null
|
||||
private var mSubCategoryList = arrayListOf<CategoryEntity>()
|
||||
private val mBinding by lazy { FragmentTagsBinding.inflate(layoutInflater) }
|
||||
|
||||
private var mAdapter: NewCategoryListAdapter? = null
|
||||
private val mDataWatcher = 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)
|
||||
}
|
||||
}
|
||||
|
||||
private lateinit var mExposureListener: ExposureListener
|
||||
private lateinit var mViewModel: NewCategoryListViewModel
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mViewModel = provideListViewModel()
|
||||
|
||||
mViewModel.title = arguments?.getString(EntranceConsts.KEY_NAME) ?: ""
|
||||
mViewModel.categoryTitle = arguments?.getString(EntranceConsts.KEY_CATEGORY_TITLE) ?: ""
|
||||
mEntrance = arguments?.getString(EntranceConsts.KEY_ENTRANCE) ?: Constants.ENTRANCE_UNKNOWN
|
||||
mPrimeCategory = arguments?.getParcelable(EntranceConsts.KEY_DATA)
|
||||
mSubCategoryList = mPrimeCategory?.data as? ArrayList<CategoryEntity> ?: arrayListOf()
|
||||
|
||||
val initSelectedCategory = arguments?.getString(EntranceConsts.KEY_CATEGORY_INIT_TITLE)
|
||||
|
||||
mViewModel.selectedCategory =
|
||||
mSubCategoryList.find { categoryEntity -> categoryEntity.name == initSelectedCategory }
|
||||
?: CategoryEntity()
|
||||
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
setNavigationTitle(mViewModel.title)
|
||||
|
||||
mViewModel.refresh.observeNonNull(this) { onRefresh() }
|
||||
|
||||
mBinding.configContainer.visibility = View.VISIBLE
|
||||
mBinding.configContainer.updateAllTextView(mViewModel.sortType)
|
||||
mBinding.configContainer.setOnConfigSetupListener(object : ConfigFilterView.OnConfigFilterSetupListener {
|
||||
override fun onShowSortSize() {}
|
||||
|
||||
override fun onSetupSortSize(sortSize: SubjectSettingEntity.Size) {
|
||||
mViewModel.updateSortConfig(sortSize = sortSize)
|
||||
}
|
||||
|
||||
override fun onSetupSortType(sortType: ConfigFilterView.SortType) {
|
||||
mViewModel.updateSortConfig(sortType = sortType)
|
||||
}
|
||||
})
|
||||
|
||||
mExposureListener = ExposureListener(this, mAdapter!!)
|
||||
mListRv.addOnScrollListener(mExposureListener)
|
||||
|
||||
updateCategoriesView(mSubCategoryList)
|
||||
|
||||
mSkeletonScreen =
|
||||
Skeleton.bind(mBinding.listSkeleton).shimmer(false).load(R.layout.fragment_subject_skeleton).show()
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
super.onResume()
|
||||
DownloadManager.getInstance().addObserver(mDataWatcher)
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance().removeObserver(mDataWatcher)
|
||||
}
|
||||
|
||||
override fun getItemDecoration() = null
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() = mBinding.root
|
||||
|
||||
override fun provideListViewModel(): NewCategoryListViewModel {
|
||||
return viewModelProvider()
|
||||
}
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<GameEntity> {
|
||||
return mAdapter
|
||||
?: NewCategoryListAdapter(requireContext(), mViewModel, mEntrance).apply { mAdapter = this }
|
||||
}
|
||||
|
||||
private fun updateCategoriesView(categoryList: ArrayList<CategoryEntity>) {
|
||||
mBinding.tagsRecyclerView.layoutManager =
|
||||
LinearLayoutManager(requireContext(), LinearLayoutManager.HORIZONTAL, false)
|
||||
mBinding.tagsRecyclerView.adapter = NewCategoryHorizontalAdapter(requireContext(), mViewModel, categoryList) {
|
||||
mBinding.tagsRecyclerView.smoothScrollToPosition(it)
|
||||
}
|
||||
|
||||
// 调整选中标签位置
|
||||
for (index in 0 until categoryList.size) {
|
||||
if (categoryList[index].name == mViewModel.selectedCategory.name) {
|
||||
mBinding.tagsRecyclerView.postDelayed({
|
||||
tryCatchInRelease { mBinding.tagsRecyclerView.smoothScrollToPosition(index) }
|
||||
}, 200)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onRefresh() {
|
||||
mAdapter?.clearPositionAndPackageMap()
|
||||
|
||||
super.onRefresh()
|
||||
}
|
||||
|
||||
// 下载被删除事件
|
||||
@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.isInstalledOrUninstalled()) {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDarkModeChanged() {
|
||||
super.onDarkModeChanged()
|
||||
mBinding.run {
|
||||
divider.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
|
||||
tagsRecyclerView.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
tagsRecyclerView.adapter?.run {
|
||||
notifyItemRangeChanged(0, itemCount)
|
||||
}
|
||||
configContainer.run {
|
||||
container.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
|
||||
updateAllTextView(mViewModel.sortType)
|
||||
updatePopupWindow()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,81 @@
|
||||
package com.gh.gamecenter.category
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.exposure.ExposureUtils
|
||||
import com.gh.gamecenter.core.utils.UrlFilterUtils
|
||||
import com.gh.common.view.ConfigFilterView
|
||||
import com.gh.gamecenter.common.baselist.ListViewModel
|
||||
import com.gh.gamecenter.entity.CategoryEntity
|
||||
import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.SubjectSettingEntity
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
|
||||
class NewCategoryListViewModel(application: Application) : ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
var title = "" // 显示在 Toolbar 的标题
|
||||
var categoryTitle = "" // 跳转进来的分类的标题
|
||||
|
||||
val refresh = MutableLiveData<Boolean>()
|
||||
|
||||
var selectedCategory = CategoryEntity()
|
||||
|
||||
var sortType = ConfigFilterView.SortType.RECOMMENDED
|
||||
private var mSortSize = SubjectSettingEntity.Size()
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<List<GameEntity>>? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<List<GameEntity>> {
|
||||
return RetrofitManager.getInstance()
|
||||
.api
|
||||
.getGamesInCategory(selectedCategory.id, getSortType(), getSortSize(), page)
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
ExposureUtils.updateExposureInfo(it)
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun changeSelectedCategory(category: CategoryEntity) {
|
||||
if (selectedCategory != category) {
|
||||
selectedCategory = category
|
||||
|
||||
refresh.postValue(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateSortConfig(
|
||||
sortSize: SubjectSettingEntity.Size? = null,
|
||||
sortType: ConfigFilterView.SortType? = null
|
||||
) {
|
||||
if (sortSize != null && sortSize != mSortSize) {
|
||||
mSortSize = sortSize
|
||||
refresh.postValue(true)
|
||||
} else if (sortType != null && sortType != this.sortType) {
|
||||
this.sortType = sortType
|
||||
refresh.postValue(true)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSortSize(): String? {
|
||||
return UrlFilterUtils.getFilterQuery(
|
||||
"min_size", mSortSize.min.toString(),
|
||||
"max_size", mSortSize.max.toString()
|
||||
)
|
||||
}
|
||||
|
||||
fun getSortType(): String? {
|
||||
return if (sortType == ConfigFilterView.SortType.RECOMMENDED) {
|
||||
"download:-1"
|
||||
} else {
|
||||
"publish:-1"
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -161,7 +161,6 @@ class DownloadFragment : BaseFragment_TabLayout() {
|
||||
val showOnFailed = downloadManagerAd?.displayRule?.onFailedAction == "show"
|
||||
if ((showThirdPartyAd && thirdPartyAd != null) || (!showThirdPartyAd && thirdPartyAd != null && ownerAd == null)) {
|
||||
initThirdPartyAd(thirdPartyAd) { isSuccess ->
|
||||
if (!isAdded) return@initThirdPartyAd
|
||||
mBinding.maskView.goneIf(!isSuccess)
|
||||
if (!isSuccess && ownerAd != null && showOnFailed) {
|
||||
mSlideInterval = ownerAd.adSource?.sliderInterval ?: -1
|
||||
@ -194,7 +193,7 @@ class DownloadFragment : BaseFragment_TabLayout() {
|
||||
}
|
||||
|
||||
private fun initOwnerAd(adConfig: AdConfig) {
|
||||
if (!isAdded || adConfig.id.isEmpty()) return
|
||||
if (adConfig.id.isEmpty()) return
|
||||
mAdGameViewModel = viewModelProvider(AdGameViewModel.Factory(adConfig))
|
||||
initAdGameBanner(adConfig)
|
||||
mBinding.closeAdIv.setOnClickListener {
|
||||
@ -228,7 +227,6 @@ class DownloadFragment : BaseFragment_TabLayout() {
|
||||
adConfig.displayRule.onFailedAction == "show" && adConfig.thirdPartyAd != null) {
|
||||
// 自有广告游戏为空时,显示第三方广告
|
||||
initThirdPartyAd(adConfig.thirdPartyAd) { isSuccess ->
|
||||
if (!isAdded) return@initThirdPartyAd
|
||||
mBinding.maskView.goneIf(!isSuccess)
|
||||
if (isSuccess) {
|
||||
SPUtils.setLong(Constants.SP_LAST_DOWNLOAD_MANAGER_AD_SHOW_TIME, System.currentTimeMillis())
|
||||
|
||||
@ -6,6 +6,40 @@ import com.gh.gamecenter.feature.entity.GameEntity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class CatalogEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
var name: String = "",
|
||||
var switch: CatalogSwitch = CatalogSwitch(),
|
||||
@SerializedName("has_special")
|
||||
var hasSpecial: Boolean = false,
|
||||
@SerializedName("sub_catalogs")
|
||||
var subCatalog: List<SubCatalogEntity> = emptyList()
|
||||
) : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class SubCatalogEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
var name: String = "",
|
||||
var icon: String = "",
|
||||
var type: String = "",
|
||||
var link: LinkEntity = LinkEntity(),
|
||||
var recommended: Boolean = false
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
data class CatalogSwitch(
|
||||
@SerializedName("sort_hot")
|
||||
var hotSort: String = "",
|
||||
@SerializedName("sort_new")
|
||||
var newSort: String = "",
|
||||
@SerializedName("sort_star")
|
||||
var starSort: String = ""
|
||||
) : Parcelable
|
||||
}
|
||||
|
||||
|
||||
@Parcelize
|
||||
class SpecialCatalogEntity(
|
||||
|
||||
@ -73,6 +73,8 @@ data class SubjectRecommendEntity(
|
||||
"column" -> "专题"
|
||||
"column_collection" -> "专题合集"
|
||||
"block" -> "版块"
|
||||
"category" -> "分类"
|
||||
"catalog" -> "新分类"
|
||||
"category_v2" -> "新分类2.0"
|
||||
"column_test" -> "开测表"
|
||||
"server" -> "开服表"
|
||||
|
||||
@ -11,6 +11,8 @@ import androidx.fragment.app.Fragment
|
||||
import androidx.viewpager.widget.ViewPager
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.amway.AmwayFragment
|
||||
import com.gh.gamecenter.catalog.CatalogFragment
|
||||
import com.gh.gamecenter.category.CategoryDirectoryFragment
|
||||
import com.gh.gamecenter.category2.CategoryV2Fragment
|
||||
import com.gh.gamecenter.common.base.adapter.FragmentAdapter
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
@ -157,6 +159,16 @@ abstract class HomeTabWrapperFragment : SearchToolWrapperFragment() {
|
||||
putString(GameServersTestFragment.TEST_COLUMN_ID, tabEntity.link)
|
||||
})
|
||||
|
||||
"category" -> CategoryDirectoryFragment().with(Bundle().apply {
|
||||
putString(EntranceConsts.KEY_CATEGORY_ID, tabEntity.link)
|
||||
putString(EntranceConsts.KEY_CATEGORY_TITLE, tabEntity.text)
|
||||
})
|
||||
|
||||
"catalog" -> CatalogFragment().with(Bundle().apply {
|
||||
putString(EntranceConsts.KEY_CATALOG_ID, tabEntity.link)
|
||||
putString(EntranceConsts.KEY_CATALOG_TITLE, tabEntity.text)
|
||||
})
|
||||
|
||||
"category_v2" -> CategoryV2Fragment().with(Bundle().apply {
|
||||
putString(EntranceConsts.KEY_CATEGORY_ID, tabEntity.link)
|
||||
putString(EntranceConsts.KEY_CATEGORY_TITLE, tabEntity.text)
|
||||
|
||||
@ -26,6 +26,7 @@ import com.gh.gamecenter.adapter.viewholder.GameHeadViewHolder
|
||||
import com.gh.gamecenter.adapter.viewholder.GameImageViewHolder
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
|
||||
import com.gh.gamecenter.adapter.viewholder.GameViewPagerViewHolder
|
||||
import com.gh.gamecenter.category.CategoryDirectoryActivity
|
||||
import com.gh.gamecenter.common.baselist.LoadStatus
|
||||
import com.gh.gamecenter.common.callback.OnViewClickListener
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
@ -979,6 +980,14 @@ class GameFragmentAdapter(
|
||||
)
|
||||
)
|
||||
|
||||
"category" -> mContext.startActivity(
|
||||
CategoryDirectoryActivity.getIntent(
|
||||
mContext,
|
||||
entity.link!!,
|
||||
entity.text!!
|
||||
)
|
||||
)
|
||||
|
||||
"column" -> {
|
||||
SubjectActivity.startSubjectActivity(
|
||||
mContext,
|
||||
|
||||
@ -2,8 +2,8 @@ package com.gh.gamecenter.game.columncollection.detail
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.utils.updateStatusBarColor
|
||||
|
||||
@ -34,8 +34,8 @@ class ColumnCollectionDetailActivity : ToolBarActivity() {
|
||||
}
|
||||
|
||||
override fun getBusinessId(): Pair<String, String> {
|
||||
val fragment = targetFragment as? ColumnCollectionDetailFragment?
|
||||
return if (fragment?.arguments != null) {
|
||||
val fragment = targetFragment as ColumnCollectionDetailFragment
|
||||
return if (fragment.arguments != null) {
|
||||
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "", "")
|
||||
} else {
|
||||
super.getBusinessId()
|
||||
|
||||
@ -36,8 +36,8 @@ class CommonCollectionDetailActivity : ToolBarActivity() {
|
||||
}
|
||||
|
||||
override fun getBusinessId(): Pair<String, String> {
|
||||
val fragment = targetFragment as? CommonCollectionDetailFragment?
|
||||
return if (fragment?.arguments != null) {
|
||||
val fragment = targetFragment as CommonCollectionDetailFragment
|
||||
return if (fragment.arguments != null) {
|
||||
Pair(
|
||||
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
|
||||
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""
|
||||
|
||||
@ -36,8 +36,8 @@ class CustomCommonCollectionDetailActivity : ToolBarActivity() {
|
||||
}
|
||||
|
||||
override fun getBusinessId(): Pair<String, String> {
|
||||
val fragment = targetFragment as? CustomCommonCollectionDetailFragment?
|
||||
return if (fragment?.arguments != null) {
|
||||
val fragment = targetFragment as CustomCommonCollectionDetailFragment
|
||||
return if (fragment.arguments != null) {
|
||||
Pair(
|
||||
fragment.requireArguments().getString(EntranceConsts.KEY_COLLECTION_ID) ?: "",
|
||||
fragment.requireArguments().getString(EntranceConsts.KEY_BLOCK_ID) ?: ""
|
||||
|
||||
@ -182,9 +182,7 @@ class AddGamesDialogFragment : BaseDialogFragment() {
|
||||
return super.onBack()
|
||||
}
|
||||
|
||||
private fun showGuidePopupWindow(): PopupWindow? {
|
||||
if (!isAdded) return null
|
||||
|
||||
private fun showGuidePopupWindow(): PopupWindow {
|
||||
val guideBinding = LayoutGameCollectionAddGamesGuideBinding.inflate(layoutInflater)
|
||||
val popupWindow = BugFixedPopupWindow(
|
||||
guideBinding.root,
|
||||
|
||||
@ -7,8 +7,8 @@ import android.view.View
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.common.base.activity.ToolBarActivity
|
||||
import com.gh.gamecenter.common.constant.EntranceConsts
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
import com.gh.gamecenter.core.utils.DisplayUtils
|
||||
import com.gh.gamecenter.common.exposure.ExposureSource
|
||||
|
||||
/**
|
||||
* 游戏单详情
|
||||
@ -30,8 +30,8 @@ class GameCollectionDetailActivity : ToolBarActivity() {
|
||||
}
|
||||
|
||||
override fun getBusinessId(): Pair<String, String> {
|
||||
val fragment = targetFragment as? GameCollectionDetailFragment?
|
||||
return if (fragment?.arguments != null) {
|
||||
val fragment = targetFragment as GameCollectionDetailFragment
|
||||
return if (fragment.arguments != null) {
|
||||
Pair(fragment.requireArguments().getString(EntranceConsts.KEY_GAME_COLLECTION_ID) ?: "", "")
|
||||
} else {
|
||||
super.getBusinessId()
|
||||
|
||||
@ -149,6 +149,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
|
||||
private var mShowConcernOnMenu = false
|
||||
|
||||
private var mSpecialDownloadDetailFragmentIsShowing = false
|
||||
|
||||
private val mFragmentsList = ArrayList<Fragment>()
|
||||
private val mTabTitleList = ArrayList<String>()
|
||||
private val mTabTypeList = ArrayList<String>() // tab 类型的列表,用于确定某个类型的 tab 在第几个位置
|
||||
@ -165,7 +167,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
|| downloadEntity.status == DownloadStatus.redirected
|
||||
) {
|
||||
// 特殊下载弹窗
|
||||
if (isSpecialDownloadDialogAvailable(downloadEntity) && !isSpecialDownloadDetailFragmentIsShowing()) {
|
||||
if (isSpecialDownloadDialogAvailable() && !mSpecialDownloadDetailFragmentIsShowing) {
|
||||
updateSpecialDownloadDialogIcon(true)
|
||||
|
||||
if (downloadEntity.status == DownloadStatus.add) {
|
||||
@ -866,10 +868,8 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
|
||||
showConcernIconAtBottomBarIfAvailable()
|
||||
|
||||
val downloadEntitySnapshot = DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity)
|
||||
|
||||
if (isSpecialDownloadDialogAvailable(downloadEntitySnapshot)
|
||||
&& downloadEntitySnapshot != null) {
|
||||
if (isSpecialDownloadDialogAvailable()
|
||||
&& DownloadManager.getInstance().getDownloadEntitySnapshot(mGameEntity) != null) {
|
||||
updateSpecialDownloadDialogIcon(true)
|
||||
}
|
||||
|
||||
@ -2562,15 +2562,12 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
* 2. 未获取到游戏详情数据
|
||||
* 3. 当前游戏 APK 不为 1 个
|
||||
* 4. 当前游戏类型不为畅玩
|
||||
* 5. 当前游戏不是双下载时使用本地下载进行下载
|
||||
*/
|
||||
private fun isSpecialDownloadDialogAvailable(downloadEntity: DownloadEntity? = null): Boolean {
|
||||
private fun isSpecialDownloadDialogAvailable(): Boolean {
|
||||
if (Config.getNewApiSettingsEntity()?.install?.questionTip?.linkEntity == null) return false
|
||||
if (mNewGameDetailEntity == null || mGameEntity == null) return false
|
||||
if (mGameEntity?.getApk()?.size != 1) return false
|
||||
if (downloadEntity?.asVGame() == true) return false
|
||||
if (downloadEntity?.isSimulatorGame() == true) return false
|
||||
if (downloadEntity?.isLocalDownloadInDualDownloadMode() == true) return false
|
||||
if (GameUtils.shouldPerformAsVGame(mGameEntity!!)) return false
|
||||
|
||||
return true
|
||||
}
|
||||
@ -2588,6 +2585,7 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
if (fragment == null) {
|
||||
if (!visibilityViewModel.specialDownloadVisibleLiveData.hasObservers()) {
|
||||
visibilityViewModel.specialDownloadVisibleLiveData.observe(viewLifecycleOwner) {
|
||||
mSpecialDownloadDetailFragmentIsShowing = it
|
||||
updateSpecialDownloadDialogIcon(visible = !it)
|
||||
}
|
||||
}
|
||||
@ -2603,10 +2601,6 @@ class GameDetailFragment : BaseLazyFragment(), IScrollable {
|
||||
}
|
||||
}
|
||||
|
||||
private fun isSpecialDownloadDetailFragmentIsShowing(): Boolean {
|
||||
return childFragmentManager.findFragmentByTag(TAG_SPECIAL_DOWNLOAD_DIALOG) != null
|
||||
}
|
||||
|
||||
override fun scrollToTop() {
|
||||
val fragment = mFragmentsList.safelyGetInRelease(mBodyBinding.gamedetailVp.currentItem)
|
||||
if (fragment is IScrollable && fragment.isAdded) {
|
||||
|
||||
@ -394,7 +394,7 @@ class SpecialDownloadDialogFragment : BaseDraggableDialogFragment() {
|
||||
SensorsBridge.trackDownloadComponentsContentClick(
|
||||
gameId = gameEntity.id,
|
||||
gameName = gameEntity.name ?: "unknown",
|
||||
gameSchemaType = gameEntity.gameBitChinese,
|
||||
gameSchemeType = gameEntity.gameBitChinese,
|
||||
downloadStatus = gameEntity.downloadStatusChinese,
|
||||
gameType = gameEntity.categoryChinese,
|
||||
downloadType = if (asVGame) "畅玩下载" else "本地下载",
|
||||
@ -405,7 +405,7 @@ class SpecialDownloadDialogFragment : BaseDraggableDialogFragment() {
|
||||
SensorsBridge.trackDownloadComponentsShow(
|
||||
gameId = gameEntity.id,
|
||||
gameName = gameEntity.name ?: "unknown",
|
||||
gameSchemaType = gameEntity.gameBitChinese,
|
||||
gameSchemeType = gameEntity.gameBitChinese,
|
||||
downloadStatus = gameEntity.downloadStatusChinese,
|
||||
gameType = gameEntity.categoryChinese,
|
||||
downloadType = if (asVGame) "畅玩下载" else "本地下载",
|
||||
|
||||
@ -17,7 +17,6 @@ import com.gh.common.iinterface.ISuperiorChain
|
||||
import com.gh.common.prioritychain.CustomFloatingWindowHandler
|
||||
import com.gh.common.prioritychain.PriorityChain
|
||||
import com.gh.common.prioritychain.PullDownPushHandler
|
||||
import com.gh.common.prioritychain.VideoHandler
|
||||
import com.gh.common.util.DefaultSearchHintHelper
|
||||
import com.gh.common.util.DialogUtils
|
||||
import com.gh.common.util.DirectUtils
|
||||
@ -469,10 +468,10 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
|
||||
|
||||
binding = FragmentCustomBinding.bind(mCachedView)
|
||||
|
||||
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0, false)
|
||||
|
||||
buildPriorityChain()
|
||||
|
||||
scrollCalculatorHelper = ScrollCalculatorHelper(binding.gameList, R.id.autoVideoView, 0)
|
||||
|
||||
adapter = CustomPageAdapter(
|
||||
viewModel,
|
||||
viewLifecycleOwner,
|
||||
@ -513,11 +512,9 @@ class CustomPageFragment : LazyFragment(), ISmartRefreshContent, IScrollable {
|
||||
|
||||
private fun buildPriorityChain() {
|
||||
val floatingWindowHandler = CustomFloatingWindowHandler(23)
|
||||
val videoHandler = VideoHandler(24, scrollCalculatorHelper)
|
||||
|
||||
priorityChain.addHandler(pullDownPushHandler)
|
||||
priorityChain.addHandler(floatingWindowHandler)
|
||||
priorityChain.addHandler(videoHandler)
|
||||
(parentFragment as? BaseTabWrapperFragment)?.addTabGuideHandlerIfExists(priorityChain)
|
||||
|
||||
viewModel.floatingWindows.observe(viewLifecycleOwner, EventObserver {
|
||||
|
||||
@ -5,6 +5,7 @@ import android.os.Handler
|
||||
import android.os.Looper
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.forEach
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
@ -14,28 +15,13 @@ import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.video.detail.CustomManager
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class ScrollCalculatorHelper(
|
||||
val mListRv: RecyclerView,
|
||||
private val mPlayId: Int,
|
||||
private val mRangeTop: Int,
|
||||
private var isEnabled: Boolean = true
|
||||
) {
|
||||
|
||||
class ScrollCalculatorHelper(val mListRv: RecyclerView, private val mPlayId: Int, private val mRangeTop: Int) {
|
||||
private var mFirstVisible = -1
|
||||
private var mLastVisible = 0
|
||||
private var mRunnable: PlayRunnable? = null
|
||||
private val mPlayHandler = Handler(Looper.getMainLooper())
|
||||
var currentPlayer: AutomaticVideoView? = null
|
||||
|
||||
fun enableAndPlayIfValid() {
|
||||
isEnabled = true
|
||||
|
||||
if (mListRv.isAttachedToWindow
|
||||
&& mListRv.scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
playVideo(mListRv)
|
||||
}
|
||||
}
|
||||
|
||||
fun onScrollStateChanged(scrollState: Int) {
|
||||
if (scrollState == RecyclerView.SCROLL_STATE_IDLE) {
|
||||
playVideo(mListRv)
|
||||
@ -94,7 +80,7 @@ class ScrollCalculatorHelper(
|
||||
}
|
||||
|
||||
private fun playVideo(view: RecyclerView?) {
|
||||
if (view == null || !view.isAttachedToWindow || !isEnabled) return
|
||||
if (view == null) return
|
||||
val layoutManager = view.layoutManager
|
||||
var gsyBaseVideoPlayer: AutomaticVideoView
|
||||
for (i in mFirstVisible until mLastVisible + 1) {
|
||||
|
||||
@ -13,7 +13,7 @@ object HeadUpDisplayLogHelper {
|
||||
source = source,
|
||||
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
|
||||
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name
|
||||
@ -25,7 +25,7 @@ object HeadUpDisplayLogHelper {
|
||||
source = source,
|
||||
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
|
||||
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name
|
||||
@ -36,7 +36,7 @@ object HeadUpDisplayLogHelper {
|
||||
SensorsBridge.trackAutomaticInstallationPromptBarShow(
|
||||
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
|
||||
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name
|
||||
@ -47,7 +47,7 @@ object HeadUpDisplayLogHelper {
|
||||
SensorsBridge.trackAutomaticInstallationPromptBarClick(
|
||||
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载",
|
||||
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
|
||||
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameSchemeType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameType = downloadEntity.getMetaExtra(Constants.GAME_CATEGORY_IN_CHINESE),
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name
|
||||
|
||||
@ -130,24 +130,6 @@ object PackagesManager {
|
||||
return null
|
||||
}
|
||||
|
||||
/**
|
||||
* 根据游戏 ID 获取已安装的信息
|
||||
*
|
||||
* @param gameId 游戏 Id
|
||||
* @return 如果为空:未安装
|
||||
*/
|
||||
fun getInstalledDataByGameId(gameId: String?): GameInstall? {
|
||||
if (TextUtils.isEmpty(gameId)) {
|
||||
return null
|
||||
}
|
||||
for (gameInstall in mInstalledList) {
|
||||
if (gameInstall.id == gameId) {
|
||||
return gameInstall
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* 判断包名是否可以更新
|
||||
|
||||
@ -515,8 +515,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
TextHelper.highlightTextThatIsWrappedInsideWrapperByDefault(
|
||||
viewHolder.commentContentTv,
|
||||
commentEntity.getContent(),
|
||||
Constants.DEFAULT_TEXT_WRAPPER,
|
||||
new TextHelper.DirectToWebViewHighlightedTextClick(mContext, mEntrance)
|
||||
Constants.DEFAULT_TEXT_WRAPPER_2,
|
||||
new TextHelper.CopyToClipboardHighlightedTextClick()
|
||||
);
|
||||
ArticleCommentParent parent = commentEntity.getParent();
|
||||
if (parent != null && !TextUtils.isEmpty(parent.getUser().getName())) {
|
||||
@ -540,8 +540,8 @@ public class NewsDetailAdapter extends BaseRecyclerAdapter<ViewHolder> {
|
||||
TextHelper.highlightTextThatIsWrappedInsideWrapperByDefault(
|
||||
viewHolder.quoteContentTv,
|
||||
content,
|
||||
Constants.DEFAULT_TEXT_WRAPPER,
|
||||
new TextHelper.DirectToWebViewHighlightedTextClick(mContext, mEntrance)
|
||||
Constants.DEFAULT_TEXT_WRAPPER_2,
|
||||
new TextHelper.CopyToClipboardHighlightedTextClick()
|
||||
);
|
||||
} else {
|
||||
viewHolder.quoteContainer.setVisibility(View.GONE);
|
||||
|
||||
@ -11,9 +11,11 @@ import com.gh.gamecenter.room.AppDatabase
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class PackageFilterManager {
|
||||
object PackageFilterManager {
|
||||
|
||||
private var mPackageKey = "" // 用于下次获取包名-游戏信息的 key
|
||||
private val mPendingPackageNameSet by lazy { hashSetOf<String>() } // 因遇到异常而等待下次操作更新的包名列表
|
||||
private val mValidPackageNameSet by lazy { hashSetOf<String>() } // 已被收录的游戏包名列表
|
||||
|
||||
val packageKey: String
|
||||
get() = mPackageKey
|
||||
@ -32,7 +34,7 @@ class PackageFilterManager {
|
||||
) {
|
||||
if (appendOnly) {
|
||||
// 添加因为异常而没能正常更新的包名列表
|
||||
packageList.addAll(PackageRepository.mPendingPackageNameSet)
|
||||
packageList.addAll(mPendingPackageNameSet)
|
||||
}
|
||||
|
||||
RetrofitManager.getInstance()
|
||||
@ -42,12 +44,12 @@ class PackageFilterManager {
|
||||
.subscribe(object : BiResponse<PackageFilter>() {
|
||||
override fun onSuccess(data: PackageFilter) {
|
||||
mPackageKey = data.key
|
||||
PackageRepository.mPendingPackageNameSet.clear()
|
||||
mPendingPackageNameSet.clear()
|
||||
|
||||
val partialPackageList = arrayListOf<String>()
|
||||
|
||||
if (!appendOnly) {
|
||||
PackageRepository.mValidPackageNameSet.clear()
|
||||
mValidPackageNameSet.clear()
|
||||
tryWithDefaultCatch {
|
||||
AppDatabase.getInstance().packageFilterDao().deleteAllPackageName()
|
||||
}
|
||||
@ -61,13 +63,13 @@ class PackageFilterManager {
|
||||
}
|
||||
|
||||
partialPackageList.add(packageName)
|
||||
PackageRepository.mValidPackageNameSet.add(packageName)
|
||||
mValidPackageNameSet.add(packageName)
|
||||
}
|
||||
|
||||
if (appendOnly) {
|
||||
callbackClosure?.invoke(ArrayList(partialPackageList))
|
||||
} else {
|
||||
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
|
||||
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
|
||||
}
|
||||
}
|
||||
|
||||
@ -75,7 +77,7 @@ class PackageFilterManager {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (appendOnly) {
|
||||
PackageRepository.mPendingPackageNameSet.addAll(packageList)
|
||||
mPendingPackageNameSet.addAll(packageList)
|
||||
} else {
|
||||
if (exception is retrofit2.HttpException && exception.code() == 403) {
|
||||
// 403 代表 key 过期,需要重新获取
|
||||
@ -89,13 +91,28 @@ class PackageFilterManager {
|
||||
for (packageEntity in packageEntityList) {
|
||||
// 依然为已安装状态才加入到有效包名列表中
|
||||
if (PackageUtils.isInstalled(HaloApp.getInstance(), packageEntity.packageName)) {
|
||||
PackageRepository.mValidPackageNameSet.add(packageEntity.packageName)
|
||||
mValidPackageNameSet.add(packageEntity.packageName)
|
||||
}
|
||||
}
|
||||
|
||||
callbackClosure?.invoke(ArrayList(PackageRepository.mValidPackageNameSet))
|
||||
callbackClosure?.invoke(ArrayList(mValidPackageNameSet))
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 该包名是否有效 (即是否已被光环收录)
|
||||
*/
|
||||
fun isPackageValid(packageName: String): Boolean {
|
||||
return mValidPackageNameSet.contains(packageName)
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
|
||||
*/
|
||||
fun hasPendingPackage(): Boolean {
|
||||
return mPendingPackageNameSet.isEmpty()
|
||||
}
|
||||
|
||||
}
|
||||
@ -6,6 +6,7 @@ import android.text.TextUtils
|
||||
import androidx.lifecycle.LiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.filter.RegionSettingHelper
|
||||
import com.gh.gamecenter.feature.utils.ApkActiveUtils
|
||||
import com.gh.common.util.GameUtils
|
||||
import com.gh.common.util.PackageUtils
|
||||
import com.gh.gamecenter.R
|
||||
@ -15,29 +16,19 @@ import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.retrofit.ObservableUtil
|
||||
import com.gh.gamecenter.common.retrofit.Response
|
||||
import com.gh.gamecenter.common.utils.secondOrNull
|
||||
import com.gh.gamecenter.common.utils.toArrayList
|
||||
import com.gh.gamecenter.common.utils.tryCatchInRelease
|
||||
import com.gh.gamecenter.core.runOnIoThread
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
import com.gh.gamecenter.entity.AppEntity
|
||||
import com.gh.gamecenter.entity.GameUpdateEntity
|
||||
import com.gh.gamecenter.entity.PackageGame
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.eventbus.EBPackage
|
||||
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.login.user.UserManager
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository.gameInstalled
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository.gameUpdate
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.vspace.VHelper
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.SingleObserver
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.disposables.Disposable
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.json.JSONException
|
||||
@ -83,10 +74,6 @@ object PackageRepository {
|
||||
fun changeRecentVaPlayed() {
|
||||
_recentVaPlayedChanged.postValue(Unit)
|
||||
}
|
||||
val packageFilterManager = PackageFilterManager()
|
||||
var mPendingPackageNameSet = hashSetOf<String>() // 因遇到异常而等待下次操作更新的包名列表
|
||||
var mValidPackageNameSet = hashSetOf<String>() // 已被收录的游戏包名列表
|
||||
|
||||
|
||||
/**
|
||||
* 预留方法,如果想手动初始化可以调用
|
||||
@ -102,51 +89,18 @@ object PackageRepository {
|
||||
if (gameUpdate.isNotEmpty()) gameUpdate.clear()
|
||||
if (mInstalledPkgSet.isNotEmpty()) mInstalledPkgSet.clear()
|
||||
|
||||
Single.zip<Result<Any?>, Result<Any?>, Result<Any?>>(
|
||||
Single.create { emitter ->
|
||||
val list = PackageUtils.getAllPackageName(mApplication)
|
||||
uploadAppList()
|
||||
initFilterPackage(list) { filteredList ->
|
||||
emitter.onSuccess(Result.success(null))
|
||||
val list = PackageUtils.getAllPackageName(mApplication)
|
||||
|
||||
mInstalledPkgSet.addAll(filteredList)
|
||||
notifyInstallPkgData()
|
||||
loadInstalledGameDigestAndNotifyData(packageFilterManager.packageKey, filteredList)
|
||||
}
|
||||
uploadAppList()
|
||||
|
||||
initFilterPackage(list) { filteredList ->
|
||||
mIsInitialisingData = false
|
||||
|
||||
},
|
||||
Single.create { emitter ->
|
||||
// 畅玩游戏更新
|
||||
var allGames = VHelper.getAllVGameSnapshots()
|
||||
if (allGames.isEmpty()) {
|
||||
VHelper.refreshVGameSnapshot()
|
||||
allGames = VHelper.getAllVGameSnapshots()
|
||||
}
|
||||
val allGamePkgNames = allGames.map { it.packageName }.toArrayList()
|
||||
if (allGamePkgNames.isNotEmpty()) {
|
||||
val packageFilterManager = PackageFilterManager()
|
||||
updateFilterPackage(packageFilterManager, allGamePkgNames) {
|
||||
emitter.onSuccess(Result.success(null))
|
||||
loadInstalledGameDigestAndNotifyData(
|
||||
packageKey = packageFilterManager.packageKey,
|
||||
filteredList = allGamePkgNames,
|
||||
isVGame = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
) { t1, t2 -> Result.success(null) }.subscribe(object : SingleObserver<Result<Any?>> {
|
||||
override fun onSubscribe(d: Disposable) {
|
||||
}
|
||||
mInstalledPkgSet.addAll(filteredList)
|
||||
notifyInstallPkgData()
|
||||
|
||||
override fun onSuccess(t: Result<Any?>) {
|
||||
mIsInitialisingData = false
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable) {
|
||||
}
|
||||
})
|
||||
loadInstalledGameDigestAndNotifyData(filteredList)
|
||||
}
|
||||
|
||||
loadGhzsUpdate()
|
||||
}
|
||||
@ -159,15 +113,18 @@ object PackageRepository {
|
||||
list: MutableList<String>,
|
||||
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
|
||||
) {
|
||||
packageFilterManager.updateFilterPackages(list, false, callbackClosure)
|
||||
PackageFilterManager.updateFilterPackages(list, false) {
|
||||
callbackClosure?.invoke(it)
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFilterPackage(
|
||||
packageFilterManager: PackageFilterManager,
|
||||
list: MutableList<String>,
|
||||
callbackClosure: ((list: ArrayList<String>) -> Unit)? = null
|
||||
) {
|
||||
packageFilterManager.updateFilterPackages(list, true, callbackClosure)
|
||||
PackageFilterManager.updateFilterPackages(list, true) {
|
||||
callbackClosure?.invoke(it)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -178,8 +135,7 @@ object PackageRepository {
|
||||
PackageUtils.getGhVersionName(),
|
||||
PackageUtils.getGhVersionCode(),
|
||||
HaloApp.getInstance().channel,
|
||||
Build.VERSION.SDK_INT
|
||||
)
|
||||
Build.VERSION.SDK_INT)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<AppEntity>() {
|
||||
@ -252,7 +208,6 @@ object PackageRepository {
|
||||
*/
|
||||
@SuppressLint("CheckResult")
|
||||
private fun loadInstalledGameDigestAndNotifyData(
|
||||
packageKey: String,
|
||||
filteredList: ArrayList<String>,
|
||||
isVGame: Boolean = false,
|
||||
updateInstallStatus: Boolean = false
|
||||
@ -267,7 +222,7 @@ object PackageRepository {
|
||||
}, Any())
|
||||
|
||||
while (++page <= maxPageCount) {
|
||||
var observable = mNewApi.getPackageGames(packageKey, page, PAGE_SIZE)
|
||||
var observable = mNewApi.getPackageGames(PackageFilterManager.packageKey, page, PAGE_SIZE)
|
||||
.subscribeOn(Schedulers.io())
|
||||
|
||||
observable.subscribe(object : BiResponse<List<PackageGame>>() {
|
||||
@ -283,8 +238,7 @@ object PackageRepository {
|
||||
}
|
||||
|
||||
for (game in validGames) {
|
||||
val shouldNotifyChanges =
|
||||
validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
|
||||
val shouldNotifyChanges = validateGameAndPostChanges(gh_id, game, pkgName, isVGame, updateInstallStatus)
|
||||
if (!isNotifyUpdate && shouldNotifyChanges) {
|
||||
isNotifyUpdate = true
|
||||
}
|
||||
@ -305,13 +259,11 @@ object PackageRepository {
|
||||
/**
|
||||
* 验证游戏并且更新数据
|
||||
*/
|
||||
private fun validateGameAndPostChanges(
|
||||
ghId: Any?,
|
||||
game: GameEntity,
|
||||
pkgName: String,
|
||||
isVGame: Boolean,
|
||||
updateInstallStatus: Boolean
|
||||
): Boolean {
|
||||
private fun validateGameAndPostChanges(ghId: Any?,
|
||||
game: GameEntity,
|
||||
pkgName: String,
|
||||
isVGame: Boolean,
|
||||
updateInstallStatus: Boolean): Boolean {
|
||||
if (ghId == null || ghId == game.id) {
|
||||
gameInstalled.add(GameInstall.transformGameInstall(game, pkgName, isVGame))
|
||||
mInstalledGameList.add(game)
|
||||
@ -320,8 +272,7 @@ object PackageRepository {
|
||||
addCurrentlyInstalledVersionIfValid(game)
|
||||
|
||||
if (updateInstallStatus) {
|
||||
EventBus.getDefault()
|
||||
.post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
|
||||
EventBus.getDefault().post(EBPackage(EBPackage.TYPE_INSTALLED, pkgName, game.getApk().firstOrNull()?.version))
|
||||
}
|
||||
|
||||
if (isCanUpdate || isCanPluggable) {
|
||||
@ -405,8 +356,7 @@ object PackageRepository {
|
||||
) {
|
||||
// 使用了镜像的游戏;插件化关闭的游戏;无需插件化
|
||||
if (game.shouldUseMirrorInfo()
|
||||
|| apk.plugin == "close"
|
||||
) {
|
||||
|| apk.plugin == "close") {
|
||||
return false
|
||||
}
|
||||
|
||||
@ -466,11 +416,8 @@ object PackageRepository {
|
||||
* @param pkgName 已安装的游戏包名
|
||||
* @param cachedGameEntity 缓存的游戏实体,若存在免去再请求接口的过程
|
||||
*/
|
||||
fun addInstalledGame(
|
||||
packageFilterManager: PackageFilterManager,
|
||||
pkgName: String,
|
||||
cachedGameEntity: GameEntity? = null
|
||||
) {
|
||||
fun addInstalledGame(pkgName: String,
|
||||
cachedGameEntity: GameEntity? = null) {
|
||||
mInstalledPkgSet.add(pkgName)
|
||||
notifyInstallPkgData()
|
||||
|
||||
@ -481,8 +428,7 @@ object PackageRepository {
|
||||
game = cachedGameEntity,
|
||||
pkgName = pkgName,
|
||||
isVGame = false,
|
||||
updateInstallStatus = false
|
||||
)
|
||||
updateInstallStatus = false)
|
||||
|
||||
if (containsUpdate) {
|
||||
notifyGameUpdateData()
|
||||
@ -490,9 +436,8 @@ object PackageRepository {
|
||||
} else {
|
||||
val list = arrayListOf(pkgName)
|
||||
|
||||
updateFilterPackage(packageFilterManager, list) {
|
||||
updateFilterPackage(list) {
|
||||
loadInstalledGameDigestAndNotifyData(
|
||||
packageKey = packageFilterManager.packageKey,
|
||||
filteredList = list,
|
||||
updateInstallStatus = false
|
||||
)
|
||||
@ -508,11 +453,9 @@ object PackageRepository {
|
||||
* @param isVGame 是否为畅玩游戏
|
||||
* @param updateInstallStatus 是否更新安装状态
|
||||
*/
|
||||
fun addInstalledGames(
|
||||
packageFilterManager: PackageFilterManager,
|
||||
pkgNameList: ArrayList<String>,
|
||||
isVGame: Boolean = false,
|
||||
updateInstallStatus: Boolean = false,
|
||||
fun addInstalledGames(pkgNameList: ArrayList<String>,
|
||||
isVGame: Boolean = false,
|
||||
updateInstallStatus: Boolean = false,
|
||||
) {
|
||||
// 畅玩游戏不添加至本地的已安装包名列表中
|
||||
if (!isVGame) {
|
||||
@ -522,9 +465,8 @@ object PackageRepository {
|
||||
}
|
||||
notifyInstallPkgData()
|
||||
|
||||
updateFilterPackage(packageFilterManager, pkgNameList) {
|
||||
updateFilterPackage(pkgNameList) {
|
||||
loadInstalledGameDigestAndNotifyData(
|
||||
packageKey = packageFilterManager.packageKey,
|
||||
filteredList = pkgNameList,
|
||||
isVGame = isVGame,
|
||||
updateInstallStatus = updateInstallStatus
|
||||
@ -584,13 +526,6 @@ object PackageRepository {
|
||||
changeRecentVaPlayed()
|
||||
}
|
||||
|
||||
/**
|
||||
* 是否存在因为接口异常而导致没有查询收录情况的包名列表
|
||||
*/
|
||||
fun hasPendingPackage(): Boolean {
|
||||
return mPendingPackageNameSet.isEmpty()
|
||||
}
|
||||
|
||||
private fun notifyGameInstallData() {
|
||||
PackagesManager.initGameInstall(ArrayList(gameInstalled))
|
||||
gameInstalledLiveData.postValue(ArrayList(gameInstalled))
|
||||
|
||||
@ -63,13 +63,7 @@ class PackageViewModel(
|
||||
* @param cachedGameEntity 缓存的游戏实体
|
||||
*/
|
||||
fun addInstalledGame(pkgName: String?, cachedGameEntity: GameEntity? = null) {
|
||||
if (!TextUtils.isEmpty(pkgName)) {
|
||||
mRepository.addInstalledGame(
|
||||
mRepository.packageFilterManager,
|
||||
pkgName!!,
|
||||
cachedGameEntity
|
||||
)
|
||||
}
|
||||
if (!TextUtils.isEmpty(pkgName)) mRepository.addInstalledGame(pkgName!!, cachedGameEntity)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +81,9 @@ class PackageViewModel(
|
||||
// 未同意获取已安装应用权限时不进行数据初始化
|
||||
if (!PackageHelper.isGetInstalledPackagesAgreed()) return
|
||||
|
||||
if (mRepository.gameInstalled.size == 0 || mRepository.hasPendingPackage()) {
|
||||
if (mRepository.gameInstalled.size == 0
|
||||
|| PackageFilterManager.hasPendingPackage()
|
||||
) {
|
||||
PackageHelper.initPackageRelatedData();
|
||||
}
|
||||
}
|
||||
|
||||
@ -20,6 +20,7 @@ import com.gh.gamecenter.entity.BadgeEntity;
|
||||
import com.gh.gamecenter.entity.BlockEntity;
|
||||
import com.gh.gamecenter.entity.BottomTab;
|
||||
import com.gh.gamecenter.entity.CarouselEntity;
|
||||
import com.gh.gamecenter.entity.CatalogEntity;
|
||||
import com.gh.gamecenter.entity.CategoryEntity;
|
||||
import com.gh.gamecenter.entity.CommonCollectionContentEntity;
|
||||
import com.gh.gamecenter.entity.CommonCollectionEntity;
|
||||
@ -912,6 +913,19 @@ public interface ApiService {
|
||||
@GET("blocks/{block_id}/columns")
|
||||
Observable<List<SubjectEntity>> getBlockColumns(@Path("block_id") String blockId, @Query("page") int page);
|
||||
|
||||
/**
|
||||
* 获取分类目录
|
||||
*/
|
||||
@Headers({"Content-Type: application/json", "Accept: application/json"})
|
||||
@GET("categories/{category_id}/directories")
|
||||
Observable<List<CategoryEntity>> getCategories(@Path("category_id") String categoryId, @Query("page") int page);
|
||||
|
||||
/**
|
||||
* 获取分类游戏
|
||||
*/
|
||||
@GET("categories/{category_id}/games")
|
||||
Single<List<GameEntity>> getGamesInCategory(@Path("category_id") String categoryId, @Query("sort") String sort, @Query("filter") String filter, @Query("page") int page);
|
||||
|
||||
/**
|
||||
* 获取关注的用户列表
|
||||
*/
|
||||
@ -2271,6 +2285,18 @@ public interface ApiService {
|
||||
@Query("page") int page,
|
||||
@Query("page_size") int pageSize);
|
||||
|
||||
/**
|
||||
* 获取一级分类数据
|
||||
*/
|
||||
@GET("catalogs/{catalog_id}")
|
||||
Single<CatalogEntity> getCatalogs(@Path("catalog_id") String catalogId);
|
||||
|
||||
/**
|
||||
* 获取一级分类数据 及其 二级分类数据
|
||||
*/
|
||||
@GET("catalogs/{catalog_id}/{primary_catalog_id}")
|
||||
Single<CatalogEntity> getSubCatalogs(@Path("catalog_id") String catalogId, @Path("primary_catalog_id") String primaryCatalogId);
|
||||
|
||||
/**
|
||||
* 获取精选分类
|
||||
*/
|
||||
|
||||
@ -80,12 +80,10 @@ class SearchGameResultViewModel(
|
||||
.subscribe({ mutableList ->
|
||||
mSearchSubjects.addAll(mutableList)
|
||||
mSearchSubjects.forEach {
|
||||
val item = SearchItemData(subject = it)
|
||||
if (it.location <= 0 || it.location > itemDataList.size) {
|
||||
itemDataList.add(item)
|
||||
} else {
|
||||
itemDataList.add(it.location - 1, item)
|
||||
}
|
||||
itemDataList.add(
|
||||
if (it.location <= 0 || it.location >= itemDataList.size) itemDataList.size else it.location - 1,
|
||||
SearchItemData(subject = it)
|
||||
)
|
||||
}
|
||||
// 处理初始化列表且游戏列表size为0的情况
|
||||
handleLoadStatusWhenGameListIsEmpty(list, itemDataList)
|
||||
|
||||
@ -16,12 +16,11 @@ import com.gh.gamecenter.common.utils.ImageUtils
|
||||
import com.gh.gamecenter.common.utils.dip2px
|
||||
import com.gh.gamecenter.databinding.PieceBottomTabBinding
|
||||
import com.gh.gamecenter.entity.BottomTab
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||||
|
||||
/**
|
||||
* 底部Tab 基类
|
||||
*/
|
||||
abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
|
||||
abstract class BaseBottomTabFragment<T: ViewBinding>: ToolbarFragment() {
|
||||
|
||||
protected var mViewPager: ViewPager2? = null
|
||||
protected var mBottomTabContainer: LinearLayout? = null
|
||||
@ -45,7 +44,6 @@ abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
|
||||
protected fun initBottomTab(bottomTabList: List<BottomTab>) {
|
||||
mBottomTabBindingList.clear()
|
||||
mBottomTabContainer?.run {
|
||||
visibility = View.VISIBLE
|
||||
removeAllViews()
|
||||
bottomTabList.forEachIndexed { index, bottomTab ->
|
||||
addView(
|
||||
@ -63,13 +61,6 @@ abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
|
||||
.apply {
|
||||
tabTv.text = bottomTab.name
|
||||
if (!TextUtils.isEmpty(bottomTab.jsCode)) {
|
||||
tabLottie.setFailureListener {
|
||||
SentryHelper.onEvent(
|
||||
SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED,
|
||||
BOTTOM_TAB_ID, bottomTab.id,
|
||||
BOTTOM_TAB_NAME, bottomTab.name
|
||||
)
|
||||
}
|
||||
tabLottie.setAnimationFromJson(bottomTab.jsCode, bottomTab.id + bottomTab.name)
|
||||
}
|
||||
if (bottomTab.iconSelector != 0) {
|
||||
@ -131,13 +122,6 @@ abstract class BaseBottomTabFragment<T : ViewBinding> : ToolbarFragment() {
|
||||
protected open fun onPageSelected(position: Int) {
|
||||
checkIndex(position)
|
||||
}
|
||||
|
||||
protected open fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
|
||||
protected open fun onPageScrollStateChanged(state: Int) {}
|
||||
|
||||
companion object {
|
||||
private const val SENTRY_ID_BOTTOM_TAB_LOTTIE_LOAD_FAILED = "bottom_tab_lottie_load_failed"
|
||||
private const val BOTTOM_TAB_ID = "bottom_tab_id"
|
||||
private const val BOTTOM_TAB_NAME = "bottom_tab_name"
|
||||
}
|
||||
}
|
||||
@ -13,8 +13,6 @@ import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.core.text.color
|
||||
import androidx.core.view.doOnNextLayout
|
||||
import androidx.viewpager2.adapter.FragmentStateAdapter
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.ethanhua.skeleton.SkeletonScreen
|
||||
import com.gh.common.iinterface.ISuperiorChain
|
||||
import com.gh.common.prioritychain.BottomTabGuideHandler
|
||||
import com.gh.common.prioritychain.GlobalPriorityChainHelper
|
||||
@ -50,8 +48,6 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
|
||||
private var mBottomTabGuideHandler: BottomTabGuideHandler? = null
|
||||
private var mBottomTabGuidePopupWindow: PopupWindow? = null
|
||||
|
||||
private var mSkeletonScreen: SkeletonScreen? = null
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
override fun getInflatedLayout(): View = mBinding.root
|
||||
override fun provideAdapter(): FragmentStateAdapter = mAdapter
|
||||
@ -67,13 +63,10 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
|
||||
super.onCreate(savedInstanceState)
|
||||
DisplayUtils.transparentStatusBar(requireActivity())
|
||||
|
||||
initSkeleton()
|
||||
|
||||
mBinding.viewShadow.visibility = if (mIsDarkModeOn) View.GONE else View.VISIBLE
|
||||
buildPriorityChain()
|
||||
|
||||
mViewModel?.bottomTabListLiveData?.observe(this) {
|
||||
mSkeletonScreen?.hide()
|
||||
mBottomTabList.clear()
|
||||
mBottomTabList.addAll(it)
|
||||
mViewPager?.offscreenPageLimit = it.size
|
||||
@ -91,18 +84,6 @@ class MainWrapperFragment : BaseBottomTabFragment<PieceBottomTabBinding>(), OnBa
|
||||
mPriorityChain.addHandler(mBottomTabGuideHandler!!)
|
||||
}
|
||||
|
||||
private fun initSkeleton() {
|
||||
mSkeletonScreen = Skeleton.bind(mBinding.skeleton)
|
||||
.shimmer(true)
|
||||
.angle(Constants.SHIMMER_ANGLE)
|
||||
.color(R.color.ui_skeleton_highlight)
|
||||
.duration(Constants.SHIMMER_DURATION)
|
||||
.maskWidth(Constants.MASK_WIDTH)
|
||||
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
|
||||
.load(R.layout.fragment_main_skeleton)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showBottomTabGuideIfExists() {
|
||||
val guidePosition = mBottomTabList.indexOfFirst { it.guide != null }
|
||||
if (guidePosition != -1) {
|
||||
|
||||
@ -3,10 +3,10 @@ package com.gh.gamecenter.wrapper
|
||||
import android.annotation.SuppressLint
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.util.HomeBottomBarHelper
|
||||
import com.gh.common.util.HomeBottomBarHelper.isDefaultHomeBottomTabDataExist
|
||||
import com.gh.common.util.ViewPagerFragmentHelper
|
||||
import com.gh.gamecenter.common.entity.LaunchRedirect
|
||||
import com.gh.gamecenter.common.retrofit.BiResponse
|
||||
import com.gh.gamecenter.common.utils.singleToMain
|
||||
import com.gh.gamecenter.core.utils.GsonUtils
|
||||
import com.gh.gamecenter.core.utils.SingletonHolder
|
||||
import com.gh.gamecenter.entity.BottomTab
|
||||
@ -17,7 +17,6 @@ import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.SupervisorJob
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.SharedFlow
|
||||
import kotlinx.coroutines.launch
|
||||
@ -135,13 +134,7 @@ class MainWrapperRepository {
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getDataUnion() {
|
||||
// 若历史 tab 数据存在,优先使用作为占位
|
||||
if (isDefaultHomeBottomTabDataExist()) {
|
||||
processBottomTabData(emptyList())
|
||||
} else {
|
||||
// 若 timeout 后数据未加载完成,则即便还没回调 onFailure 也生成两个底部 tab
|
||||
emitDefaultTabDataAfterTimeout()
|
||||
}
|
||||
processBottomTabData(emptyList())
|
||||
mNewApi.dataUnion
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<DataUnionEntity>() {
|
||||
@ -239,15 +232,6 @@ class MainWrapperRepository {
|
||||
multiTabNavLiveData.postValue(multiTabNav)
|
||||
}
|
||||
|
||||
private fun emitDefaultTabDataAfterTimeout() {
|
||||
CoroutineScope(SupervisorJob()).launch {
|
||||
delay(3000)
|
||||
if (!mMainDataIsLoaded) {
|
||||
processBottomTabData(emptyList())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object : SingletonHolder<MainWrapperRepository>({ MainWrapperRepository() })
|
||||
}
|
||||
|
||||
|
||||
@ -58,7 +58,6 @@ import com.gh.gamecenter.eventbus.EBPackage
|
||||
import com.gh.gamecenter.feature.exposure.ExposureEvent
|
||||
import com.gh.gamecenter.feature.exposure.time.TimeUtil
|
||||
import com.gh.gamecenter.feature.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.feature.utils.SentryHelper
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
|
||||
import com.gh.gamecenter.home.custom.CustomPageFragment
|
||||
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
|
||||
@ -90,18 +89,7 @@ import kotlin.math.roundToInt
|
||||
* @see Style
|
||||
*/
|
||||
class SearchToolbarTabWrapperFragment : BaseTabWrapperFragment(), ISearchToolbarTab, ISmartRefresh, ISuperiorChain {
|
||||
private val mBinding by lazy {
|
||||
try {
|
||||
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
|
||||
} catch (e: Exception) {
|
||||
SentryHelper.onEvent("VIEW_BINDING_BIND_ERROR",
|
||||
"digest", e.localizedMessage,
|
||||
"gid", HaloApp.getInstance().gid
|
||||
)
|
||||
// 玄学,重试一次,该闪退闪退
|
||||
FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater)
|
||||
}
|
||||
}
|
||||
private val mBinding by lazy { FragmentSearchToolbarTabWrapperBinding.inflate(layoutInflater) }
|
||||
private val mViewModel: SearchToolbarTabWrapperViewModel by lazy {
|
||||
viewModelProviderFromParent(
|
||||
SearchToolbarTabWrapperViewModel.Factory(mMultiTabNavId, mNoTabLinkEntity?.link ?: ""),
|
||||
|
||||
@ -14,11 +14,9 @@ import android.text.TextUtils;
|
||||
import androidx.annotation.Nullable;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
|
||||
import com.blankj.utilcode.util.ThreadUtils;
|
||||
import com.gh.download.DownloadManager;
|
||||
import com.gh.gamecenter.SplashScreenActivity;
|
||||
import com.gh.gamecenter.common.base.GlobalActivityManager;
|
||||
import com.gh.gamecenter.core.AppExecutor;
|
||||
import com.gh.ndownload.suspendwindow.NDownloadDrawOverlayPermissionWindowController;
|
||||
import com.gh.ndownload.suspendwindow.NDownloadSuspendWindowController;
|
||||
import com.gh.ndownload.suspendwindow.utils.NDownloadSuspendWindowHelper;
|
||||
@ -284,14 +282,6 @@ public class NDownloadService extends Service {
|
||||
}
|
||||
|
||||
private void showDownloadSuspendWindowIfNeeded(DownloadEntity entry) {
|
||||
if (ThreadUtils.isMainThread()) {
|
||||
showDownloadSuspendWindowIfNeededInner(entry);
|
||||
} else {
|
||||
AppExecutor.getUiExecutor().execute(() -> showDownloadSuspendWindowIfNeededInner(entry));
|
||||
}
|
||||
}
|
||||
|
||||
private void showDownloadSuspendWindowIfNeededInner(DownloadEntity entry) {
|
||||
if (!NDownloadSuspendWindowHelper.shouldSkipDownloadEntity(getApplicationContext(), entry)) {
|
||||
if (NDownloadSuspendWindowHelper.canDrawOverLayer(getApplicationContext())) {// 已开启悬浮窗权限,直接显示悬浮窗
|
||||
showDownloadSuspendWindow();
|
||||
|
||||
@ -4,7 +4,6 @@ import android.app.Activity
|
||||
import android.app.Application
|
||||
import android.app.Application.ActivityLifecycleCallbacks
|
||||
import android.os.Bundle
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.SensorsBridge
|
||||
import com.gh.gamecenter.common.utils.asVGame
|
||||
@ -46,9 +45,7 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
|
||||
}
|
||||
|
||||
fun show(activity: Activity) {
|
||||
val decorView = activity.window.decorView
|
||||
val isAttachedToWindow = ViewCompat.isAttachedToWindow(decorView)
|
||||
if (isStarted || !isAttachedToWindow) return
|
||||
if (isStarted) return
|
||||
isStarted = true
|
||||
currentActivityRef = WeakReference(activity)
|
||||
onAttachToUi(activity)
|
||||
@ -127,10 +124,10 @@ class NDownloadDrawOverlayPermissionWindowController(val application: Applicatio
|
||||
SensorsBridge.trackDownloadSuspendedWindowGuideShow(
|
||||
gameId = downloadEntity.gameId,
|
||||
gameName = downloadEntity.name,
|
||||
gameSchemaType = if (downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameSchemeType = if(downloadEntity.getMetaExtra(Constants.KEY_BIT) == "32") "32位" else "64位",
|
||||
gameType = downloadEntity.categoryChinese,
|
||||
downloadStatus = downloadEntity.getMetaExtra(Constants.DOWNLOAD_STATUS_IN_CHINESE),
|
||||
downloadType = if (downloadEntity.asVGame()) "畅玩下载" else "本地下载"
|
||||
downloadType = if(downloadEntity.asVGame()) "畅玩下载" else "本地下载"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,7 +33,7 @@ import org.greenrobot.eventbus.ThreadMode
|
||||
* 下载悬浮窗-控制器
|
||||
*/
|
||||
class NDownloadSuspendWindowController(private val application: Application) :
|
||||
NDownloadSuspendIconLayout.OnDragCallback, Application.ActivityLifecycleCallbacks,
|
||||
NDownloadSuspendIconLayout.OnDragListener, Application.ActivityLifecycleCallbacks,
|
||||
PackageInstaller.OnInstallListener, NDownloadSuspendIconView.OnIconClickListener {
|
||||
|
||||
private var isStarted = false
|
||||
@ -75,24 +75,22 @@ class NDownloadSuspendWindowController(private val application: Application) :
|
||||
|
||||
downloadCount = 0
|
||||
currentDownloadEntity = null
|
||||
onDetachFromUi()
|
||||
NDataChanger.deleteObserver(downloadObserver)
|
||||
PackageInstaller.unregisterOnInstallListener(this)
|
||||
EventBus.getDefault().unregister(this)
|
||||
application.unregisterActivityLifecycleCallbacks(this)
|
||||
onDetachFromUi()
|
||||
isStarted = false
|
||||
}
|
||||
|
||||
private fun onAttachToUi() {
|
||||
closeWindow.attach()
|
||||
iconWindow.attach()
|
||||
iconWindow.setOnDragCallback(this)
|
||||
iconWindow.setOnDragListener(this)
|
||||
iconWindow.setOnIconClickListener(this)
|
||||
}
|
||||
|
||||
private fun onDetachFromUi() {
|
||||
iconWindow.setOnDragCallback(null)
|
||||
iconWindow.setOnIconClickListener(null)
|
||||
closeWindow.detach()
|
||||
iconWindow.detach()
|
||||
}
|
||||
|
||||
@ -12,7 +12,6 @@ import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.common.constant.Constants
|
||||
import com.gh.gamecenter.common.utils.asVGame
|
||||
import com.gh.gamecenter.common.utils.getExtension
|
||||
import com.gh.gamecenter.common.utils.isLocalDownloadInDualDownloadMode
|
||||
import com.gh.gamecenter.common.utils.isSimulatorDownload
|
||||
import com.gh.gamecenter.common.utils.isSimulatorGame
|
||||
import com.gh.gamecenter.core.utils.SPUtils
|
||||
@ -185,7 +184,6 @@ object NDownloadSuspendWindowHelper {
|
||||
|| downloadEntity.isPluggable
|
||||
|| downloadEntity.isSimulatorDownload()
|
||||
|| downloadEntity.isSimulatorGame()
|
||||
|| downloadEntity.isLocalDownloadInDualDownloadMode()
|
||||
|
||||
@JvmStatic
|
||||
fun isDrawOverlayPermissionWindowShown(): Boolean =
|
||||
|
||||
@ -26,7 +26,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
|
||||
ViewConfiguration.get(context).scaledTouchSlop
|
||||
}
|
||||
|
||||
private var onDragCallback: OnDragCallback? = null
|
||||
private var onDragListener: OnDragListener? = null
|
||||
|
||||
val icon = NDownloadSuspendIconView(context).also {
|
||||
it.clipChildren = false
|
||||
@ -39,12 +39,12 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
|
||||
addView(it, LayoutParams(size, size))
|
||||
}
|
||||
|
||||
fun setOnDragCallback(onDragCallback: OnDragCallback?) {
|
||||
this.onDragCallback = onDragCallback
|
||||
fun setOnDragListener(onDragListener: OnDragListener) {
|
||||
this.onDragListener = onDragListener
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
val onDragCallback = this.onDragCallback
|
||||
val onDragListener = this.onDragListener
|
||||
?: return super.onInterceptTouchEvent(ev)
|
||||
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
@ -66,7 +66,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
|
||||
val dragging = xDiff > touchSlop || yDiff > touchSlop
|
||||
if (dragging) {
|
||||
isDragging = true
|
||||
onDragCallback.onDragStart(this)
|
||||
onDragListener.onDragStart(this)
|
||||
return true
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev)
|
||||
@ -81,7 +81,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onTouchEvent(ev: MotionEvent): Boolean {
|
||||
val onDragListener = this.onDragCallback
|
||||
val onDragListener = this.onDragListener
|
||||
?: return super.onInterceptTouchEvent(ev)
|
||||
|
||||
when(ev.action) {
|
||||
@ -93,7 +93,7 @@ class NDownloadSuspendIconLayout @JvmOverloads constructor(
|
||||
return super.onTouchEvent(ev)
|
||||
}
|
||||
|
||||
interface OnDragCallback {
|
||||
interface OnDragListener {
|
||||
|
||||
fun onDragStart(v: View)
|
||||
|
||||
|
||||
@ -109,7 +109,7 @@ class NDownloadSuspendIconView @JvmOverloads constructor(
|
||||
addView(it, layoutParams)
|
||||
}
|
||||
|
||||
fun setOnIconClickListener(listener: OnIconClickListener?) {
|
||||
fun setOnIconClickListener(listener: OnIconClickListener) {
|
||||
this.onIconClickListener = listener
|
||||
}
|
||||
|
||||
|
||||
@ -56,13 +56,6 @@ class NDownloadSuspendCloseWindow(context: Context, suspend: Boolean = true) :
|
||||
it.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
popupAnimator.removeListener(hiddenListener)
|
||||
popupAnimator.removeListener(shownListener)
|
||||
popupAnimator.cancel()
|
||||
root.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: NDownloadSuspendCloseLayout) {
|
||||
view.apply {
|
||||
popupAnimator.addUpdateListener { value ->
|
||||
|
||||
@ -19,7 +19,7 @@ import com.gh.ndownload.suspendwindow.view.NDownloadSuspendIconView
|
||||
*/
|
||||
class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, private val draggable: Boolean = true) :
|
||||
NDownloadSuspendWindow<NDownloadSuspendIconLayout>(context, suspend),
|
||||
NDownloadSuspendIconLayout.OnDragCallback {
|
||||
NDownloadSuspendIconLayout.OnDragListener {
|
||||
|
||||
companion object {
|
||||
private const val TYPE_DRAGGING = 0
|
||||
@ -40,7 +40,7 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
|
||||
/**
|
||||
* 图标拖动回调事件
|
||||
*/
|
||||
private var onDragCallback: NDownloadSuspendIconLayout.OnDragCallback? = null
|
||||
private var onDragListener: NDownloadSuspendIconLayout.OnDragListener? = null
|
||||
|
||||
/**
|
||||
* 手指松开拖动的悬浮图标时,自动吸附到屏幕侧边的动画
|
||||
@ -67,30 +67,20 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
|
||||
get() = root.getLocationOnScreen(screenLocation)
|
||||
.let { screenLocation[1] - layoutParams.y }
|
||||
|
||||
fun setOnDragCallback(onDragCallback: NDownloadSuspendIconLayout.OnDragCallback?) {
|
||||
this.onDragCallback = onDragCallback
|
||||
fun setOnDragListener(onDragListener: NDownloadSuspendIconLayout.OnDragListener?) {
|
||||
this.onDragListener = onDragListener
|
||||
}
|
||||
|
||||
override fun onAttach() {
|
||||
if (draggable) {
|
||||
root.post(initRunnable)
|
||||
root.setOnDragCallback(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDetach() {
|
||||
if (draggable) {
|
||||
root.removeCallbacks(initRunnable)
|
||||
root.setOnDragCallback(null)
|
||||
}
|
||||
edgeAnimator.removeAllUpdateListeners()
|
||||
edgeAnimator.removeAllListeners()
|
||||
override fun detach() {
|
||||
super.detach()
|
||||
root.removeCallbacks(initRunnable)
|
||||
edgeAnimator.cancel()
|
||||
}
|
||||
|
||||
@SuppressLint("InflateParams")
|
||||
override fun onCreateView(context: Context): NDownloadSuspendIconLayout =
|
||||
NDownloadSuspendIconLayout(context).also {
|
||||
if (draggable) it.setOnDragListener(this)
|
||||
it.clipChildren = false
|
||||
it.clipToPadding = false
|
||||
}
|
||||
@ -106,6 +96,7 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
|
||||
override fun onViewCreated(view: NDownloadSuspendIconLayout) {
|
||||
super.onViewCreated(view)
|
||||
view.setBackgroundResource(R.drawable.shape_download_suspend_icon_idle_right)
|
||||
if (draggable) view.post(initRunnable)
|
||||
}
|
||||
|
||||
override fun onConfigurationChanged(configuration: Configuration) {
|
||||
@ -144,7 +135,7 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
|
||||
root.icon.setDownloadIconUrl(url)
|
||||
}
|
||||
|
||||
fun setOnIconClickListener(listener: NDownloadSuspendIconView.OnIconClickListener?) {
|
||||
fun setOnIconClickListener(listener: NDownloadSuspendIconView.OnIconClickListener) {
|
||||
root.icon.setOnIconClickListener(listener)
|
||||
}
|
||||
|
||||
@ -212,16 +203,16 @@ class NDownloadSuspendIconWindow(context: Context, suspend: Boolean = true, priv
|
||||
|
||||
override fun onDragStart(v: View) {
|
||||
setIconLayoutStyleByType(TYPE_DRAGGING)
|
||||
onDragCallback?.onDragStart(v)
|
||||
onDragListener?.onDragStart(v)
|
||||
}
|
||||
|
||||
override fun onDragMove(v: View, x: Float, y: Float) {
|
||||
moveToPosition(v, x, y)
|
||||
onDragCallback?.onDragMove(v, x, y)
|
||||
onDragListener?.onDragMove(v, x, y)
|
||||
}
|
||||
|
||||
override fun onDragEnd(v: View, x: Float, y: Float) {
|
||||
animateToEdge(v, x, y)
|
||||
onDragCallback?.onDragEnd(v, x, y)
|
||||
onDragListener?.onDragEnd(v, x, y)
|
||||
}
|
||||
}
|
||||
@ -46,35 +46,22 @@ abstract class NDownloadSuspendWindow<T : View>(
|
||||
onViewCreated(it)
|
||||
}
|
||||
|
||||
fun attach() {
|
||||
open fun attach() {
|
||||
if (!isAttached) {
|
||||
try {
|
||||
_isAttached = true
|
||||
windowManager.addView(root, layoutParams)
|
||||
context.registerComponentCallbacks(this)
|
||||
onAttach()
|
||||
} catch (e: Throwable) {
|
||||
// 处理“Unable to add window android.view.ViewRootImpl$W@7bc9502 -- permission denied for window type 2038”的异常
|
||||
// 目前Sentry上仅Z10型号的手机报这个错误(Android 8.1.0),这里明明已经授予了SYSTEM_ALERT_WINDOW权限,但是就是会抛异常= =,只能暴力捕获处理了。。。
|
||||
// 相关异常的链接:https://sentry.shanqu.cc/organizations/lightgame/issues/407275/?project=22
|
||||
_isAttached = false
|
||||
}
|
||||
_isAttached = true
|
||||
windowManager.addView(root, layoutParams)
|
||||
context.registerComponentCallbacks(this)
|
||||
}
|
||||
}
|
||||
|
||||
fun detach() {
|
||||
open fun detach() {
|
||||
if (isAttached) {
|
||||
_isAttached = false
|
||||
onDetach()
|
||||
context.unregisterComponentCallbacks(this)
|
||||
windowManager.removeView(root)
|
||||
}
|
||||
}
|
||||
|
||||
protected open fun onAttach() = Unit
|
||||
|
||||
protected open fun onDetach() = Unit
|
||||
|
||||
protected open fun onViewCreated(view: T) = Unit
|
||||
|
||||
override fun onConfigurationChanged(configuration: Configuration) = Unit
|
||||
|
||||
@ -56,7 +56,6 @@ import com.gh.gamecenter.history.HistoryActivity
|
||||
import com.gh.gamecenter.login.user.UserManager
|
||||
import com.gh.gamecenter.manager.PackagesManager
|
||||
import com.gh.gamecenter.mygame.MyGameActivity
|
||||
import com.gh.gamecenter.packagehelper.PackageFilterManager
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
@ -1998,7 +1997,7 @@ object VHelper {
|
||||
validInstalledPackageList.add(packageName)
|
||||
}
|
||||
}
|
||||
PackageRepository.addInstalledGames(PackageFilterManager(), validInstalledPackageList, true)
|
||||
PackageRepository.addInstalledGames(validInstalledPackageList, true)
|
||||
}
|
||||
|
||||
/**
|
||||
@ -2278,13 +2277,6 @@ object VHelper {
|
||||
val token = UserManager.getInstance().token
|
||||
val userName = UserManager.getInstance().userInfoEntity?.name
|
||||
val userAvatar = UserManager.getInstance().userInfoEntity?.icon
|
||||
|
||||
if (token == null) {
|
||||
SentryHelper.onEvent("LAUNCH_SUCCESS_BUT_TOKEN_EMPTY", "gid", HaloApp.getInstance().gid)
|
||||
Utils.log(LOG_TAG, "登录成功,但 token 为空")
|
||||
return@runOnIoThread
|
||||
}
|
||||
|
||||
Utils.log(
|
||||
LOG_TAG,
|
||||
"登录成功,插入用户信息:token=${token},userName=${userName}, uri=${va.getUriAuthorizationString()}"
|
||||
|
||||
@ -1,6 +1,6 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<shape xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:shape="rectangle">
|
||||
<corners android:radius="20dp" />
|
||||
<solid android:color="@color/ui_skeleton_frame" />
|
||||
<corners android:radius="14dp" />
|
||||
<solid android:color="#14FFFFFF" />
|
||||
</shape>
|
||||
BIN
app/src/main/res/drawable-xhdpi/ic_category_arrow_down.webp
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_category_arrow_down.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 290 B |
BIN
app/src/main/res/drawable-xhdpi/ic_category_arrow_up.webp
Normal file
BIN
app/src/main/res/drawable-xhdpi/ic_category_arrow_up.webp
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 304 B |
Binary file not shown.
|
Before Width: | Height: | Size: 554 B |
34
app/src/main/res/layout/catalog_item.xml
Normal file
34
app/src/main/res/layout/catalog_item.xml
Normal file
@ -0,0 +1,34 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="52dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/selected_tag"
|
||||
android:layout_width="14dp"
|
||||
android:layout_height="6dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="34dp"
|
||||
android:src="@drawable/ic_catalog_selected"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/catalog_name"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="12sp"
|
||||
tools:text="精选" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/recommend_tag"
|
||||
android:layout_width="16dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginLeft="52dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:src="@drawable/ic_recommend"
|
||||
tools:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
50
app/src/main/res/layout/fragment_catalog.xml
Normal file
50
app/src/main/res/layout/fragment_catalog.xml
Normal file
@ -0,0 +1,50 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface">
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/ui_divider" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_catalog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginTop="1dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_catalog"
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_background" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/container_sub_catalog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</LinearLayout>
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_none_data"
|
||||
layout="@layout/reuse_none_data" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_data_exception"
|
||||
layout="@layout/reuse_data_exception" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
</RelativeLayout>
|
||||
60
app/src/main/res/layout/fragment_catalog_list.xml
Normal file
60
app/src/main/res/layout/fragment_catalog_list.xml
Normal file
@ -0,0 +1,60 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/list_skeleton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/filter_container" />
|
||||
|
||||
<com.gh.common.view.CatalogFilterView
|
||||
android:id="@+id/filter_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_below="@id/filter_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:background="@color/ui_background" />
|
||||
|
||||
<androidx.swiperefreshlayout.widget.SwipeRefreshLayout
|
||||
android:id="@+id/list_refresh"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/divider">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/list_rv"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
</androidx.swiperefreshlayout.widget.SwipeRefreshLayout>
|
||||
|
||||
<include
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
|
||||
<include
|
||||
layout="@layout/reuse_no_connection"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/filter_container" />
|
||||
|
||||
<include
|
||||
layout="@layout/reuse_none_data"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/filter_container" />
|
||||
|
||||
<include
|
||||
layout="@layout/reuse_data_exception"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/filter_container" />
|
||||
|
||||
</RelativeLayout>
|
||||
27
app/src/main/res/layout/fragment_category_skeleton.xml
Normal file
27
app/src/main/res/layout/fragment_category_skeleton.xml
Normal file
@ -0,0 +1,27 @@
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:paddingTop="4dp"
|
||||
android:orientation="vertical">
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
<include layout="@layout/item_skeleton_category" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
@ -142,14 +142,7 @@
|
||||
android:focusable="true"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="horizontal"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent" />
|
||||
|
||||
<FrameLayout
|
||||
android:id="@+id/skeleton"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
</com.gh.gamecenter.common.view.MaterializedConstraintLayout>
|
||||
@ -1,316 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_above="@id/b"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.gh.gamecenter.common.view.StatusBarView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="48dp"
|
||||
android:paddingStart="16dp"
|
||||
android:paddingEnd="16dp">
|
||||
|
||||
<View
|
||||
android:id="@+id/sb"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="32dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_40"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ic1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/ic1"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/ic2"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/ic2"
|
||||
android:layout_width="24dp"
|
||||
android:layout_height="24dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/bg_skeleton_radius_4"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="28dp"
|
||||
android:gravity="bottom"
|
||||
android:paddingTop="8dp">
|
||||
|
||||
<View
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="20dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginStart="24dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<ImageView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="29dp"
|
||||
android:layout_marginTop="7dp"
|
||||
android:src="@drawable/ic_home_tab_indicator_skeleton" />
|
||||
|
||||
<androidx.constraintlayout.widget.ConstraintLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp"
|
||||
android:paddingHorizontal="16dp"
|
||||
android:paddingVertical="8dp">
|
||||
|
||||
<View
|
||||
android:id="@+id/square"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="140dp"
|
||||
android:layout_marginEnd="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_10"
|
||||
app:layout_constraintBottom_toBottomOf="parent"
|
||||
app:layout_constraintEnd_toStartOf="@id/rect1"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
app:layout_constraintTop_toTopOf="parent" />
|
||||
|
||||
<View
|
||||
android:id="@+id/rect1"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="66dp"
|
||||
android:background="@drawable/bg_skeleton_radius_10"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toEndOf="@id/square"
|
||||
app:layout_constraintTop_toTopOf="@id/square" />
|
||||
|
||||
<View
|
||||
android:id="@+id/rect2"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="66dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_10"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintHorizontal_bias="0"
|
||||
app:layout_constraintHorizontal_chainStyle="packed"
|
||||
app:layout_constraintStart_toStartOf="@id/rect1"
|
||||
app:layout_constraintTop_toBottomOf="@id/rect1" />
|
||||
|
||||
</androidx.constraintlayout.widget.ConstraintLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="87dp"
|
||||
android:orientation="horizontal"
|
||||
android:paddingHorizontal="18dp"
|
||||
android:paddingVertical="16dp">
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_skeleton_radius_8" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="26dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_skeleton_radius_8" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="26dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_skeleton_radius_8" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="26dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_skeleton_radius_8" />
|
||||
|
||||
<View
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginStart="26dp"
|
||||
android:layout_weight="1"
|
||||
android:background="@drawable/bg_skeleton_radius_8" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:paddingTop="16dp"
|
||||
android:paddingBottom="16dp">
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="18dp">
|
||||
|
||||
<View
|
||||
android:layout_width="54dp"
|
||||
android:layout_height="18dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="16dp">
|
||||
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginTop="53dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<HorizontalScrollView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:scrollbars="none">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="8dp"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_simple_game" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
</HorizontalScrollView>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_game" />
|
||||
|
||||
<include layout="@layout/fragment_main_skeleton_item_game" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/b"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="54dp"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_main_skeleton_item_bottom_tab"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_main_skeleton_item_bottom_tab"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_main_skeleton_item_bottom_tab"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_main_skeleton_item_bottom_tab"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
<include
|
||||
layout="@layout/fragment_main_skeleton_item_bottom_tab"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_weight="1" />
|
||||
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
@ -1,26 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="22dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
|
||||
<View
|
||||
android:layout_width="22dp"
|
||||
android:layout_height="10dp"
|
||||
android:layout_marginTop="6dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -1,47 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingLeft="16dp"
|
||||
android:paddingTop="8dp"
|
||||
android:paddingRight="16dp"
|
||||
android:paddingBottom="8dp">
|
||||
|
||||
<View
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:background="@drawable/bg_skeleton_radius_14" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginLeft="12dp"
|
||||
android:layout_marginTop="4dp"
|
||||
android:layout_weight="1"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="72dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="160dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="32dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
</LinearLayout>
|
||||
|
||||
<View
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_gravity="center_vertical"
|
||||
android:background="@drawable/bg_skeleton_radius_999" />
|
||||
</LinearLayout>
|
||||
@ -1,49 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="80dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="11dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="44dp"
|
||||
android:layout_height="11dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4"/>
|
||||
|
||||
<View
|
||||
android:layout_width="5dp"
|
||||
android:layout_height="5dp"
|
||||
android:layout_marginTop="5dp"
|
||||
android:layout_marginBottom="5dp"
|
||||
android:background="@drawable/bg_skeleton_radius_40"/>
|
||||
|
||||
<View
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:background="@drawable/bg_skeleton_radius_14" />
|
||||
|
||||
<View
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="56dp"
|
||||
android:layout_height="28dp"
|
||||
android:layout_marginTop="12dp"
|
||||
android:background="@drawable/bg_skeleton_radius_40" />
|
||||
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
30
app/src/main/res/layout/fragment_sub_catalog.xml
Normal file
30
app/src/main/res/layout/fragment_sub_catalog.xml
Normal file
@ -0,0 +1,30 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:background="@color/ui_surface">
|
||||
|
||||
<androidx.recyclerview.widget.RecyclerView
|
||||
android:id="@+id/rv_sub_catalog"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_no_connection"
|
||||
layout="@layout/reuse_no_connection" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_none_data"
|
||||
layout="@layout/reuse_none_data" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_data_exception"
|
||||
layout="@layout/reuse_data_exception" />
|
||||
|
||||
<include
|
||||
android:id="@+id/reuse_loading"
|
||||
layout="@layout/reuse_loading"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerInParent="true" />
|
||||
</RelativeLayout>
|
||||
91
app/src/main/res/layout/item_category.xml
Normal file
91
app/src/main/res/layout/item_category.xml
Normal file
@ -0,0 +1,91 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:id="@+id/divider"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="8dp"
|
||||
android:background="@color/ui_background" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/container_category"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="37dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_primary_category"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/iconIv"
|
||||
android:layout_width="25dp"
|
||||
android:layout_height="25dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/categoryName"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="8dp"
|
||||
android:textColor="#000"
|
||||
android:textSize="12sp"
|
||||
tools:text="不是不能用" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_sub_category"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_toRightOf="@id/container_primary_category"
|
||||
android:gravity="center_vertical"
|
||||
android:orientation="vertical">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_unexpandable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical" />
|
||||
|
||||
<net.cachapa.expandablelayout.ExpandableLayout
|
||||
android:id="@+id/container_expandable"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:el_expanded="false" />
|
||||
</LinearLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="37dp"
|
||||
android:layout_below="@id/container_category">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/iv_toggle"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentBottom="true"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:paddingLeft="20dp"
|
||||
android:paddingTop="20dp"
|
||||
android:paddingRight="20dp"
|
||||
android:paddingBottom="10dp"
|
||||
android:src="@drawable/ic_category_arrow_down"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
88
app/src/main/res/layout/item_skeleton_category.xml
Normal file
88
app/src/main/res/layout/item_skeleton_category.xml
Normal file
@ -0,0 +1,88 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal"
|
||||
android:paddingTop="38dp"
|
||||
android:paddingBottom="36dp"
|
||||
android:layout_marginTop="8dp">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/viewLeft"
|
||||
android:layout_width="90dp"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="28dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="28dp"
|
||||
android:layout_height="12dp"
|
||||
android:layout_marginTop="8dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:gravity="center_horizontal"
|
||||
android:orientation="vertical">
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
|
||||
<View
|
||||
android:layout_width="48dp"
|
||||
android:layout_height="16dp"
|
||||
android:layout_marginTop="16dp"
|
||||
android:background="@drawable/bg_skeleton_radius_4" />
|
||||
</LinearLayout>
|
||||
|
||||
|
||||
</LinearLayout>
|
||||
36
app/src/main/res/layout/layout_catalog_filter.xml
Normal file
36
app/src/main/res/layout/layout_catalog_filter.xml
Normal file
@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:id="@+id/config_controller"
|
||||
style="@style/filterRoot">
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_type"
|
||||
style="@style/filterItemContainer">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/type_tv"
|
||||
style="@style/filterItem"
|
||||
tools:text="热门推荐" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_catalog"
|
||||
style="@style/filterItemContainer">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/catalog_tv"
|
||||
style="@style/filterItem"
|
||||
tools:text="MOD单机" />
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/container_size"
|
||||
style="@style/filterItemContainer">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/size_tv"
|
||||
style="@style/filterItem"
|
||||
android:text="全部大小" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
@ -7,4 +7,15 @@
|
||||
android:id="@+id/surface_container"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
|
||||
<RelativeLayout
|
||||
android:id="@+id/thumb"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/thumbImage"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent" />
|
||||
</RelativeLayout>
|
||||
</FrameLayout>
|
||||
59
app/src/main/res/layout/layout_sub_category.xml
Normal file
59
app/src/main/res/layout/layout_sub_category.xml
Normal file
@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="horizontal">
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_left_sub_category"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:ellipsize="end"
|
||||
android:textColor="@color/text_black"
|
||||
android:maxLines="1"
|
||||
android:textSize="12sp"
|
||||
tools:text="星海爭霸" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_left"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="15dp"
|
||||
android:background="#ededed"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_center_sub_category"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:ellipsize="end"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/text_black"
|
||||
android:textSize="12sp"
|
||||
tools:text="空海爭霸" />
|
||||
|
||||
<View
|
||||
android:id="@+id/divider_right"
|
||||
android:layout_width="1dp"
|
||||
android:layout_height="15dp"
|
||||
android:background="#ededed"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/tv_right_sub_category"
|
||||
android:layout_width="0dp"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center"
|
||||
android:maxLines="1"
|
||||
android:textColor="@color/text_black"
|
||||
android:textSize="12sp"
|
||||
tools:text="地海爭霸" />
|
||||
</LinearLayout>
|
||||
@ -11,12 +11,12 @@
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/adImage"
|
||||
style="@style/frescoStyle"
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="0dp"
|
||||
android:tint="#01000000"
|
||||
android:visibility="gone"
|
||||
app:actualImageScaleType="centerCrop"
|
||||
app:fadeDuration="0"
|
||||
android:visibility="gone"
|
||||
app:layout_constraintBottom_toTopOf="@id/startAdIcpContainer"
|
||||
app:layout_constraintEnd_toEndOf="parent"
|
||||
app:layout_constraintStart_toStartOf="parent"
|
||||
|
||||
51
app/src/main/res/layout/sub_catalog_item.xml
Normal file
51
app/src/main/res/layout/sub_catalog_item.xml
Normal file
@ -0,0 +1,51 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="17dp"
|
||||
android:background="@drawable/bg_sub_catalog_item" />
|
||||
|
||||
<com.facebook.drawee.view.SimpleDraweeView
|
||||
android:id="@+id/catalog_icon"
|
||||
android:layout_width="64dp"
|
||||
android:layout_height="64dp"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="17dp"
|
||||
app:fadeDuration="500"
|
||||
app:placeholderImage="@drawable/occupy"
|
||||
app:placeholderImageScaleType="fitXY"
|
||||
app:roundedCornerRadius="16dp"
|
||||
app:roundingBorderColor="@color/ui_background"
|
||||
app:roundingBorderWidth="1dp" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/catalog_name"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@+id/catalog_icon"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:layout_marginTop="8dp"
|
||||
android:layout_marginBottom="7dp"
|
||||
android:includeFontPadding="false"
|
||||
android:textColor="@color/text_primary"
|
||||
android:textSize="12sp"
|
||||
tools:text="加速插件" />
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/recommend_tag"
|
||||
android:layout_width="18dp"
|
||||
android:layout_height="14dp"
|
||||
android:layout_alignLeft="@+id/catalog_icon"
|
||||
android:layout_alignBottom="@+id/catalog_icon"
|
||||
android:layout_marginLeft="50dp"
|
||||
android:layout_marginBottom="54dp"
|
||||
android:src="@drawable/ic_recommend"
|
||||
tools:visibility="visible" />
|
||||
</RelativeLayout>
|
||||
@ -17,8 +17,8 @@ buildscript {
|
||||
password("u9gZYH4MQEwLLQZK")
|
||||
}
|
||||
}
|
||||
maven { url "https://maven.google.com" }
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url "https://maven.google.com" }
|
||||
}
|
||||
|
||||
dependencies {
|
||||
@ -41,8 +41,7 @@ allprojects {
|
||||
google()
|
||||
jcenter()
|
||||
mavenCentral()
|
||||
maven { url 'https://maven.aliyun.com/repository/public' }
|
||||
maven { url 'https://maven.aliyun.com/repository/central'}
|
||||
maven { url 'https://jitpack.io' }
|
||||
maven { url 'https://maven.aliyun.com/nexus/content/repositories/releases/' }
|
||||
maven { url 'https://artifact.bytedance.com/repository/Volcengine/'}
|
||||
maven { url 'https://artifact.bytedance.com/repository/pangle' }
|
||||
@ -54,7 +53,6 @@ allprojects {
|
||||
// 配置HMS Core SDK的Maven仓地址。
|
||||
maven { url 'https://developer.huawei.com/repo/' }
|
||||
maven { url 'https://developer.hihonor.com/repo' }
|
||||
maven { url 'https://jitpack.io' }
|
||||
}
|
||||
}
|
||||
task clean(type: Delete) {
|
||||
|
||||
@ -7,8 +7,8 @@ ext {
|
||||
targetSdkVersion = 30
|
||||
|
||||
// application info (每个大版本之间的 versionCode 增加 20)
|
||||
versionCode = 1095
|
||||
versionName = "5.37.5"
|
||||
versionCode = 1090
|
||||
versionName = "5.37.0"
|
||||
applicationId = "com.gh.gamecenter"
|
||||
applicationIdGat = "com.gh.gamecenter.intl"
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user