Merge branch 'feature-games_collection' into dev-5.5.0
This commit is contained in:
@ -22,12 +22,10 @@ import com.gh.common.view.RichEditor
|
||||
import com.gh.gamecenter.CropImageActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.LocalVideoEntity
|
||||
import com.gh.gamecenter.entity.VideoEntity
|
||||
import com.gh.gamecenter.qa.editor.GameActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertAnswerWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.InsertArticleWrapperActivity
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.gh.gamecenter.qa.editor.*
|
||||
import com.gh.gamecenter.qa.entity.AnswerEntity
|
||||
import com.gh.gamecenter.qa.entity.ArticleEntity
|
||||
import com.gh.gamecenter.qa.entity.EditorInsertEntity
|
||||
@ -114,6 +112,14 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
INSERT_GAME_COLLECTION_CODE -> {
|
||||
val gameCollectionEntity = data?.getParcelableExtra<GamesCollectionEntity>(GamesCollectionEntity::class.java.simpleName)
|
||||
if (gameCollectionEntity != null) {
|
||||
mRichEditor.focusEditor()
|
||||
insertData = EditorInsertEntity.transform(gameCollectionEntity)
|
||||
mRichEditor.insertCustomStyleLink(insertData)
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_IMAGE -> {
|
||||
if (data != null) mViewModel.uploadPic(data)
|
||||
}
|
||||
@ -248,7 +254,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
|
||||
R.id.editor_paragraph_h4, R.id.editor_font_container, R.id.editor_paragraph_container,
|
||||
R.id.editor_paragraph_quote, R.id.editor_link_answer, R.id.editor_link_article,
|
||||
R.id.editor_link_game, R.id.editor_link_video, R.id.uploadVideoGuideClose,
|
||||
R.id.originalTipsClose
|
||||
R.id.originalTipsClose, R.id.editor_link_game_collection
|
||||
)
|
||||
fun onRichClick(view: View) {
|
||||
when (view.id) {
|
||||
@ -354,6 +360,12 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
|
||||
INSERT_GAME_CODE
|
||||
)
|
||||
}
|
||||
R.id.editor_link_game_collection -> {
|
||||
startActivityForResult(
|
||||
InsertGameCollectionWrapperActivity.getIntent(this),
|
||||
INSERT_GAME_COLLECTION_CODE
|
||||
)
|
||||
}
|
||||
R.id.editor_link_video -> {
|
||||
chooseVideo()
|
||||
}
|
||||
@ -714,6 +726,7 @@ abstract class BaseRichEditorActivity<VM : BaseRichEditorViewModel> : ToolBarAct
|
||||
const val INSERT_ANSWER_CODE = 411
|
||||
const val INSERT_ARTICLE_CODE = 412
|
||||
const val INSERT_GAME_CODE = 413
|
||||
const val INSERT_GAME_COLLECTION_CODE = 414
|
||||
const val MAX_INPUT_TEXT_NUM = 10000
|
||||
const val MAX_MEDIA_COUNT = 20
|
||||
|
||||
|
||||
@ -340,6 +340,11 @@ object DefaultUrlHandler {
|
||||
val position = uri.getQueryParameter("position") ?: ""
|
||||
DirectUtils.directToHelpAndFeedback(context, position.toInt())
|
||||
}
|
||||
|
||||
EntranceUtils.HOST_GAME_COLLECTION_DETAIL -> {
|
||||
DirectUtils.directToGameCollectionDetail(context, id, entrance)
|
||||
}
|
||||
|
||||
else -> DialogHelper.showUpgradeDialog(context)
|
||||
}
|
||||
return true
|
||||
|
||||
@ -234,6 +234,8 @@ public class Constants {
|
||||
public static final String SP_SHOULD_SHOW_GAME_DETAIL_INSTALL_GUIDE = "should_show_game_detail_install_guide";
|
||||
// 儿童/青少年模式
|
||||
public static final String SP_TEENAGER_MODE = "teenager_mode";
|
||||
// 我的游戏引导
|
||||
public static final String SP_MY_GAME_GUIDE = "my_game_guide";
|
||||
|
||||
//手机号码匹配规则
|
||||
public static final String REGEX_MOBILE = "^((13[0-9])|(15[^4,\\D])|(18[0,5-9]))\\d{8}$";
|
||||
@ -344,12 +346,19 @@ public class Constants {
|
||||
public static final String ACTIVITY_DETAIL_ADDRESS_DEV = "https://static-web.ghzs.com/ghzs_activity_dev/common.html?from=ghzs";
|
||||
public static final String ACTIVITY_DETAIL_ADDRESS = "https://static-web.ghzs.com/ghzs_activity_prod/common.html?from=ghzs";
|
||||
|
||||
// 游戏单详情分享链接
|
||||
public static final String GAME_COLLECTION_SHARE_ADDRESS_DEV = "https://dev-and-static.ghzs.com/web/game_collection/index.html#/?from=ghzs";
|
||||
public static final String GAME_COLLECTION_SHARE_ADDRESS = "https://and-static.ghzs.com/web/game_collection/index.html#/?from=ghzs";
|
||||
|
||||
// 青少年模式找回密码
|
||||
public static final String TEEN_MODE_RESET_PASSWORD = "https://resource.ghzs.com/page/privacy_policies/help_password.html";
|
||||
|
||||
// 儿童/青少年使用须知
|
||||
public static final String TEEN_MODE_RULE = "https://resource.ghzs.com/page/privacy_policies/teenager_privacy.html";
|
||||
|
||||
//游戏单管理规范
|
||||
public static final String GAME_COLLECTION_RULE = "https://resource.ghzs.com/page/privacy_policies/game_collection.html";
|
||||
|
||||
//最少需要多少数据才能上传
|
||||
public static final int DATA_AMOUNT = 20;
|
||||
|
||||
|
||||
@ -37,6 +37,7 @@ public class ItemViewType {
|
||||
public static final int BLANK_DIVIDER = 29; // 空白补充区域
|
||||
public static final int COMMON_LINK_COLLECTION = 30; // 通用链接合集
|
||||
public static final int RANK_COLLECTION = 31; // 排行榜样式专题合集
|
||||
public static final int GAME_COLLECTION_ITEM = 32; // 游戏单
|
||||
|
||||
/**
|
||||
* 普通列表
|
||||
|
||||
@ -6,6 +6,7 @@ import androidx.room.RoomDatabase
|
||||
import androidx.room.TypeConverters
|
||||
import androidx.room.migration.Migration
|
||||
import androidx.sqlite.db.SupportSQLiteDatabase
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.HistoryGameEntity
|
||||
import com.gh.gamecenter.entity.MyVideoEntity
|
||||
import com.gh.gamecenter.entity.NewsEntity
|
||||
@ -15,7 +16,7 @@ import com.gh.gamecenter.room.converter.*
|
||||
import com.gh.gamecenter.room.dao.*
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class], version = 9, exportSchema = false)
|
||||
@Database(entities = [AnswerEntity::class, ArticleEntity::class, NewsEntity::class, HistoryGameEntity::class, MyVideoEntity::class, GamesCollectionEntity::class], version = 10, exportSchema = false)
|
||||
@TypeConverters(CountConverter::class,
|
||||
CommunityConverter::class,
|
||||
TimeConverter::class,
|
||||
@ -28,7 +29,10 @@ import com.halo.assistant.HaloApp
|
||||
UserConverter::class,
|
||||
ImageInfoConverter::class,
|
||||
VideoInfoConverter::class,
|
||||
QuestionsConverter::class)
|
||||
QuestionsConverter::class,
|
||||
MeConverter::class,
|
||||
SimpleGameListConverter::class,
|
||||
TagInfoListConverter::class)
|
||||
|
||||
abstract class HistoryDatabase : RoomDatabase() {
|
||||
|
||||
@ -37,6 +41,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
abstract fun newsDao(): NewsHistoryDao
|
||||
abstract fun gameDao(): GameDao
|
||||
abstract fun videoHistoryDao(): VideoHistoryDao
|
||||
abstract fun gamesCollectionDao(): GamesCollectionDao
|
||||
|
||||
companion object {
|
||||
|
||||
@ -101,6 +106,13 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
}
|
||||
}
|
||||
|
||||
val MIGRATION_9_10: Migration = object : Migration(9, 10) {
|
||||
override fun migrate(database: SupportSQLiteDatabase) {
|
||||
database.execSQL("CREATE TABLE GamesCollectionEntity (id TEXT NOT NULL PRIMARY KEY, tags TEXT DEFAULT '', games TEXT DEFAULT '', title TEXT NOT NULL DEFAULT '', intro TEXT NOT NULL DEFAULT '', cover TEXT NOT NULL DEFAULT '', display TEXT NOT NULL DEFAULT '', stamp TEXT NOT NULL DEFAULT '', count TEXT NOT NULL DEFAULT '', user TEXT NOT NULL DEFAULT '', me TEXT NOT NULL DEFAULT '', orderTag INTEGER NOT NULL DEFAULT 0)"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
val instance by lazy {
|
||||
Room.databaseBuilder(HaloApp.getInstance().application, HistoryDatabase::class.java, "USER_TRACK_HISTORY_DATABASE")
|
||||
.addMigrations(MIGRATION_2_3)
|
||||
@ -110,6 +122,7 @@ abstract class HistoryDatabase : RoomDatabase() {
|
||||
.addMigrations(MIGRATION_6_7)
|
||||
.addMigrations(MIGRATION_7_8)
|
||||
.addMigrations(MIGRATION_8_9)
|
||||
.addMigrations(MIGRATION_9_10)
|
||||
.build()
|
||||
}
|
||||
}
|
||||
|
||||
@ -24,6 +24,11 @@ object HistoryHelper {
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.articleDao().addArticle(articleEntity) } }
|
||||
}
|
||||
|
||||
fun insertGamesCollectionEntity(gamesCollectionDetailEntity: GamesCollectionDetailEntity) {
|
||||
val gamesCollectionEntity = convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity)
|
||||
runOnIoThread { tryCatchInRelease { HistoryDatabase.instance.gamesCollectionDao().addGamesCollection(gamesCollectionEntity) } }
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun insertGameEntity(gameEntity: GameEntity) {
|
||||
val historyGameEntity = convertGameEntityToHistoryGameEntity(gameEntity)
|
||||
@ -145,4 +150,29 @@ object HistoryHelper {
|
||||
return answerEntity
|
||||
}
|
||||
|
||||
private fun convertGamesCollectionDetailToGamesCollection(gamesCollectionDetailEntity: GamesCollectionDetailEntity): GamesCollectionEntity {
|
||||
val gamesCollectionEntity = GamesCollectionEntity()
|
||||
|
||||
gamesCollectionEntity.id = gamesCollectionDetailEntity.id
|
||||
gamesCollectionEntity.tags = gamesCollectionDetailEntity.tags
|
||||
gamesCollectionEntity.games = ArrayList(gamesCollectionDetailEntity.games?.take(3)?.map { it.toSimpleGame() })
|
||||
gamesCollectionEntity.title = gamesCollectionDetailEntity.title
|
||||
gamesCollectionEntity.intro = gamesCollectionDetailEntity.intro
|
||||
gamesCollectionEntity.cover = gamesCollectionDetailEntity.cover
|
||||
gamesCollectionEntity.display = gamesCollectionDetailEntity.display
|
||||
gamesCollectionEntity.orderTag = System.currentTimeMillis()
|
||||
gamesCollectionEntity.stamp = gamesCollectionDetailEntity.stamp
|
||||
gamesCollectionEntity.count = gamesCollectionDetailEntity.count
|
||||
gamesCollectionDetailEntity.user?.run {
|
||||
gamesCollectionEntity.user = User(
|
||||
id = id ?: "",
|
||||
name = name ?: "",
|
||||
icon = icon ?: "",
|
||||
badge = badge)
|
||||
}
|
||||
gamesCollectionEntity.me = gamesCollectionDetailEntity.me
|
||||
|
||||
return gamesCollectionEntity
|
||||
}
|
||||
|
||||
}
|
||||
@ -107,6 +107,20 @@ object CommentHelper {
|
||||
)
|
||||
}
|
||||
|
||||
fun showGameCollectionCommentOption(
|
||||
view: View,
|
||||
commentEntity: CommentEntity,
|
||||
listener: OnCommentOptionClickListener?
|
||||
) {
|
||||
showCommentOptions(
|
||||
view = view,
|
||||
commentEntity = commentEntity,
|
||||
showConversation = false,
|
||||
ignoreModerator = true,
|
||||
listener = listener
|
||||
)
|
||||
}
|
||||
|
||||
private fun showCommentOptions(
|
||||
view: View,
|
||||
commentEntity: CommentEntity,
|
||||
|
||||
@ -35,10 +35,12 @@ import com.gh.gamecenter.fragment.MainWrapperFragment
|
||||
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActivity
|
||||
import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailActivity
|
||||
import com.gh.gamecenter.game.upload.GameSubmissionActivity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.gh.gamecenter.gamedetail.fuli.kaifu.ServersCalendarActivity
|
||||
import com.gh.gamecenter.gamedetail.history.HistoryApkListActivity
|
||||
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.mygame.PlayedGameActivity
|
||||
import com.gh.gamecenter.personalhome.UserHomeActivity
|
||||
@ -353,6 +355,8 @@ object DirectUtils {
|
||||
|
||||
//"h5_game_center" -> directLetoGameCenter(context)
|
||||
|
||||
"game_list" -> directToGameCollectionSquare(context, entrance, "", "", "")
|
||||
|
||||
"" -> {
|
||||
// do nothing
|
||||
}
|
||||
@ -1642,4 +1646,31 @@ object DirectUtils {
|
||||
bundle.putString(KEY_COLLECTION_ID, collectionId)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至游戏单广场
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToGameCollectionSquare(context: Context, entrance: String = "", forumName: String = "", gameCollectionTitle: String = "", gameCollectionId: String = "") {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_TO, GameCollectionSquareActivity::class.java.name)
|
||||
bundle.putString(KEY_ENTRANCE, entrance)
|
||||
bundle.putString(KEY_FORUM_NAME, forumName)
|
||||
bundle.putString(KEY_GAME_COLLECTION_TITLE, gameCollectionTitle)
|
||||
bundle.putString(KEY_GAME_COLLECTION_ID, gameCollectionId)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
|
||||
/**
|
||||
* 跳转至游戏单详情
|
||||
*/
|
||||
@JvmStatic
|
||||
fun directToGameCollectionDetail(context: Context, id: String, entrance: String? = null) {
|
||||
if (id.isEmpty()) return
|
||||
val bundle = Bundle()
|
||||
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
|
||||
bundle.putString(KEY_TO, GameCollectionDetailActivity::class.java.name)
|
||||
bundle.putString(KEY_GAME_COLLECTION_ID, id)
|
||||
jumpActivity(context, bundle)
|
||||
}
|
||||
}
|
||||
@ -94,6 +94,7 @@ public class EntranceUtils {
|
||||
public static final String HOST_GAME_RATING_DETAIL = "game_rating_detail";
|
||||
public static final String HOST_HELP_AND_FEEDBACK = "help_and_feedback";
|
||||
public static final String HOST_LAUNCH_SIMULATOR_GAME = "launch_simulator_game";
|
||||
public static final String HOST_GAME_COLLECTION_DETAIL = "game_collection_detail";
|
||||
public static final String KEY_DATA = "data";
|
||||
public static final String KEY_MESSAGE = "message";
|
||||
public static final String KEY_MESSAGE_ID = "message_id";
|
||||
@ -251,6 +252,11 @@ public class EntranceUtils {
|
||||
public static final String KEY_PARENT_TAG = "parent_tag";
|
||||
public static final String KEY_BLOCK_ID = "block_id";
|
||||
public static final String KEY_BLOCK_NAME = "block_name";
|
||||
public static final String KEY_INSERT_GAME_COLLECTION = "insert_game_collection";
|
||||
public static final String KEY_IS_FROM_SQUARE = "is_from_square";
|
||||
public static final String KEY_FORUM_NAME = "forum_name";//版块名称
|
||||
public static final String KEY_GAME_COLLECTION_TITLE = "game_collection_title";//游戏单标题
|
||||
public static final String KEY_GAME_COLLECTION_ID = "game_collection_id";//游戏单ID
|
||||
|
||||
public static void jumpActivity(Context context, Bundle bundle) {
|
||||
bundle.putBoolean(KEY_REQUIRE_REDIRECT, true);
|
||||
|
||||
@ -2,6 +2,7 @@ package com.gh.common.util
|
||||
|
||||
import android.content.Context
|
||||
import com.gh.common.constant.Config
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.entity.ErrorEntity
|
||||
@ -105,6 +106,9 @@ object ErrorHelper {
|
||||
|
||||
403057,
|
||||
403068 -> handleErrorWithCommentBannedDialog(context, errorEntity)
|
||||
403200,
|
||||
403201,
|
||||
403202 -> handleErrorWithGameCollectionBannedDialog(context, errorEntity)
|
||||
|
||||
403001 -> Utils.toast(context, "标签名称太长了")
|
||||
403002 -> Utils.toast(context, "已经被邀请了")
|
||||
@ -161,6 +165,30 @@ object ErrorHelper {
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleErrorWithGameCollectionBannedDialog(context: Context, errorEntity: ErrorEntity?) {
|
||||
val bannedType = if (errorEntity?.data?.alwaysBlock == true) {
|
||||
""
|
||||
} else {
|
||||
"(非永久)"
|
||||
}
|
||||
val dialogContext = DialogUtils.checkDialogContext(context)
|
||||
DialogHelper.showDialog(
|
||||
dialogContext,
|
||||
"提示",
|
||||
"你因违反《游戏单管理规范》,已被禁言$bannedType,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})",
|
||||
"去看看", "关闭", {
|
||||
dialogContext.startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
dialogContext,
|
||||
"游戏单管理规范",
|
||||
Constants.GAME_COLLECTION_RULE
|
||||
)
|
||||
)
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
|
||||
private fun handleErrorWithCommentBannedDialog(context: Context, errorEntity: ErrorEntity?) {
|
||||
val bannedType = if (errorEntity?.data?.alwaysBlock == true) {
|
||||
""
|
||||
@ -173,7 +201,13 @@ object ErrorHelper {
|
||||
"提示",
|
||||
"你因违反《光环助手评论规则》,已被禁言$bannedType,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})",
|
||||
"去看看", "关闭", {
|
||||
dialogContext.startActivity(WebActivity.getWebIntent(dialogContext, dialogContext.getString(R.string.comment_rules_title), dialogContext.getString(R.string.comment_rules_url)))
|
||||
dialogContext.startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
dialogContext,
|
||||
dialogContext.getString(R.string.comment_rules_title),
|
||||
dialogContext.getString(R.string.comment_rules_url)
|
||||
)
|
||||
)
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
@ -189,9 +223,15 @@ object ErrorHelper {
|
||||
DialogHelper.showDialog(
|
||||
dialogContext,
|
||||
"提示",
|
||||
"你因违反《问答版块规则》,已被禁言$bannedType,如有疑问,请联系客服(QQ:1562479331)",
|
||||
"你因违反《问答版块规则》,已被禁言$bannedType,如有疑问,请联系客服(QQ:${Config.getSettings()?.support?.qq})",
|
||||
"去看看", "关闭", {
|
||||
dialogContext.startActivity(WebActivity.getWebIntent(dialogContext, dialogContext.getString(R.string.community_rule_title), dialogContext.getString(R.string.community_rule_url)))
|
||||
dialogContext.startActivity(
|
||||
WebActivity.getWebIntent(
|
||||
dialogContext,
|
||||
dialogContext.getString(R.string.community_rule_title),
|
||||
dialogContext.getString(R.string.community_rule_url)
|
||||
)
|
||||
)
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
|
||||
@ -1615,4 +1615,169 @@ object NewLogUtils {
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//进入我的游戏单
|
||||
fun logEnterMyGameCollection() {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_self_location"
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//进入创建游戏单
|
||||
fun logEnterGameCollectionEdit(entrance: String) {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_create_location"
|
||||
"entrance" to entrance
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//进入游戏单广场
|
||||
fun logEnterGameCollectionSquare(entrance: String, forumName: String = "", title: String = "", id: String = "") {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_square"
|
||||
"entrance" to entrance
|
||||
"forum_name" to forumName
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//进入选择标签
|
||||
fun logEnterGameCollectionTag() {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_tag_location"
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//筛选游戏单标签
|
||||
fun logFilterGameCollectionTag(tagCategory: String, tagName: String) {
|
||||
val json = json {
|
||||
"event" to "filter_game_collect_tag"
|
||||
"filter_tag_category" to tagCategory
|
||||
"filter_tag_name" to tagName
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//点击安利墙卡片
|
||||
fun logClickGameCollectionAmway() {
|
||||
val json = json {
|
||||
"event" to "click_game_collect_recommend_card"
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//进入安利墙
|
||||
fun logEnterGameCollectionAmway() {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_recommend_card"
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//点击卡片的游戏icon
|
||||
fun logClickGameCollectionGameIcon(title: String, id: String, gameName: String, gameId: String) {
|
||||
val json = json {
|
||||
"event" to "click_game_collect_recommend_card_icon"
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
"game_name" to gameName
|
||||
"game_id" to gameId
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//点击卡片的作者信息
|
||||
fun logClickGameCollectionAuthor(title: String, id: String) {
|
||||
val json = json {
|
||||
"event" to "click_game_collect_recommend_card_author"
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//点击进入游戏单
|
||||
fun logEnterGameCollectionDetail(title: String, id: String) {
|
||||
val json = json {
|
||||
"event" to "enter_game_collect_detail"
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//浏览游戏单/游戏单详情相关点击
|
||||
@JvmStatic
|
||||
fun logViewOrClickGameCollectionDetail(
|
||||
event: String,
|
||||
title: String,
|
||||
id: String,
|
||||
shareType: String = ""
|
||||
) {
|
||||
val json = json {
|
||||
"event" to event
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
if (shareType.isNotEmpty()) {
|
||||
"share_type" to shareType
|
||||
}
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
|
||||
//游戏单视频相关
|
||||
@JvmStatic
|
||||
fun logGameCollectionVideoEvent(
|
||||
event: String,
|
||||
title: String,
|
||||
id: String,
|
||||
progress: Double,
|
||||
playTime: Int,
|
||||
playAction: String = "",
|
||||
stopAction: String = "",
|
||||
) {
|
||||
val json = json {
|
||||
"event" to event
|
||||
"game_collect_title" to title
|
||||
"game_collect_id" to id
|
||||
"progress" to progress
|
||||
"play_time" to playTime
|
||||
if (playAction.isNotEmpty()) {
|
||||
"play_action" to playAction
|
||||
}
|
||||
if (stopAction.isNotEmpty()) {
|
||||
"stop_action" to stopAction
|
||||
}
|
||||
"timestamp" to System.currentTimeMillis() / 1000
|
||||
parseAndPutMeta().invoke(this)
|
||||
}
|
||||
log(json, "event", false)
|
||||
}
|
||||
}
|
||||
@ -55,4 +55,24 @@ public class PatternUtils {
|
||||
return newText;
|
||||
}
|
||||
|
||||
/**
|
||||
* 判断字符串中是否有连续2个以上的换行
|
||||
*/
|
||||
public static boolean isHasWrap(String text){
|
||||
String pattern = "[\\n]{2,}";
|
||||
Regex regex = new Regex(pattern);
|
||||
return regex.find(text, 0) != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* 替换字符串中连续2个以上的换行为一个换行
|
||||
*/
|
||||
public static String replaceWrap(String text) {
|
||||
String pattern = "[\\n]{2,}";
|
||||
String newText = text;
|
||||
if (isHasSpace(text)) {
|
||||
newText = text.replaceAll(pattern, "\n");
|
||||
}
|
||||
return newText;
|
||||
}
|
||||
}
|
||||
|
||||
@ -284,6 +284,29 @@ public class PostCommentUtils {
|
||||
});
|
||||
}
|
||||
|
||||
public static void reportGameCollectionComment(final Context context,
|
||||
final String gameCollectionId,
|
||||
final String commentId,
|
||||
final String reportData,
|
||||
final PostCommentListener listener) {
|
||||
RequestBody body = RequestBody.create(MediaType.parse("application/json"), reportData);
|
||||
RetrofitManager.getInstance(context).getApi()
|
||||
.reportGameCollectionComment(gameCollectionId, commentId, body)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(new Response<ResponseBody>() {
|
||||
@Override
|
||||
public void onResponse(ResponseBody response) {
|
||||
listener.postSuccess(null);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onFailure(HttpException e) {
|
||||
listener.postFailed(e);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public interface PostCommentListener {
|
||||
void postSuccess(JSONObject response);
|
||||
|
||||
|
||||
@ -87,7 +87,8 @@ public class ShareUtils {
|
||||
web("web链接"),
|
||||
userHome("个人主页"),
|
||||
qaDetail("QA内容详情"),
|
||||
inviteFriends("邀请好友");
|
||||
inviteFriends("邀请好友"),
|
||||
gameCollection("游戏单");
|
||||
|
||||
private String name;
|
||||
|
||||
@ -148,6 +149,19 @@ public class ShareUtils {
|
||||
ShareUtils.shareEntrance == ShareEntrance.communityArticle ||
|
||||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
|
||||
NewLogUtils.logShareResult(true);
|
||||
} else if (ShareUtils.shareEntrance == ShareEntrance.gameCollection) {
|
||||
String shareType;
|
||||
if (mShareType == ShareType.qq) {
|
||||
shareType = "QQ好友";
|
||||
} else {
|
||||
shareType = "QQ空间";
|
||||
}
|
||||
NewLogUtils.logViewOrClickGameCollectionDetail(
|
||||
"click_game_collect_detail_favorite_success",
|
||||
shareEntity.getShareTitle(),
|
||||
resourceId,
|
||||
shareType
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
26
app/src/main/java/com/gh/common/util/SingletonHolder.kt
Normal file
26
app/src/main/java/com/gh/common/util/SingletonHolder.kt
Normal file
@ -0,0 +1,26 @@
|
||||
package com.gh.common.util
|
||||
|
||||
open class SingletonHolder<out T>(creator: () -> T) {
|
||||
private var creator: (() -> T)? = creator
|
||||
|
||||
@Volatile
|
||||
private var instance: T? = null
|
||||
|
||||
fun getInstance(): T {
|
||||
val obj = instance
|
||||
if (obj != null) {
|
||||
return obj
|
||||
}
|
||||
return synchronized(this) {
|
||||
val obj1 = instance
|
||||
if (obj1 != null) {
|
||||
obj1
|
||||
} else {
|
||||
val created = creator!!()
|
||||
instance = created
|
||||
creator = null
|
||||
created
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
37
app/src/main/java/com/gh/common/view/StackRecyclerView.kt
Normal file
37
app/src/main/java/com/gh/common/view/StackRecyclerView.kt
Normal file
@ -0,0 +1,37 @@
|
||||
package com.gh.common.view
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.MotionEvent
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import kotlin.math.abs
|
||||
|
||||
class StackRecyclerView : RecyclerView {
|
||||
|
||||
private var posX: Float = 0.0F
|
||||
private var posY: Float = 0.0F
|
||||
|
||||
constructor(context: Context) : super(context)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet) : super(context, attrs)
|
||||
|
||||
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
|
||||
context,
|
||||
attrs,
|
||||
defStyleAttr
|
||||
)
|
||||
|
||||
override fun dispatchTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (ev.action == MotionEvent.ACTION_DOWN) {
|
||||
posX = ev.x
|
||||
posY = ev.y
|
||||
} else if (ev.action == MotionEvent.ACTION_MOVE) {
|
||||
if (posY - ev.y > abs(posX - ev.x) || ev.y - posY > abs(posX - ev.x)) {
|
||||
parent.requestDisallowInterceptTouchEvent(false)
|
||||
} else {
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
}
|
||||
return super.dispatchTouchEvent(ev)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,22 @@
|
||||
package com.gh.common.view.stacklayoutmanager
|
||||
|
||||
import android.view.View
|
||||
import com.gh.common.view.stacklayoutmanager.StackLayoutManager.ScrollOrientation
|
||||
|
||||
abstract class StackAnimation(scrollOrientation: ScrollOrientation, visibleCount: Int) {
|
||||
|
||||
protected val mScrollOrientation = scrollOrientation
|
||||
protected var mVisibleCount = visibleCount
|
||||
|
||||
internal fun setVisibleCount(visibleCount: Int) {
|
||||
mVisibleCount = visibleCount
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部回调,用来做动画.
|
||||
* @param firstMovePercent 第一个可视 item 移动的百分比,当即将完全移出屏幕的时候 firstMovePercent无限接近1.
|
||||
* @param itemView 当前的 itemView.
|
||||
* @param position 当前 itemView 对应的位置,position = 0 until visibleCount.
|
||||
*/
|
||||
abstract fun doAnimation(firstMovePercent: Float, itemView: View, position: Int)
|
||||
}
|
||||
@ -0,0 +1,34 @@
|
||||
package com.gh.common.view.stacklayoutmanager
|
||||
|
||||
import android.view.View
|
||||
|
||||
abstract class StackLayout(scrollOrientation: StackLayoutManager.ScrollOrientation,
|
||||
visibleCount: Int,
|
||||
perItemOffset: Int) {
|
||||
|
||||
protected val mScrollOrientation = scrollOrientation
|
||||
protected var mVisibleCount = visibleCount
|
||||
protected var mPerItemOffset = perItemOffset
|
||||
|
||||
internal fun setItemOffset(offset: Int) {
|
||||
mPerItemOffset = offset
|
||||
}
|
||||
|
||||
internal fun getItemOffset(): Int {
|
||||
return mPerItemOffset
|
||||
}
|
||||
|
||||
/**
|
||||
* 外部回调,用来做布局.
|
||||
* @param firstMovePercent 第一个可视 item 移动的百分比,当即将完全移出屏幕的时候 firstMovePercent无限接近1.
|
||||
* @param itemView 当前的 itemView.
|
||||
* @param position 当前 itemView 对应的位置,position = 0 until visibleCount.
|
||||
*/
|
||||
abstract fun doLayout(stackLayoutManager: StackLayoutManager,
|
||||
scrollOffset: Int,
|
||||
firstMovePercent: Float,
|
||||
itemView: View,
|
||||
position: Int)
|
||||
|
||||
abstract fun requestLayout()
|
||||
}
|
||||
@ -0,0 +1,544 @@
|
||||
package com.gh.common.view.stacklayoutmanager
|
||||
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.recyclerview.widget.RecyclerView.*
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.dip2px
|
||||
import com.lightgame.utils.Utils
|
||||
import kotlin.math.ceil
|
||||
import kotlin.math.floor
|
||||
|
||||
class StackLayoutManager(scrollOrientation: ScrollOrientation,
|
||||
visibleCount: Int,
|
||||
animation: Class<out StackAnimation>,
|
||||
layout: Class<out StackLayout>) : RecyclerView.LayoutManager() {
|
||||
private enum class FlingOrientation{NONE, LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP}
|
||||
|
||||
enum class ScrollOrientation{LEFT_TO_RIGHT, RIGHT_TO_LEFT, TOP_TO_BOTTOM, BOTTOM_TO_TOP}
|
||||
|
||||
private var mVisibleItemCount = visibleCount
|
||||
|
||||
private var mScrollOrientation = scrollOrientation
|
||||
|
||||
private var mScrollOffset: Int
|
||||
|
||||
var isFlinging = false
|
||||
|
||||
private lateinit var mOnScrollListener: OnScrollListener
|
||||
private lateinit var mOnFlingListener: OnFlingListener
|
||||
|
||||
//做动画的组件,支持自定义
|
||||
private var mAnimation: StackAnimation? = null
|
||||
//做布局的组件,支持自定义
|
||||
private var mLayout: StackLayout? = null
|
||||
|
||||
//是否是翻页效果
|
||||
private var mPagerMode = true
|
||||
|
||||
//触发翻页效果的最低 Fling速度
|
||||
private var mPagerFlingVelocity = 0
|
||||
|
||||
//标志当前滚动是否是调用scrollToCenter之后触发的滚动
|
||||
private var mFixScrolling = false
|
||||
|
||||
//fling的方向,用来判断是前翻还是后翻
|
||||
private var mFlingOrientation = FlingOrientation.NONE
|
||||
|
||||
//当前所处item对应的位置
|
||||
private var itemPosition = 0
|
||||
|
||||
//判断item位置是否发生了改变
|
||||
private var isItemPositionChanged = false
|
||||
|
||||
//item 位置发生改变的回调
|
||||
private var itemChangedListener: ItemChangedListener? = null
|
||||
|
||||
interface ItemChangedListener {
|
||||
fun onItemChanged(position: Int)
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置是否为ViewPager 式翻页模式.
|
||||
* <p>
|
||||
* 当设置为 true 的时候,可以配合[StackLayoutManager.setPagerFlingVelocity]设置触发翻页的最小速度.
|
||||
* @param isPagerMode 这个值默认是 false,当设置为 true 的时候,会有 viewPager 翻页效果.
|
||||
*/
|
||||
fun setPagerMode(isPagerMode: Boolean) {
|
||||
mPagerMode = isPagerMode
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前是否为ViewPager翻页模式.
|
||||
*/
|
||||
fun getPagerMode(): Boolean {
|
||||
return mPagerMode
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置触发ViewPager翻页效果的最小速度.
|
||||
* <p>
|
||||
* 该值仅在 [StackLayoutManager.getPagerMode] == true的时候有效.
|
||||
* @param velocity 默认值是2000.
|
||||
*/
|
||||
fun setPagerFlingVelocity(@androidx.annotation.IntRange(from = 0, to = Int.MAX_VALUE.toLong()) velocity: Int) {
|
||||
mPagerFlingVelocity = Int.MAX_VALUE.coerceAtMost(0.coerceAtLeast(velocity))
|
||||
}
|
||||
|
||||
/**
|
||||
* @return 当前触发翻页的最小 fling 速度.
|
||||
*/
|
||||
fun getPagerFlingVelocity(): Int {
|
||||
return mPagerFlingVelocity
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置recyclerView 静止时候可见的itemView 个数.
|
||||
* @param count 可见 itemView,默认为3
|
||||
*/
|
||||
fun setVisibleItemCount(@androidx.annotation.IntRange(from = 1, to = Long.MAX_VALUE)count: Int) {
|
||||
mVisibleItemCount = (itemCount - 1).coerceAtMost(1.coerceAtLeast(count))
|
||||
mAnimation?.setVisibleCount(mVisibleItemCount)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取recyclerView 静止时候可见的itemView 个数.
|
||||
* @return 静止时候可见的itemView 个数,默认为3.
|
||||
*/
|
||||
fun getVisibleItemCount(): Int {
|
||||
return mVisibleItemCount
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 item 偏移值,即第 i 个 item 相对于 第 i-1个 item 在水平方向的偏移值,默认是40px.
|
||||
* @param offset 每个 item 相对于前一个的偏移值.
|
||||
*/
|
||||
fun setItemOffset(offset: Int) {
|
||||
mLayout?.setItemOffset(offset)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取每个 item 相对于前一个的水平偏移值.
|
||||
* @return 每个 item 相对于前一个的水平偏移值.
|
||||
*/
|
||||
fun getItemOffset(): Int {
|
||||
return if (mLayout == null) {
|
||||
0
|
||||
} else {
|
||||
mLayout!!.getItemOffset()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置item 移动动画.
|
||||
* @param animation item 移动动画.
|
||||
*/
|
||||
fun setAnimation(animation: StackAnimation) {
|
||||
mAnimation = animation
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 item 移动动画.
|
||||
* @return item 移动动画.
|
||||
*/
|
||||
fun getAnimation(): StackAnimation? {
|
||||
return mAnimation
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取StackLayoutManager 的滚动方向.
|
||||
* @return StackLayoutManager 的滚动方向.
|
||||
*/
|
||||
fun getScrollOrientation(): ScrollOrientation {
|
||||
return mScrollOrientation
|
||||
}
|
||||
|
||||
/**
|
||||
* 返回第一个可见 itemView 的位置.
|
||||
* @return 返回第一个可见 itemView 的位置.
|
||||
*/
|
||||
fun getFirstVisibleItemPosition(): Int {
|
||||
if (width == 0 || height == 0) {
|
||||
return 0
|
||||
}
|
||||
return when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT -> floor((mScrollOffset * 1.0 / width)).toInt()
|
||||
ScrollOrientation.LEFT_TO_RIGHT -> itemCount - 1 - ceil((mScrollOffset * 1.0 / width)).toInt()
|
||||
ScrollOrientation.BOTTOM_TO_TOP -> floor((mScrollOffset * 1.0 / height)).toInt()
|
||||
ScrollOrientation.TOP_TO_BOTTOM -> itemCount - 1 - ceil((mScrollOffset * 1.0 / height)).toInt()
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 item 位置改变时触发的回调
|
||||
*/
|
||||
fun setItemChangedListener(listener: ItemChangedListener) {
|
||||
itemChangedListener = listener
|
||||
}
|
||||
|
||||
// constructor(scrollOrientation: ScrollOrientation) : this(scrollOrientation, 3, DefaultAnimation::class.java, DefaultLayout::class.java)
|
||||
//
|
||||
// constructor(scrollOrientation: ScrollOrientation, visibleCount: Int) : this(scrollOrientation, visibleCount, DefaultAnimation::class.java, DefaultLayout::class.java)
|
||||
|
||||
// constructor() : this(ScrollOrientation.RIGHT_TO_LEFT)
|
||||
|
||||
init {
|
||||
mScrollOffset = when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.BOTTOM_TO_TOP -> 0
|
||||
else -> Int.MAX_VALUE
|
||||
}
|
||||
|
||||
if (StackAnimation::class.java.isAssignableFrom(animation)) {
|
||||
try {
|
||||
val cla = animation.getDeclaredConstructor(ScrollOrientation::class.java, Int::class.javaPrimitiveType)
|
||||
mAnimation = cla.newInstance(scrollOrientation, visibleCount) as StackAnimation
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
if (StackLayout::class.java.isAssignableFrom(layout)) {
|
||||
try {
|
||||
val cla = layout.getDeclaredConstructor(ScrollOrientation::class.java, Int::class.javaPrimitiveType, Int::class.javaPrimitiveType)
|
||||
mLayout = cla.newInstance(scrollOrientation, visibleCount, 30) as StackLayout
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun generateDefaultLayoutParams(): LayoutParams {
|
||||
return LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT)
|
||||
}
|
||||
|
||||
override fun onAttachedToWindow(view: RecyclerView) {
|
||||
super.onAttachedToWindow(view)
|
||||
mOnFlingListener = object : OnFlingListener() {
|
||||
override fun onFling(velocityX: Int, velocityY: Int): Boolean {
|
||||
if (mPagerMode) {
|
||||
when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.LEFT_TO_RIGHT -> {
|
||||
mFlingOrientation = when {
|
||||
velocityX > mPagerFlingVelocity -> FlingOrientation.RIGHT_TO_LEFT
|
||||
velocityX < -mPagerFlingVelocity -> FlingOrientation.LEFT_TO_RIGHT
|
||||
else -> FlingOrientation.NONE
|
||||
}
|
||||
if (mScrollOffset in 1 until width * (itemCount - 1)) { //边界不需要滚动
|
||||
mFixScrolling = true
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
mFlingOrientation = when {
|
||||
velocityY > mPagerFlingVelocity -> FlingOrientation.BOTTOM_TO_TOP
|
||||
velocityY < -mPagerFlingVelocity -> FlingOrientation.TOP_TO_BOTTOM
|
||||
else -> FlingOrientation.NONE
|
||||
}
|
||||
if (mScrollOffset in 1 until width * (itemCount - 1)) { //边界不需要滚动
|
||||
mFixScrolling = true
|
||||
}
|
||||
}
|
||||
}
|
||||
calculateAndScrollToTarget(view)
|
||||
}
|
||||
else {
|
||||
Utils.log("$velocityX $velocityY")
|
||||
}
|
||||
return mPagerMode
|
||||
}
|
||||
}
|
||||
view.onFlingListener = mOnFlingListener
|
||||
|
||||
mOnScrollListener = object : OnScrollListener() {
|
||||
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
|
||||
if (newState == SCROLL_STATE_IDLE) {
|
||||
isFlinging = false
|
||||
if (!mFixScrolling) {
|
||||
mFixScrolling = true
|
||||
calculateAndScrollToTarget(view)
|
||||
} else {
|
||||
//表示此次 IDLE 是由修正位置结束触发的
|
||||
mFixScrolling = false
|
||||
}
|
||||
} else if (newState == SCROLL_STATE_DRAGGING) {
|
||||
mFixScrolling = false
|
||||
isFlinging = false
|
||||
} else if (newState == SCROLL_STATE_SETTLING) {
|
||||
isFlinging = true
|
||||
}
|
||||
}
|
||||
}
|
||||
view.addOnScrollListener(mOnScrollListener)
|
||||
}
|
||||
|
||||
override fun onDetachedFromWindow(view: RecyclerView?, recycler: RecyclerView.Recycler?) {
|
||||
super.onDetachedFromWindow(view, recycler)
|
||||
if (view?.onFlingListener == mOnFlingListener) {
|
||||
view.onFlingListener = null
|
||||
}
|
||||
view?.removeOnScrollListener(mOnScrollListener)
|
||||
}
|
||||
|
||||
override fun canScrollHorizontally(): Boolean {
|
||||
if (itemCount == 0) {
|
||||
return false
|
||||
}
|
||||
return when (mScrollOrientation) {
|
||||
ScrollOrientation.LEFT_TO_RIGHT, ScrollOrientation.RIGHT_TO_LEFT -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun canScrollVertically(): Boolean {
|
||||
if (itemCount == 0) {
|
||||
return false
|
||||
}
|
||||
return when (mScrollOrientation) {
|
||||
ScrollOrientation.TOP_TO_BOTTOM, ScrollOrientation.BOTTOM_TO_TOP -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLayoutChildren(recycler: RecyclerView.Recycler, state: State) {
|
||||
|
||||
mLayout?.requestLayout()
|
||||
|
||||
removeAndRecycleAllViews(recycler)
|
||||
|
||||
if (itemCount > 0) {
|
||||
mScrollOffset = getValidOffset(mScrollOffset)
|
||||
loadItemView(recycler)
|
||||
}
|
||||
}
|
||||
|
||||
override fun scrollHorizontallyBy(dx: Int, recycler: RecyclerView.Recycler, state: State): Int {
|
||||
return handleScrollBy(dx, recycler)
|
||||
}
|
||||
|
||||
override fun scrollVerticallyBy(dy: Int, recycler: RecyclerView.Recycler, state: State?): Int {
|
||||
return handleScrollBy(dy, recycler)
|
||||
}
|
||||
|
||||
override fun scrollToPosition(position: Int) {
|
||||
if (position < 0 || position >= itemCount) {
|
||||
throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]")
|
||||
}
|
||||
if (position < 0) {
|
||||
throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]")
|
||||
}
|
||||
mScrollOffset = getPositionOffset(position)
|
||||
requestLayout()
|
||||
}
|
||||
|
||||
override fun smoothScrollToPosition(recyclerView: RecyclerView, state: State?, position: Int) {
|
||||
if (position < 0 || position >= itemCount) {
|
||||
throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]")
|
||||
}
|
||||
if (position < 0) {
|
||||
throw ArrayIndexOutOfBoundsException("$position is out of bound [0..$itemCount-1]")
|
||||
}
|
||||
mFixScrolling = true
|
||||
scrollToCenter(position, recyclerView, true)
|
||||
}
|
||||
|
||||
private fun updatePositionRecordAndNotify(position: Int) {
|
||||
if (itemChangedListener == null) {
|
||||
return
|
||||
}
|
||||
if (position != itemPosition) {
|
||||
isItemPositionChanged = true
|
||||
itemPosition = position
|
||||
itemChangedListener?.onItemChanged(itemPosition)
|
||||
} else {
|
||||
isItemPositionChanged = false
|
||||
}
|
||||
}
|
||||
|
||||
private fun handleScrollBy(offset: Int, recycler: RecyclerView.Recycler): Int {
|
||||
//期望值,不得超过最大最小值,所以期望值不一定等于实际值
|
||||
val expectOffset = mScrollOffset + offset
|
||||
//实际值
|
||||
mScrollOffset = getValidOffset(expectOffset)
|
||||
|
||||
//实际偏移,超过最大最小值之后的偏移都应该是0,该值作为返回值,否则在极限位置进行滚动的时候不会出现弹性阴影
|
||||
val exactMove = mScrollOffset - expectOffset + offset
|
||||
|
||||
if (exactMove == 0) {
|
||||
//itemViews 位置都不会改变,直接 return
|
||||
return 0
|
||||
}
|
||||
|
||||
detachAndScrapAttachedViews(recycler)
|
||||
|
||||
loadItemView(recycler)
|
||||
|
||||
return offset
|
||||
}
|
||||
|
||||
private fun loadItemView(recycler: RecyclerView.Recycler) {
|
||||
val firstVisiblePosition = getFirstVisibleItemPosition()
|
||||
val lastVisiblePosition = getLastVisibleItemPosition()
|
||||
|
||||
//位移百分比
|
||||
val movePercent = getFirstVisibleItemMovePercent()
|
||||
|
||||
for (i in lastVisiblePosition downTo firstVisiblePosition) {
|
||||
val view = recycler.getViewForPosition(i)
|
||||
view.layoutParams = view.layoutParams.apply { width = DisplayUtils.getScreenWidth() - 50F.dip2px() }
|
||||
//添加到recycleView 中
|
||||
addView(view)
|
||||
//测量
|
||||
measureChild(view, 0, 0)
|
||||
//布局
|
||||
mLayout?.doLayout(this, mScrollOffset, movePercent, view, i - firstVisiblePosition)
|
||||
//做动画
|
||||
mAnimation?.doAnimation(movePercent, view, i - firstVisiblePosition)
|
||||
}
|
||||
|
||||
//尝试更新当前item的位置并通知外界
|
||||
updatePositionRecordAndNotify(firstVisiblePosition)
|
||||
|
||||
//重用
|
||||
if (firstVisiblePosition - 1 >= 0) {
|
||||
val view = recycler.getViewForPosition(firstVisiblePosition - 1)
|
||||
resetViewAnimateProperty(view)
|
||||
removeAndRecycleView(view, recycler)
|
||||
}
|
||||
if (lastVisiblePosition + 1 < itemCount) {
|
||||
val view = recycler.getViewForPosition(lastVisiblePosition + 1)
|
||||
resetViewAnimateProperty(view)
|
||||
removeAndRecycleView(view, recycler)
|
||||
}
|
||||
}
|
||||
|
||||
private fun resetViewAnimateProperty(view: View) {
|
||||
view.rotationY = 0f
|
||||
view.rotationX = 0f
|
||||
view.scaleX = 1f
|
||||
view.scaleY = 1f
|
||||
view.alpha = 1f
|
||||
}
|
||||
|
||||
private fun calculateAndScrollToTarget(view: RecyclerView) {
|
||||
val targetPosition = calculateCenterPosition(getFirstVisibleItemPosition())
|
||||
scrollToCenter(targetPosition, view, true)
|
||||
}
|
||||
|
||||
private fun scrollToCenter(targetPosition: Int, recyclerView: RecyclerView, animation: Boolean) {
|
||||
val targetOffset = getPositionOffset(targetPosition)
|
||||
when(mScrollOrientation) {
|
||||
ScrollOrientation.LEFT_TO_RIGHT, ScrollOrientation.RIGHT_TO_LEFT -> {
|
||||
if (animation) {
|
||||
recyclerView.smoothScrollBy(targetOffset - mScrollOffset, 0)
|
||||
} else {
|
||||
recyclerView.scrollBy(targetOffset - mScrollOffset, 0)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
if (animation) {
|
||||
recyclerView.smoothScrollBy(0, targetOffset - mScrollOffset)
|
||||
} else {
|
||||
recyclerView.scrollBy(0, targetOffset - mScrollOffset)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getValidOffset(expectOffset: Int): Int {
|
||||
val offset = (width * (itemCount - 1)).coerceAtMost(expectOffset).coerceAtLeast(0)
|
||||
return when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT, ScrollOrientation.LEFT_TO_RIGHT -> offset
|
||||
else -> (height * (itemCount - 1)).coerceAtMost(expectOffset).coerceAtLeast(0)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getPositionOffset(position: Int): Int {
|
||||
return when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT -> position * width
|
||||
ScrollOrientation.LEFT_TO_RIGHT -> (itemCount - 1 - position) * width
|
||||
ScrollOrientation.BOTTOM_TO_TOP -> position * height
|
||||
ScrollOrientation.TOP_TO_BOTTOM -> (itemCount - 1 - position) * height
|
||||
}
|
||||
}
|
||||
|
||||
private fun getLastVisibleItemPosition(): Int {
|
||||
val firstVisiblePosition = getFirstVisibleItemPosition()
|
||||
return if (firstVisiblePosition + mVisibleItemCount > itemCount - 1) {
|
||||
itemCount - 1
|
||||
} else {
|
||||
firstVisiblePosition + mVisibleItemCount
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFirstVisibleItemMovePercent(): Float {
|
||||
if (width == 0 || height == 0) {
|
||||
return 0f
|
||||
}
|
||||
return when (mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT -> (mScrollOffset % width) * 1.0f / width
|
||||
ScrollOrientation.LEFT_TO_RIGHT -> {
|
||||
val targetPercent = 1 - (mScrollOffset % width) * 1.0f / width
|
||||
return if (targetPercent == 1f) {
|
||||
0f
|
||||
} else {
|
||||
targetPercent
|
||||
}
|
||||
}
|
||||
ScrollOrientation.BOTTOM_TO_TOP -> (mScrollOffset % height) * 1.0f / height
|
||||
ScrollOrientation.TOP_TO_BOTTOM -> {
|
||||
val targetPercent = 1 - (mScrollOffset % height) * 1.0f / height
|
||||
return if (targetPercent == 1f) {
|
||||
0f
|
||||
} else {
|
||||
targetPercent
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun calculateCenterPosition(position: Int): Int {
|
||||
//当是 Fling 触发的时候
|
||||
val triggerOrientation = mFlingOrientation
|
||||
mFlingOrientation = FlingOrientation.NONE
|
||||
when(mScrollOrientation) {
|
||||
ScrollOrientation.RIGHT_TO_LEFT -> {
|
||||
if (triggerOrientation == FlingOrientation.RIGHT_TO_LEFT) {
|
||||
return position + 1
|
||||
} else if (triggerOrientation == FlingOrientation.LEFT_TO_RIGHT) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
ScrollOrientation.LEFT_TO_RIGHT -> {
|
||||
if (triggerOrientation == FlingOrientation.LEFT_TO_RIGHT) {
|
||||
return position + 1
|
||||
} else if (triggerOrientation == FlingOrientation.RIGHT_TO_LEFT) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
ScrollOrientation.BOTTOM_TO_TOP -> {
|
||||
if (triggerOrientation == FlingOrientation.BOTTOM_TO_TOP) {
|
||||
return position + 1
|
||||
} else if (triggerOrientation == FlingOrientation.TOP_TO_BOTTOM) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
ScrollOrientation.TOP_TO_BOTTOM -> {
|
||||
if (triggerOrientation == FlingOrientation.TOP_TO_BOTTOM) {
|
||||
return position + 1
|
||||
} else if (triggerOrientation == FlingOrientation.BOTTOM_TO_TOP) {
|
||||
return position
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//当不是 fling 触发的时候
|
||||
val percent = getFirstVisibleItemMovePercent()
|
||||
//向左移动超过50% position(firstVisibleItemPosition)++
|
||||
//否 position不变
|
||||
return if (percent < 0.5) {
|
||||
position
|
||||
} else {
|
||||
position + 1
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -8,6 +8,7 @@ import android.os.Bundle;
|
||||
import android.view.MenuItem;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import com.gh.base.ToolBarActivity;
|
||||
@ -20,6 +21,10 @@ import java.io.File;
|
||||
import java.lang.ref.SoftReference;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.core.view.OnApplyWindowInsetsListener;
|
||||
import androidx.core.view.ViewCompat;
|
||||
import androidx.core.view.WindowInsetsCompat;
|
||||
|
||||
import butterknife.BindView;
|
||||
|
||||
public class CropImageActivity extends ToolBarActivity {
|
||||
@ -37,11 +42,16 @@ public class CropImageActivity extends ToolBarActivity {
|
||||
|
||||
@NonNull
|
||||
public static Intent getIntent(Context context, String picturePath, float cropRatio, String entrance) {
|
||||
return getIntent(context, picturePath, cropRatio, true, entrance);
|
||||
}
|
||||
|
||||
@NonNull
|
||||
public static Intent getIntent(Context context, String picturePath, float cropRatio, boolean isBlackTheme, String entrance) {
|
||||
Intent intent = new Intent(context, CropImageActivity.class);
|
||||
intent.putExtra(EntranceUtils.KEY_PATH, picturePath);
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance);
|
||||
intent.putExtra(EntranceUtils.KEY_IMAGE_CROP_RATIO, cropRatio);
|
||||
intent.putExtra(EntranceUtils.KEY_BLACK_THEME, true);
|
||||
intent.putExtra(EntranceUtils.KEY_BLACK_THEME, isBlackTheme);
|
||||
return intent;
|
||||
}
|
||||
|
||||
@ -62,12 +72,13 @@ public class CropImageActivity extends ToolBarActivity {
|
||||
setToolbarMenu(R.menu.menu_positive);
|
||||
MenuItem menuItem = getMenuItem(R.id.layout_menu_positive);
|
||||
TextView menuButton = menuItem.getActionView().findViewById(R.id.menu_answer_post);
|
||||
menuButton.setTextColor(getResources().getColor(mBlackTheme ? R.color.theme_font : R.color.title));
|
||||
menuButton.setTextColor(getResources().getColor(R.color.theme_font));
|
||||
|
||||
float ratio = getIntent().getFloatExtra(EntranceUtils.KEY_IMAGE_CROP_RATIO, 1F);
|
||||
mCropImageCustom.setCropRatio(ratio);
|
||||
|
||||
DisplayUtils.transparentStatusBar(this);
|
||||
DisplayUtils.setLightStatusBar(this, !mBlackTheme);
|
||||
DisplayUtils.setStatusBarColor(this, R.color.transparent, !mBlackTheme);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -36,6 +36,7 @@ import static com.gh.common.util.EntranceUtils.HOST_COMMUNITY_COLUMN_DETAIL;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_COMMUNITY_QUESTION_LABEL_DETAIL;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_DOWNLOAD;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_GAME;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_GAME_COLLECTION_DETAIL;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_LIBAO;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_QQ;
|
||||
import static com.gh.common.util.EntranceUtils.HOST_QQ_GROUP;
|
||||
@ -390,6 +391,9 @@ public class SkipActivity extends BaseActivity {
|
||||
position = uri.getQueryParameter("position");
|
||||
DirectUtils.directToHelpAndFeedback(this, TextUtils.isEmpty(position) ? 0 : Integer.parseInt(position));
|
||||
break;
|
||||
case HOST_GAME_COLLECTION_DETAIL:
|
||||
DirectUtils.directToGameCollectionDetail(this, path, ENTRANCE_BROWSER);
|
||||
break;
|
||||
default:
|
||||
EntranceUtils.jumpActivity(this, new Bundle()); // 跳转至首页
|
||||
return;
|
||||
|
||||
@ -272,6 +272,13 @@ public class WeiBoShareActivity extends Activity implements WbShareCallback {
|
||||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.communityArticle ||
|
||||
ShareUtils.shareEntrance == ShareUtils.ShareEntrance.video) {
|
||||
NewLogUtils.logShareResult(true);
|
||||
} else if (ShareUtils.shareEntrance == ShareUtils.ShareEntrance.gameCollection) {
|
||||
NewLogUtils.logViewOrClickGameCollectionDetail(
|
||||
"click_game_collect_detail_favorite_success",
|
||||
ShareUtils.shareEntity.getShareTitle(),
|
||||
ShareUtils.resourceId,
|
||||
"新浪微博"
|
||||
);
|
||||
}
|
||||
} else {
|
||||
IntegralLogHelper.INSTANCE.logInviteResult("成功", "微博");
|
||||
|
||||
@ -0,0 +1,6 @@
|
||||
package com.gh.gamecenter.adapter.viewholder
|
||||
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.gamecenter.databinding.GameCollectionItemBinding
|
||||
|
||||
class GameCollectionItemViewHolder(var binding: GameCollectionItemBinding) : BaseRecyclerViewHolder<Any>(binding.root)
|
||||
@ -48,7 +48,8 @@ class GameHeadViewHolder(var binding: GameHeadItemBinding) :
|
||||
&& subject.data != null
|
||||
&& subject.data!!.size >= subject.more ?: 0
|
||||
&& subject.type != "column_collection"
|
||||
&& subject.type != "gallery_slide") {
|
||||
&& subject.type != "gallery_slide"
|
||||
&& subject.type != "game_list_collection") {
|
||||
binding.headMore.visibility = View.GONE
|
||||
} else if (subject.home == "hide"){
|
||||
binding.headMore.visibility = View.GONE
|
||||
|
||||
@ -3,8 +3,10 @@ package com.gh.gamecenter.collection;
|
||||
import android.os.Bundle;
|
||||
|
||||
import com.gh.base.fragment.BaseFragment_TabLayout;
|
||||
import com.gh.common.util.EntranceUtils;
|
||||
import com.gh.common.util.MtaHelper;
|
||||
import com.gh.gamecenter.R;
|
||||
import com.gh.gamecenter.manager.UserManager;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
@ -29,27 +31,41 @@ public class CollectionWrapperFragment extends BaseFragment_TabLayout {
|
||||
return fragment;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected int getLayoutId() {
|
||||
return R.layout.fragment_no_padding_tablayout_viewpager;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initTabTitleList(List<String> tabTitleList) {
|
||||
tabTitleList.add(getString(R.string.answer));
|
||||
tabTitleList.add(getString(R.string.collection_article));
|
||||
tabTitleList.add(getString(R.string.video));
|
||||
tabTitleList.add(getString(R.string.game_collection));
|
||||
tabTitleList.add(getString(R.string.collection_toolkit));
|
||||
tabTitleList.add(getString(R.string.collection_info));
|
||||
tabTitleList.add(getString(R.string.video));
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void initFragmentList(List<Fragment> fragments) {
|
||||
fragments.add(new AnswerFragment().with(getArguments()));
|
||||
fragments.add(new CommunityArticleFragment().with(getArguments()));
|
||||
fragments.add(new ToolsFragment().with(getArguments()));
|
||||
fragments.add(new ArticleFragment().with(getArguments()));
|
||||
|
||||
Bundle arguments = getArguments();
|
||||
if (arguments != null)
|
||||
arguments.putString("videoStyle", VideoFragment.VideoStyle.COLLECT.getValue());
|
||||
fragments.add(new VideoFragment().with(arguments));
|
||||
|
||||
Bundle gameCollectionArguments = getArguments();
|
||||
if (gameCollectionArguments != null) {
|
||||
gameCollectionArguments.putString(EntranceUtils.KEY_USER_ID, UserManager.getInstance().getUserId());
|
||||
gameCollectionArguments.putString(EntranceUtils.KEY_TYPE, GamesCollectionFragment.TYPE_COLLECT);
|
||||
}
|
||||
fragments.add(new GamesCollectionFragment().with(arguments));
|
||||
|
||||
fragments.add(new ToolsFragment().with(getArguments()));
|
||||
fragments.add(new ArticleFragment().with(getArguments()));
|
||||
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -0,0 +1,268 @@
|
||||
package com.gh.gamecenter.collection
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.graphics.drawable.ColorDrawable
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.PopupWindow
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.adapter.viewholder.GameCollectionItemViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.collection.GamesCollectionFragment.Companion.TYPE_USER
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
|
||||
class GamesCollectionAdapter(
|
||||
context: Context,
|
||||
private val mViewModel: GamesCollectionViewModel
|
||||
) : ListAdapter<GamesCollectionEntity>(context) {
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == itemCount - 1) {
|
||||
ItemViewType.ITEM_FOOTER
|
||||
} else {
|
||||
ItemViewType.ITEM_BODY
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ItemViewType.ITEM_BODY -> GameCollectionItemViewHolder(
|
||||
DataBindingUtil.inflate(
|
||||
mLayoutInflater,
|
||||
R.layout.game_collection_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
else -> FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is GameCollectionItemViewHolder -> {
|
||||
val itemEntity = mEntityList[position]
|
||||
holder.binding.run {
|
||||
entity = itemEntity
|
||||
executePendingBindings()
|
||||
|
||||
moreIv.goneIf(itemEntity.user?.id != UserManager.getInstance().userId)
|
||||
|
||||
when (itemEntity.stamp) {
|
||||
"special_choice" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_chosen)
|
||||
}
|
||||
"official" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_official)
|
||||
}
|
||||
}
|
||||
|
||||
when {
|
||||
mViewModel.mIsInsertGameCollection -> {
|
||||
userIcon.visibility = View.VISIBLE
|
||||
userName.visibility = View.VISIBLE
|
||||
timeTv.visibility = View.GONE
|
||||
myselfTag.visibility = View.GONE
|
||||
}
|
||||
|
||||
mViewModel.type == TYPE_USER && itemEntity.display == "self_only" -> {
|
||||
userIcon.visibility = View.GONE
|
||||
userName.visibility = View.GONE
|
||||
timeTv.visibility = View.GONE
|
||||
myselfTag.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
mViewModel.type == TYPE_USER && itemEntity.display != "self_only"-> {
|
||||
userIcon.visibility = View.GONE
|
||||
userName.visibility = View.GONE
|
||||
timeTv.visibility = View.VISIBLE
|
||||
myselfTag.visibility = View.GONE
|
||||
}
|
||||
|
||||
else -> {
|
||||
userIcon.visibility = View.VISIBLE
|
||||
userName.visibility = View.VISIBLE
|
||||
timeTv.visibility = View.GONE
|
||||
myselfTag.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
moreIv.setOnClickListener {
|
||||
showOptionPopupWindow(moreIv, itemEntity)
|
||||
}
|
||||
|
||||
myselfTag.setOnClickListener {
|
||||
DialogHelper.showDialog(mContext, "仅自己可见", "游戏单开启“仅自己可见”后,将不会对其他用户展示,您可以通过关闭仅自己可见或者投稿分享游戏单", "我知道了", "", {
|
||||
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
|
||||
gameOne.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(0)?.id, "")
|
||||
}
|
||||
gameTwo.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(1)?.id, "")
|
||||
}
|
||||
gameThree.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(mContext, itemEntity.games?.safelyGetInRelease(2)?.id, "")
|
||||
}
|
||||
|
||||
userIcon.setOnClickListener {
|
||||
val path = when (mViewModel.type) {
|
||||
GamesCollectionFragment.TYPE_COLLECT -> "我的收藏-游戏单"
|
||||
GamesCollectionFragment.TYPE_HISTORY -> "浏览记录-游戏单"
|
||||
GamesCollectionFragment.TYPE_USER -> "个人主页-游戏单"
|
||||
else -> ""
|
||||
}
|
||||
DirectUtils.directToHomeActivity(mContext, itemEntity.user?.id, "", path)
|
||||
}
|
||||
userName.setOnClickListener { userIcon.performClick() }
|
||||
|
||||
root.setOnClickListener {
|
||||
if (mViewModel.mIsInsertGameCollection) {
|
||||
val entity = GamesCollectionEntity().apply {
|
||||
id = itemEntity.id
|
||||
title = itemEntity.title
|
||||
intro = itemEntity.intro
|
||||
}
|
||||
val intent = Intent().apply {
|
||||
putExtra(GamesCollectionEntity::class.java.simpleName, entity)
|
||||
}
|
||||
(mContext as AppCompatActivity).setResult(Activity.RESULT_OK, intent)
|
||||
(mContext as AppCompatActivity).finish()
|
||||
} else {
|
||||
NewLogUtils.logEnterGameCollectionDetail(itemEntity.title, itemEntity.id)
|
||||
mContext.startActivity(GameCollectionDetailActivity.getIntent(mContext, itemEntity.id))
|
||||
}
|
||||
}
|
||||
|
||||
commentCountContainer.setOnClickListener {
|
||||
NewLogUtils.logEnterGameCollectionDetail(itemEntity.title, itemEntity.id)
|
||||
mContext.startActivity(
|
||||
GameCollectionDetailActivity.getIntent(
|
||||
mContext,
|
||||
itemEntity.id,
|
||||
isScrollToCommentArea = true
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
voteCountContainer.setOnClickListener {
|
||||
debounceActionWithInterval(R.id.vote_count_container, 1000) {
|
||||
mViewModel.postVoteGameCollection(itemEntity.id, !voteIcon.isChecked) {
|
||||
if (!voteIcon.isChecked) {
|
||||
voteCount.setTextColor(R.color.theme_font.toColor())
|
||||
voteIcon.isChecked = true
|
||||
voteIcon.visibility = View.GONE
|
||||
voteAnimation.visibility = View.VISIBLE
|
||||
voteAnimation.setAnimation("lottie/community_vote.json")
|
||||
voteAnimation.playAnimation()
|
||||
voteAnimation.doOnAnimationEnd {
|
||||
voteAnimation.visibility = View.GONE
|
||||
voteIcon.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
itemEntity.me?.vote = true
|
||||
itemEntity.count?.run {
|
||||
vote++
|
||||
voteCount.text = vote.toString()
|
||||
}
|
||||
} else {
|
||||
voteIcon.isChecked = false
|
||||
itemEntity.me?.vote = false
|
||||
itemEntity.count?.run {
|
||||
vote--
|
||||
if (vote < 0) vote = 0
|
||||
voteCount.setTextColor(R.color.text_999999.toColor())
|
||||
voteCount.text = vote.toString()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
is FooterViewHolder -> holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
}
|
||||
}
|
||||
|
||||
private fun showOptionPopupWindow(view: View, entity: GamesCollectionEntity) {
|
||||
val contentList = arrayListOf("编辑", "投稿", "删除")
|
||||
|
||||
val inflater = LayoutInflater.from(mContext)
|
||||
val layout = inflater.inflate(R.layout.layout_popup_container, null)
|
||||
val popupWindow = PopupWindow(
|
||||
layout,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT,
|
||||
LinearLayout.LayoutParams.WRAP_CONTENT
|
||||
)
|
||||
popupWindow.apply {
|
||||
setBackgroundDrawable(ColorDrawable(0))
|
||||
isTouchable = true
|
||||
isFocusable = true
|
||||
isOutsideTouchable = true
|
||||
}
|
||||
|
||||
val container = layout.findViewById<LinearLayout>(R.id.container)
|
||||
for (text in contentList) {
|
||||
val item = inflater.inflate(R.layout.layout_popup_option_item, container, false)
|
||||
container.addView(item)
|
||||
|
||||
val hitText = item.findViewById<TextView>(R.id.hint_text)
|
||||
hitText.text = text
|
||||
|
||||
item.setOnClickListener {
|
||||
dealOption(text, entity)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
}
|
||||
popupWindow.showAutoOrientation(view)
|
||||
}
|
||||
|
||||
private fun dealOption(content: String, entity: GamesCollectionEntity) {
|
||||
when (content) {
|
||||
"编辑" -> {
|
||||
mContext.startActivity(GameCollectionEditActivity.getIntent(mContext, entity))
|
||||
}
|
||||
|
||||
"投稿" -> {
|
||||
if ((entity.count?.game ?: 0) >= 8) {
|
||||
DialogHelper.showDialog(mContext, "温馨提示", "投稿通过后,将自动关闭“仅自己可见”,所有用户都能浏览到游戏单,确定投稿?", "确定", "取消", {
|
||||
mViewModel.publishGameCollection(entity)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
} else {
|
||||
DialogHelper.showDialog(mContext, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场哦~", "添加游戏", "我知道了", {
|
||||
mContext.startActivity(GameCollectionEditActivity.getIntent(mContext, entity))
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
|
||||
"删除" -> {
|
||||
DialogHelper.showDialog(mContext, "温馨提示", "游戏单删除后将无法恢复,是否确认删除", "确定", "取消", {
|
||||
mViewModel.deleteGameCollection(entity)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,80 @@
|
||||
package com.gh.gamecenter.collection
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.content.ContextCompat
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.EntranceUtils.*
|
||||
import com.gh.common.view.SpacingItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.baselist.ListFragment
|
||||
import com.gh.gamecenter.baselist.LoadType
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
|
||||
class GamesCollectionFragment : ListFragment<GamesCollectionEntity, GamesCollectionViewModel>() {
|
||||
|
||||
private var mUserId = ""
|
||||
private var mType = ""
|
||||
private var mIsInsertGameCollection = false
|
||||
private var mAdapter: GamesCollectionAdapter? = null
|
||||
|
||||
override fun provideListViewModel() = viewModelProvider<GamesCollectionViewModel>(
|
||||
GamesCollectionViewModel.Factory(mUserId, mType, mIsInsertGameCollection)
|
||||
)
|
||||
|
||||
override fun provideListAdapter() = mAdapter ?: GamesCollectionAdapter(requireContext(), mListViewModel).apply { mAdapter = this }
|
||||
|
||||
override fun getItemDecoration() = SpacingItemDecoration(notDecorateTheFirstItem = mType == TYPE_USER, top = 16F.dip2px())
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mUserId = arguments?.getString(KEY_USER_ID, "") ?: ""
|
||||
mType = arguments?.getString(KEY_TYPE, "") ?: ""
|
||||
mIsInsertGameCollection = arguments?.getBoolean(KEY_INSERT_GAME_COLLECTION, false) ?: false
|
||||
super.onCreate(savedInstanceState)
|
||||
mListRv?.run {
|
||||
overScrollMode = View.OVER_SCROLL_NEVER
|
||||
if (!mIsInsertGameCollection && mType == TYPE_USER) setBackgroundColor(R.color.white.toColor())
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mListViewModel.deleteLiveData.observe(viewLifecycleOwner) {
|
||||
val index = mAdapter?.entityList?.indexOf(it) ?: -1
|
||||
mAdapter?.entityList?.removeAt(index)
|
||||
if (mAdapter?.entityList?.isNullOrEmpty() == true) {
|
||||
onLoadEmpty()
|
||||
} else {
|
||||
mAdapter?.notifyItemRemoved(index)
|
||||
}
|
||||
|
||||
toast("删除成功")
|
||||
}
|
||||
|
||||
mListViewModel.publishLiveData.observe(viewLifecycleOwner) {
|
||||
mListViewModel.load(LoadType.REFRESH)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
super.onLoadEmpty()
|
||||
// RecyclerView 被隐藏的话会导致不能 AppBar 不能滑动
|
||||
if (mType == TYPE_USER) mListRv.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onLoadError() {
|
||||
super.onLoadError()
|
||||
if (mType == TYPE_USER) {
|
||||
mListRv.visibility = View.VISIBLE
|
||||
mReuseNoConn?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.background))
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TYPE_HISTORY = "history"
|
||||
const val TYPE_COLLECT = "collect"
|
||||
const val TYPE_USER = "user"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,128 @@
|
||||
package com.gh.gamecenter.collection
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.history.HistoryDatabase
|
||||
import com.gh.common.util.ErrorHelper
|
||||
import com.gh.common.util.ToastUtils
|
||||
import com.gh.common.util.observableToMain
|
||||
import com.gh.common.util.singleToMain
|
||||
import com.gh.gamecenter.baselist.ListViewModel
|
||||
import com.gh.gamecenter.collection.GamesCollectionFragment.Companion.TYPE_COLLECT
|
||||
import com.gh.gamecenter.collection.GamesCollectionFragment.Companion.TYPE_HISTORY
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Single
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
|
||||
class GamesCollectionViewModel(application: Application, var userId: String, var type: String, val mIsInsertGameCollection: Boolean = false) :
|
||||
ListViewModel<GamesCollectionEntity, GamesCollectionEntity>(application) {
|
||||
|
||||
private val mApi = RetrofitManager.getInstance(getApplication()).api
|
||||
val deleteLiveData = MutableLiveData<GamesCollectionEntity>()
|
||||
val publishLiveData = MutableLiveData<GamesCollectionEntity>()
|
||||
|
||||
override fun provideDataObservable(page: Int) = null
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<GamesCollectionEntity>> {
|
||||
return when (type) {
|
||||
TYPE_COLLECT -> mApi.getFavoriteGameCollectionList(userId)
|
||||
|
||||
TYPE_HISTORY -> {
|
||||
if (page > 5) {
|
||||
Single.create { it.onSuccess(arrayListOf()) }
|
||||
} else {
|
||||
HistoryDatabase.instance.gamesCollectionDao().getGamesCollectionWithOffset(20, (page - 1) * 20)
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
val map = if (mIsInsertGameCollection) {
|
||||
hashMapOf("filter" to "display")
|
||||
} else mapOf()
|
||||
mApi.getUserGameCollectionList(userId, map)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteGameCollection(entity: GamesCollectionEntity) {
|
||||
mApi.deleteGameCollection(entity.id)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
deleteLiveData.postValue(entity)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
ToastUtils.showToast("删除失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun publishGameCollection(entity: GamesCollectionEntity){
|
||||
mApi.deleteGameCollection(entity.id)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
publishLiveData.postValue(entity)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
ToastUtils.showToast("投稿失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun postVoteGameCollection(gameCollectionId: String, isVote: Boolean, successCallback: (() -> Unit)) {
|
||||
val single = if (isVote) {
|
||||
mApi.voteGameCollection(gameCollectionId)
|
||||
} else {
|
||||
mApi.unVoteGameCollection(gameCollectionId)
|
||||
}
|
||||
single.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
ToastUtils.toast(if (isVote) "点赞成功" else "取消点赞")
|
||||
successCallback.invoke()
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(
|
||||
getApplication(),
|
||||
exception.response()?.errorBody()?.string()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
class Factory(private val mUserId: String, private val mType: String, private val mIsInsertGameCollection: Boolean) :
|
||||
ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return GamesCollectionViewModel(HaloApp.getInstance().application, mUserId, mType, mIsInsertGameCollection) as T
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,45 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.qa.entity.Count
|
||||
import com.gh.gamecenter.qa.entity.TimeEntity
|
||||
import com.gh.gamecenter.room.converter.SimpleGameListConverter
|
||||
import com.gh.gamecenter.room.converter.TagInfoListConverter
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Entity
|
||||
@Parcelize
|
||||
class GameCollectionDraft(
|
||||
@Ignore
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
@PrimaryKey
|
||||
var primaryKey: String = "", // db key
|
||||
@TypeConverters(TagInfoListConverter::class)
|
||||
var tags: ArrayList<TagInfoEntity>? = null,
|
||||
@TypeConverters(SimpleGameListConverter::class)
|
||||
var games: ArrayList<SimpleGame>? = null,
|
||||
var title: String = "",
|
||||
var intro: String = "",
|
||||
var cover: String = "",
|
||||
var display: String = "",//self_only: 仅自己可见
|
||||
) : Parcelable {
|
||||
|
||||
fun convertGameCollectionEntity(): GamesCollectionEntity {
|
||||
val entity = GamesCollectionEntity()
|
||||
entity.id = id
|
||||
entity.tags = tags
|
||||
entity.games = games
|
||||
entity.title = title
|
||||
entity.intro = intro
|
||||
entity.cover = cover
|
||||
entity.display = display
|
||||
return entity
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,14 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class GameCollectionTagEntity(
|
||||
@SerializedName("_id")
|
||||
val categoryId: String = "",
|
||||
@SerializedName("name")
|
||||
val categoryName: String = "",
|
||||
val tags: List<TagInfoEntity> = ArrayList()
|
||||
) : Parcelable
|
||||
@ -252,6 +252,13 @@ data class GameEntity(
|
||||
@SerializedName("bbs_id")
|
||||
var bbsId: String = "",
|
||||
|
||||
//游戏单推荐分数
|
||||
@SerializedName("recommend_star")
|
||||
var recommendStar: Int = 5,
|
||||
//游戏单推荐理由
|
||||
@SerializedName("recommend_text")
|
||||
var recommendText: String = "",
|
||||
|
||||
// 本地字段,使用镜像信息
|
||||
var useMirrorInfo: Boolean = false,
|
||||
// 本地字段,曝光用
|
||||
@ -549,6 +556,20 @@ data class GameEntity(
|
||||
&& (useMirrorInfo || RegionSettingHelper.shouldThisGameDisplayMirrorInfo(id)))
|
||||
}
|
||||
|
||||
fun toSimpleGame(): SimpleGame {
|
||||
val simpleGame = SimpleGame()
|
||||
simpleGame.id = id
|
||||
simpleGame.mName = mName
|
||||
simpleGame.nameSuffix = nameSuffix
|
||||
simpleGame.mIcon = mIcon
|
||||
simpleGame.mRawIcon = mRawIcon
|
||||
simpleGame.active = active
|
||||
simpleGame.iconSubscript = iconSubscript
|
||||
simpleGame.recommendStar = recommendStar
|
||||
simpleGame.recommendText = recommendText
|
||||
return simpleGame
|
||||
}
|
||||
|
||||
fun clone(): GameEntity {
|
||||
val gameEntity = GameEntity()
|
||||
gameEntity.id = id
|
||||
@ -762,12 +783,22 @@ data class SimpleGame(
|
||||
@SerializedName("name_suffix")
|
||||
var nameSuffix: String? = null,
|
||||
@SerializedName("icon")
|
||||
private var mIcon: String? = null,
|
||||
var mIcon: String? = null,
|
||||
@SerializedName("ori_icon")
|
||||
private var mRawIcon: String? = null,
|
||||
var mRawIcon: String? = null,
|
||||
@SerializedName("icon_subscript")
|
||||
var iconSubscript: String? = null,
|
||||
var active: Boolean = false
|
||||
var active: Boolean = false,
|
||||
//游戏单推荐分数
|
||||
@SerializedName("recommend_star")
|
||||
var recommendStar: Int = 5,
|
||||
@SerializedName("mirror_status")
|
||||
var mirrorStatus: String? = "",
|
||||
@SerializedName("mirror_data")
|
||||
var mirrorData: SimpleGame? = null,
|
||||
//游戏单推荐理由
|
||||
@SerializedName("recommend_text")
|
||||
var recommendText: String = "",
|
||||
) : Parcelable {
|
||||
|
||||
@IgnoredOnParcel
|
||||
@ -788,6 +819,10 @@ data class SimpleGame(
|
||||
gameEntity.icon = mIcon
|
||||
gameEntity.rawIcon = mRawIcon
|
||||
gameEntity.iconSubscript = iconSubscript
|
||||
gameEntity.mirrorStatus = mirrorStatus
|
||||
gameEntity.mirrorData = mirrorData?.toGameEntity()
|
||||
gameEntity.recommendStar = recommendStar
|
||||
gameEntity.recommendText = recommendText
|
||||
return gameEntity
|
||||
}
|
||||
}
|
||||
|
||||
@ -0,0 +1,38 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.gh.gamecenter.qa.entity.Count
|
||||
import com.gh.gamecenter.qa.entity.TimeEntity
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
class GamesCollectionDetailEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
var tags: ArrayList<TagInfoEntity>? = null,
|
||||
var games: ArrayList<GameEntity>? = null,
|
||||
var title: String = "",
|
||||
var intro: String = "",
|
||||
var cover: String = "",
|
||||
var video: Video? = null,
|
||||
var display: String = "",//self_only: 仅自己可见
|
||||
var status: String = "",// draft/pending/pass/failed
|
||||
var time: TimeEntity? = null,
|
||||
var stamp: String = "",//special_choice: 精选 official: 官方
|
||||
var count: Count? = null,
|
||||
var user: UserEntity? = null,
|
||||
var me: MeEntity? = null,
|
||||
) : Parcelable {
|
||||
|
||||
@Parcelize
|
||||
data class Video(
|
||||
var active: Boolean = false,
|
||||
var format: String = "",
|
||||
var title: String = "",
|
||||
var size: Long = 0,
|
||||
var url: String = "",
|
||||
var poster: String = "",
|
||||
var time: Long = 0
|
||||
) : Parcelable
|
||||
}
|
||||
@ -0,0 +1,52 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import androidx.room.Entity
|
||||
import androidx.room.Ignore
|
||||
import androidx.room.PrimaryKey
|
||||
import androidx.room.TypeConverters
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.qa.entity.Count
|
||||
import com.gh.gamecenter.qa.entity.TimeEntity
|
||||
import com.gh.gamecenter.room.converter.*
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Entity
|
||||
@Parcelize
|
||||
class GamesCollectionEntity(
|
||||
@PrimaryKey
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
@TypeConverters(TagInfoListConverter::class)
|
||||
var tags: ArrayList<TagInfoEntity>? = null,
|
||||
@TypeConverters(SimpleGameListConverter::class)
|
||||
var games: ArrayList<SimpleGame>? = null,
|
||||
var title: String = "",
|
||||
var intro: String = "",
|
||||
var cover: String = "",
|
||||
var display: String = "",//self_only: 仅自己可见
|
||||
var stamp: String = "",//special_choice: 精选 offical: 官方
|
||||
@TypeConverters(CountConverter::class)
|
||||
var count: Count? = null,
|
||||
@TypeConverters(UserConverter::class)
|
||||
var user: User? = null,
|
||||
@TypeConverters(MeConverter::class)
|
||||
var me: MeEntity? = null,
|
||||
var orderTag: Long = 0,
|
||||
@Ignore
|
||||
var time: TimeEntity? = null,
|
||||
@Ignore
|
||||
var status: String = "",// draft/pending/pass/failed
|
||||
) : Parcelable {
|
||||
|
||||
fun getStatusLabelRes(): Int {
|
||||
return when {
|
||||
display == "self_only" && status == "draft" -> R.drawable.ic_game_collection_private
|
||||
status == "draft" -> R.drawable.ic_game_collection_draft
|
||||
status == "pending" -> R.drawable.ic_game_collection_pending
|
||||
status == "failed" -> R.drawable.ic_game_collection_fail
|
||||
else -> -1
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -13,6 +13,8 @@ data class HomeContent(
|
||||
val linkGame: GameEntity? = null,
|
||||
@SerializedName("link_column")
|
||||
val linkColumn: SubjectEntity? = null,
|
||||
@SerializedName("game_list")
|
||||
val linkGameCollection: List<GamesCollectionEntity>? = null,
|
||||
@SerializedName("link_top_game_comment")
|
||||
val linkTopGameComment: List<AmwayCommentEntity>? = null,
|
||||
@SerializedName("display_content")
|
||||
|
||||
@ -25,8 +25,8 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
@SerializedName("is_answer_opposed")
|
||||
var isAnswerOpposed: Boolean = false, // 是否已经踩过回答
|
||||
|
||||
@SerializedName(value = "is_answer_own", alternate = ["is_community_article_own", "is_question_own", "is_video_own"])
|
||||
var isContentOwner: Boolean = false, // 是否是当前内容(回答/社区文章/问题/视频)的拥有者
|
||||
@SerializedName(value = "is_answer_own", alternate = ["is_community_article_own", "is_question_own", "is_video_own", "is_game_list_own"])
|
||||
var isContentOwner: Boolean = false, // 是否是当前内容(回答/社区文章/问题/视频/游戏单)的拥有者
|
||||
|
||||
@SerializedName("is_answer_favorite")
|
||||
var isAnswerFavorite: Boolean = false,
|
||||
@ -62,7 +62,7 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
var isCommentOwner: Boolean = false, // 是否是当前评论的拥有者
|
||||
|
||||
@SyncPage(syncNames = [SyncFieldConstants.ARTICLE_COMMENT_VOTE])
|
||||
@SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted", "is_video_comment_voted", "is_community_article_comment_voted", "is_question_comment_voted"])
|
||||
@SerializedName("is_comment_voted", alternate = ["is_answer_comment_voted", "is_video_comment_voted", "is_community_article_comment_voted", "is_question_comment_voted", "is_game_list_voted"])
|
||||
var isCommentVoted: Boolean = false, // 是否已经点赞过当前评论
|
||||
|
||||
@SerializedName("is_version_requested")
|
||||
@ -116,7 +116,9 @@ class MeEntity(@SerializedName("is_community_voted")
|
||||
var questionDraft: QuestionDraftEntity? = null,//问题详情可能返回草稿
|
||||
|
||||
@SerializedName("is_follow_bbs")
|
||||
var isFollowForum: Boolean = false
|
||||
var isFollowForum: Boolean = false,
|
||||
|
||||
var vote: Boolean = false
|
||||
) : Parcelable
|
||||
|
||||
@Parcelize
|
||||
|
||||
@ -24,6 +24,9 @@ class MessageEntity {
|
||||
|
||||
var answer: Answer = Answer()
|
||||
|
||||
@SerializedName("game_list")
|
||||
var gameList: GameList = GameList()
|
||||
|
||||
var type: String = ""
|
||||
|
||||
var read: Boolean = true // true:已读,false:未读
|
||||
@ -60,7 +63,7 @@ class MessageEntity {
|
||||
title = parcel.readString()
|
||||
thumb = parcel.readString()
|
||||
communityId = parcel.readString()
|
||||
images = parcel.createStringArrayList()?: arrayListOf()
|
||||
images = parcel.createStringArrayList() ?: arrayListOf()
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
@ -102,7 +105,7 @@ class MessageEntity {
|
||||
constructor(parcel: Parcel) : this() {
|
||||
id = parcel.readString()
|
||||
content = parcel.readString()
|
||||
images = parcel.createStringArrayList()?: arrayListOf()
|
||||
images = parcel.createStringArrayList() ?: arrayListOf()
|
||||
}
|
||||
|
||||
override fun writeToParcel(parcel: Parcel, flags: Int) {
|
||||
@ -170,7 +173,7 @@ class MessageEntity {
|
||||
@SerializedName("_id")
|
||||
var id: String? = null
|
||||
|
||||
@SerializedName("top_id")
|
||||
@SerializedName("top_id", alternate = ["parent_id"])
|
||||
var topId: String? = null
|
||||
|
||||
var content: String? = null
|
||||
@ -216,4 +219,11 @@ class MessageEntity {
|
||||
var urlComment: String? = null
|
||||
}
|
||||
|
||||
class GameList {
|
||||
@SerializedName("_id")
|
||||
var id: String = ""
|
||||
var cover: String = ""
|
||||
var title: String = ""
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ import com.gh.common.util.ShareUtils
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class ForumShareEntity(
|
||||
data class NormalShareEntity(
|
||||
var id: String = "",
|
||||
var shareUrl: String = "",
|
||||
var shareIcon: String = "",
|
||||
@ -40,6 +40,8 @@ data class PersonalEntity(
|
||||
@SerializedName("game_comment")
|
||||
val gameComment: Int = 0,
|
||||
val video: Int = 0,
|
||||
@SerializedName("game_list")
|
||||
val gameList: Int = 0,
|
||||
@SerializedName("today_visit")
|
||||
val todayVisit: Int? = 0) : Parcelable {
|
||||
|
||||
|
||||
@ -30,6 +30,9 @@ data class SubjectEntity(
|
||||
@SerializedName("common_collection_content")
|
||||
var commonCollectionList: MutableList<CommonCollectionContentEntity>? = null,
|
||||
|
||||
@SerializedName("game_list_collection")
|
||||
var gameListCollection: List<GamesCollectionEntity>? = null,
|
||||
|
||||
@SerializedName("show_name")
|
||||
var showName: Boolean = true, // 是否显示“专题名字”,true、false
|
||||
@SerializedName("show_suffix")
|
||||
|
||||
12
app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt
Normal file
12
app/src/main/java/com/gh/gamecenter/entity/TagInfoEntity.kt
Normal file
@ -0,0 +1,12 @@
|
||||
package com.gh.gamecenter.entity
|
||||
|
||||
import android.os.Parcelable
|
||||
import com.google.gson.annotations.SerializedName
|
||||
import kotlinx.android.parcel.Parcelize
|
||||
|
||||
@Parcelize
|
||||
data class TagInfoEntity(
|
||||
@SerializedName("_id")
|
||||
var id: String = "",
|
||||
var name: String = ""
|
||||
) : Parcelable
|
||||
@ -26,6 +26,7 @@ import com.gh.gamecenter.entity.SubjectRecommendEntity
|
||||
import com.gh.gamecenter.game.GameFragment
|
||||
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFragment
|
||||
import com.gh.gamecenter.game.commoncollection.detail.CommonCollectionDetailFragment
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareFragment
|
||||
import com.gh.gamecenter.home.HomeFragment
|
||||
import com.gh.gamecenter.servers.GameServersPublishFragment
|
||||
import com.gh.gamecenter.servers.GameServersTestFragment
|
||||
@ -384,7 +385,7 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
|
||||
|
||||
private fun generateFragments(tabList: ArrayList<SubjectRecommendEntity>): ArrayList<Fragment> {
|
||||
val fragmentList = arrayListOf<Fragment>()
|
||||
for (tab in tabList) {
|
||||
for ((index, tab) in tabList.withIndex()) {
|
||||
val fragment = when (tab.type) {
|
||||
"home" -> HomeFragment().with(Bundle())
|
||||
"top_game_comment" -> AmwayFragment().with(Bundle())
|
||||
@ -392,6 +393,11 @@ class HomeSearchToolWrapperFragment : SearchToolWrapperFragment() {
|
||||
putParcelable(EntranceUtils.KEY_BLOCK_DATA, tab)
|
||||
})
|
||||
"server" -> GameServersPublishFragment().with(Bundle())
|
||||
"game_list" -> GameCollectionSquareFragment().with(Bundle().apply {
|
||||
putString(EntranceUtils.KEY_ENTRANCE, "顶部tab")
|
||||
putInt(EntranceUtils.KEY_TAB_INDEX, index)
|
||||
putString(EntranceUtils.KEY_NAME, tab.name)
|
||||
})
|
||||
"column_test" -> GameServersTestFragment().with(Bundle().apply {
|
||||
putString(GameServersTestFragment.TEST_COLUMN_ID, tab.link)
|
||||
})
|
||||
|
||||
@ -49,6 +49,7 @@ import com.gh.gamecenter.game.vertical.GameVerticalAdapter
|
||||
import com.gh.gamecenter.game.vertical.GameVerticalSlideViewHolder
|
||||
import com.gh.gamecenter.game.vertical.OnPagerSnapScrollListener
|
||||
import com.gh.gamecenter.home.BlankDividerViewHolder
|
||||
import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder
|
||||
import com.gh.gamecenter.servers.GameServersActivity
|
||||
import com.gh.gamecenter.subject.SubjectActivity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
@ -102,6 +103,8 @@ class GameFragmentAdapter(
|
||||
if (itemData.gallerySlide != null) return ItemViewType.GALLERY_SLIDE
|
||||
if (itemData.blankDivider != null) return ItemViewType.BLANK_DIVIDER
|
||||
if (itemData.rankCollection != null) return ItemViewType.RANK_COLLECTION
|
||||
if (itemData.gameCollection != null) return ItemViewType.GAME_COLLECTION_ITEM
|
||||
|
||||
return ItemViewType.LOADING
|
||||
}
|
||||
|
||||
@ -182,6 +185,9 @@ class GameFragmentAdapter(
|
||||
ItemViewType.RANK_COLLECTION -> {
|
||||
RankCollectionViewHolder(RankCollectionListBinding.bind(mLayoutInflater.inflate(R.layout.rank_collection_list, parent, false)))
|
||||
}
|
||||
ItemViewType.GAME_COLLECTION_ITEM -> {
|
||||
HomeGameCollectionViewHolder(HomeGameCollectionItemBinding.bind(mLayoutInflater.inflate(R.layout.home_game_collection_item, parent, false)))
|
||||
}
|
||||
else -> GameItemViewHolder(GameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_item, parent, false)))
|
||||
}
|
||||
}
|
||||
@ -203,6 +209,7 @@ class GameFragmentAdapter(
|
||||
is BlankDividerViewHolder -> holder.bindView(mItemDataList[position].blankDivider!!)
|
||||
is CommonCollectionViewHolder -> bindCommonCollection(holder, position)
|
||||
is RankCollectionViewHolder -> bindRankCollection(holder, position)
|
||||
is HomeGameCollectionViewHolder -> bindGameCollection(holder, position)
|
||||
}
|
||||
}
|
||||
|
||||
@ -830,6 +837,9 @@ class GameFragmentAdapter(
|
||||
blockData?.name ?: ""
|
||||
)
|
||||
}
|
||||
"game_list_collection" -> {
|
||||
DirectUtils.directToGameCollectionSquare(mContext, "版块内容列表", column.name ?: "")
|
||||
}
|
||||
else -> {
|
||||
if (column.indexRightTopLink != null) {
|
||||
val link = column.indexRightTopLink!!
|
||||
@ -859,6 +869,45 @@ class GameFragmentAdapter(
|
||||
}
|
||||
}
|
||||
|
||||
private fun bindGameCollection(holder: HomeGameCollectionViewHolder, position: Int) {
|
||||
val gameItemData = mItemDataList[position]
|
||||
val gameCollectionItemDataList = gameItemData.gameCollection?: listOf()
|
||||
|
||||
val exposureList = arrayListOf<ExposureEvent>()
|
||||
for (gameCollectionItemData in gameCollectionItemDataList) {
|
||||
runOnIoThread(true) {
|
||||
val gameCollection = gameCollectionItemData.gameCollectionItem
|
||||
val gameCollectionSource = listOf(ExposureSource("游戏单", "${gameCollection?.title} + ${gameCollection?.id}"))
|
||||
val gameExposureList = arrayListOf<ExposureEvent>()
|
||||
gameCollection?.games?.get(0)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = position; sequence = gameCollectionItemData.gameStartPosition + 1 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollection?.games?.get(1)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = position; sequence = gameCollectionItemData.gameStartPosition + 2 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollection?.games?.get(2)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = position; sequence = gameCollectionItemData.gameStartPosition + 3 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollectionItemData.exposureEventList = gameExposureList
|
||||
exposureList.addAll(gameExposureList)
|
||||
}
|
||||
}
|
||||
gameItemData.exposureEventList = exposureList
|
||||
holder.bindGameCollectionList(gameCollectionItemDataList, "版块内容列表")
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mItemDataList.size > 0) mItemDataList.size + 1 else mItemDataList.size
|
||||
}
|
||||
|
||||
@ -14,6 +14,7 @@ import com.gh.gamecenter.baselist.LoadStatus
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.game.data.GameItemData
|
||||
import com.gh.gamecenter.game.data.GameSubjectData
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
import com.gh.gamecenter.home.BlankDividerViewHolder
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
@ -650,6 +651,23 @@ class GameViewModel(application: Application, var blockData: SubjectRecommendEnt
|
||||
}
|
||||
}
|
||||
|
||||
if (subjectEntity.type == "game_list_collection") {
|
||||
val gameCollectionItem = GameItemData()
|
||||
val itemDataList = arrayListOf<GameCollectionListItemData>().apply {
|
||||
if (!subjectEntity.gameListCollection.isNullOrEmpty()) {
|
||||
var position = 0
|
||||
for (item in subjectEntity.gameListCollection!!) {
|
||||
add(GameCollectionListItemData(gameCollectionItem = item, gameStartPosition = position))
|
||||
position += if (item.count?.game!! > 2) 3 else item.count?.game ?: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
gameCollectionItem.gameCollection = itemDataList
|
||||
appendAdditionalInfoToSubjectGame(subjectEntity, index)
|
||||
mItemDataListCache.add(gameCollectionItem)
|
||||
continue
|
||||
}
|
||||
|
||||
appendAdditionalInfoToSubjectGame(subjectEntity, index)
|
||||
}
|
||||
|
||||
|
||||
@ -5,6 +5,7 @@ import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.entity.LinkEntity
|
||||
import com.gh.gamecenter.entity.SubjectEntity
|
||||
import com.gh.gamecenter.entity.SubjectRecommendEntity
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
|
||||
class GameItemData {
|
||||
var game: GameEntity? = null
|
||||
@ -24,6 +25,8 @@ class GameItemData {
|
||||
|
||||
var rankCollection: SubjectEntity? = null
|
||||
|
||||
var gameCollection: List<GameCollectionListItemData>? = null
|
||||
|
||||
var blankDivider: Float? = null // 空白的空间补全item
|
||||
|
||||
var exposureEvent: ExposureEvent? = null
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
|
||||
class AddGamesActivity : NormalActivity() {
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, AddGamesActivity::class.java, AddGamesFragment::class.java)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context): Intent {
|
||||
return getTargetIntent(context, AddGamesActivity::class.java, AddGamesFragment::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,19 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import androidx.core.os.bundleOf
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.gh.base.fragment.BaseLazyTabFragment
|
||||
import com.gh.common.util.EntranceUtils
|
||||
|
||||
class AddGamesFragment : BaseLazyTabFragment() {
|
||||
|
||||
override fun initFragmentList(fragments: MutableList<Fragment>) {
|
||||
fragments.add(AddSearchGameFragment().apply { bundleOf(EntranceUtils.KEY_NAVIGATION_TITLE to "添加游戏") })
|
||||
fragments.add(AddUserPlayedGameFragment())
|
||||
}
|
||||
|
||||
override fun initTabTitleList(tabTitleList: MutableList<String>) {
|
||||
tabTitleList.add("搜索游戏")
|
||||
tabTitleList.add("玩过游戏")
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,44 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.ToastUtils
|
||||
import com.gh.common.util.toColor
|
||||
import com.gh.common.util.toDrawable
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.game.GameItemViewHolder
|
||||
import com.gh.gamecenter.qa.editor.GameAdapter
|
||||
|
||||
class AddSearchAndPlayedGameAdapter(context: Context, val mChooseGamesViewModel: ChooseGamesViewModel) : GameAdapter(context) {
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is GameItemViewHolder) {
|
||||
val entity = mEntityList[position]
|
||||
val isSelected = mChooseGamesViewModel.chooseGamesLiveData.value?.find { it.id == entity.id } != null
|
||||
holder.binding.game = entity
|
||||
holder.binding.downloadBtn.text = if (isSelected) "删除" else "添加"
|
||||
holder.binding.downloadBtn.background =
|
||||
if (isSelected) R.drawable.bg_shape_f5_radius_999.toDrawable() else R.drawable.download_button_normal_style.toDrawable()
|
||||
holder.binding.downloadBtn.setTextColor(if (isSelected) R.color.text_999999.toColor() else R.color.white.toColor())
|
||||
|
||||
holder.binding.downloadBtn.setOnClickListener {
|
||||
val chooseGameList = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
if (isSelected) {
|
||||
chooseGameList.remove(chooseGameList.find { it.id == entity.id })
|
||||
ToastUtils.showToast("游戏已移除")
|
||||
} else {
|
||||
if (chooseGameList.size >= 100) {
|
||||
ToastUtils.showToast("已添加游戏到达上限")
|
||||
return@setOnClickListener
|
||||
}
|
||||
chooseGameList.add(entity)
|
||||
ToastUtils.showToast("游戏已添加")
|
||||
}
|
||||
notifyItemChanged(position)
|
||||
mChooseGamesViewModel.chooseGamesLiveData.postValue(chooseGameList)
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,59 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.goneIf
|
||||
import com.gh.common.util.viewModelProvider
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.SuggestionActivity
|
||||
import com.gh.gamecenter.databinding.FragmentSearchGameBinding
|
||||
import com.gh.gamecenter.qa.editor.GameAdapter
|
||||
import com.gh.gamecenter.qa.editor.GameFragment
|
||||
import com.gh.gamecenter.suggest.SuggestType
|
||||
|
||||
class AddSearchGameFragment : GameFragment() {
|
||||
|
||||
private lateinit var mBinding: FragmentSearchGameBinding
|
||||
private lateinit var mChooseGamesViewModel: ChooseGamesViewModel
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
return FragmentSearchGameBinding.inflate(layoutInflater, null, false).apply {
|
||||
mBinding = this
|
||||
}.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setNavigationTitle("添加游戏")
|
||||
noneText.text = "没有找到相关游戏,换个搜索词试试?"
|
||||
searchEt.doOnTextChanged { text, _, _, _ ->
|
||||
mBinding.promptTv.goneIf(!text.isNullOrEmpty())
|
||||
}
|
||||
|
||||
mBinding.applyGameTv.setOnClickListener {
|
||||
SuggestionActivity.startSuggestionActivity(requireContext(), SuggestType.gameCollect, "【游戏单添加游戏】")
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun provideListAdapter(): GameAdapter? {
|
||||
if (mAdapter == null) {
|
||||
mAdapter = AddSearchAndPlayedGameAdapter(requireContext(), mChooseGamesViewModel)
|
||||
}
|
||||
return mAdapter
|
||||
}
|
||||
|
||||
override fun isAutoShowKeyboard(): Boolean = false
|
||||
}
|
||||
@ -0,0 +1,51 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.toColor
|
||||
import com.gh.common.util.viewModelProvider
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.baselist.ListFragment
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.mygame.PlayedGameViewModel
|
||||
import com.gh.gamecenter.qa.editor.GameAdapter
|
||||
|
||||
class AddUserPlayedGameFragment : ListFragment<GameEntity, PlayedGameViewModel>() {
|
||||
|
||||
private var mAdapter: GameAdapter? = null
|
||||
private lateinit var mViewModel: PlayedGameViewModel
|
||||
private lateinit var mChooseGamesViewModel: ChooseGamesViewModel
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.fragment_add_user_played_game
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory())
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mCachedView.setBackgroundColor(R.color.white.toColor())
|
||||
}
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<GameEntity> {
|
||||
return mAdapter ?: AddSearchAndPlayedGameAdapter(requireContext(), mChooseGamesViewModel).apply {
|
||||
mAdapter = this
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration? {
|
||||
return null
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): PlayedGameViewModel {
|
||||
val userId = arguments?.getString(EntranceUtils.KEY_USER_ID)
|
||||
?: UserManager.getInstance().userId
|
||||
mViewModel = viewModelProvider(PlayedGameViewModel.Factory(userId, true))
|
||||
return mViewModel
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,23 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
|
||||
class ChooseGamesActivity : NormalActivity() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setNavigationTitle("选择游戏")
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, ChooseGamesActivity::class.java, ChooseGamesFragment::class.java)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context): Intent {
|
||||
return getTargetIntent(context, ChooseGamesActivity::class.java, ChooseGamesFragment::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.view.MotionEvent
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.util.PatternUtils
|
||||
import com.gh.common.util.TextHelper
|
||||
import com.gh.common.util.consume
|
||||
import com.gh.common.util.toBinding
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.databinding.ItemChooseGamesBinding
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class ChooseGamesAdapter(context: Context, val dragListener: ItemDragListener) :
|
||||
ListAdapter<GameEntity>(context) {
|
||||
|
||||
|
||||
public override fun setListData(updateData: MutableList<GameEntity>?) {
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return ChooseGamesViewHolder(parent.toBinding())
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
|
||||
return oldItem == newItem
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: GameEntity?, newItem: GameEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
@SuppressLint("ClickableViewAccessibility")
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is ChooseGamesViewHolder) {
|
||||
val gameEntity = mEntityList[position]
|
||||
holder.binding.recommendReasonEt.run {
|
||||
doOnTextChanged { text, start, _, _ ->
|
||||
if(PatternUtils.isHasWrap(text.toString())){
|
||||
setText(PatternUtils.replaceWrap(text.toString()))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
if (PatternUtils.isHasSpace(text.toString())) {
|
||||
setText(PatternUtils.replaceSpace(text.toString()))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.binding.gameNameTv.text = gameEntity.name
|
||||
holder.binding.gameIcon.displayGameIcon(gameEntity)
|
||||
holder.binding.recommendReasonEt.setText(gameEntity.recommendText)
|
||||
holder.binding.ratingScore.rating = gameEntity.recommendStar.toFloat()
|
||||
holder.binding.recommendReasonEt.filters = arrayOf(TextHelper.getFilter(45, "最多输入45个字"))
|
||||
holder.binding.recommendReasonEt.doOnTextChanged { text, _, _, _ ->
|
||||
gameEntity.recommendText = text.toString()
|
||||
}
|
||||
holder.binding.ratingScore.setOnRatingChangeListener { _, rating ->
|
||||
gameEntity.recommendStar = rating.toInt()
|
||||
}
|
||||
holder.binding.deleteIv.setOnClickListener {
|
||||
dragListener.deleteItem(gameEntity)
|
||||
}
|
||||
holder.binding.dragView.setOnTouchListener { _, event ->
|
||||
consume {
|
||||
if (event.action == MotionEvent.ACTION_DOWN) {
|
||||
dragListener.startDragItem(holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
holder.binding.topView.setOnClickListener {
|
||||
dragListener.setToTop(holder)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int = mEntityList.size
|
||||
|
||||
class ChooseGamesViewHolder(val binding: ItemChooseGamesBinding) : BaseRecyclerViewHolder<GameEntity>(binding.root)
|
||||
|
||||
interface ItemDragListener {
|
||||
fun startDragItem(holder: RecyclerView.ViewHolder)
|
||||
fun setToTop(holder: RecyclerView.ViewHolder)
|
||||
fun deleteItem(entity: GameEntity)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,141 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.ItemTouchHelper
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.DialogHelper
|
||||
import com.gh.common.util.goneIf
|
||||
import com.gh.common.util.viewModelProvider
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.FragmentChooseGamesBinding
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
import java.lang.ref.WeakReference
|
||||
import java.util.*
|
||||
|
||||
class ChooseGamesFragment : NormalFragment(), ChooseGamesAdapter.ItemDragListener {
|
||||
|
||||
private lateinit var mBinding: FragmentChooseGamesBinding
|
||||
private lateinit var mViewModel: ChooseGamesViewModel
|
||||
private lateinit var mAdapter: ChooseGamesAdapter
|
||||
|
||||
private val mItemTouchCallback = CustomItemTouchHelper(this)
|
||||
private val mItemTouchHelper = ItemTouchHelper(mItemTouchCallback)
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
return FragmentChooseGamesBinding.inflate(layoutInflater, null, false).apply {
|
||||
mBinding = this
|
||||
}.root
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
initMenu(R.menu.menu_save)
|
||||
mViewModel = viewModelProvider(ChooseGamesViewModel.Factory())
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(menuItem: MenuItem?) {
|
||||
super.onMenuItemClick(menuItem)
|
||||
menuItem?.run {
|
||||
if (itemId == R.id.layout_menu_save) {
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mViewModel.chooseGamesLiveData.observe(viewLifecycleOwner) {
|
||||
mBinding.addGamesTv.goneIf(it.isNotEmpty())
|
||||
mBinding.gamesRv.goneIf(it.isEmpty())
|
||||
mAdapter.setListData(it)
|
||||
mBinding.gameCountTv.text = "已收录${it.size}款游戏"
|
||||
}
|
||||
mBinding.gamesRv.apply {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = ChooseGamesAdapter(requireContext(), this@ChooseGamesFragment).apply {
|
||||
mAdapter = this
|
||||
}
|
||||
mItemTouchHelper.attachToRecyclerView(this)
|
||||
}
|
||||
|
||||
mBinding.addGamesButton.setOnClickListener {
|
||||
requireContext().startActivity(AddGamesActivity.getIntent(requireContext()))
|
||||
}
|
||||
|
||||
mBinding.addGamesTv.setOnClickListener { mBinding.addGamesButton.performClick() }
|
||||
|
||||
}
|
||||
|
||||
override fun startDragItem(holder: RecyclerView.ViewHolder) {
|
||||
mItemTouchHelper.startDrag(holder)
|
||||
}
|
||||
|
||||
override fun setToTop(holder: RecyclerView.ViewHolder) {
|
||||
val holderPosition = holder.bindingAdapterPosition
|
||||
for (i in holderPosition downTo 1) {
|
||||
mAdapter.notifyItemMoved(i, i - 1)
|
||||
Collections.swap(mAdapter.entityList, i, i - 1)
|
||||
val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
Collections.swap(games, i, i - 1)
|
||||
}
|
||||
}
|
||||
|
||||
override fun deleteItem(entity: GameEntity) {
|
||||
val chooseGames = mViewModel.chooseGamesLiveData.value
|
||||
chooseGames?.remove(entity)
|
||||
mViewModel.chooseGamesLiveData.postValue(chooseGames)
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
if (games.isNotEmpty()) {
|
||||
DialogHelper.showDialog(requireContext(), "提示", "是否保存本次选择的游戏", "保存", "取消", {
|
||||
requireActivity().finish()
|
||||
}, {
|
||||
mViewModel.chooseGamesLiveData.postValue(arrayListOf())
|
||||
requireActivity().finish()
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
return true
|
||||
}
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
class CustomItemTouchHelper(val fragment: ChooseGamesFragment) : ItemTouchHelper.Callback() {
|
||||
|
||||
var fragmentReference: WeakReference<ChooseGamesFragment> = WeakReference(fragment)
|
||||
|
||||
override fun getMovementFlags(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder): Int {
|
||||
return makeMovementFlags(ItemTouchHelper.UP or ItemTouchHelper.DOWN, 0)
|
||||
}
|
||||
|
||||
override fun onMove(recyclerView: RecyclerView, viewHolder: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
val fragment = fragmentReference.get()
|
||||
fragment?.run {
|
||||
mAdapter.notifyItemMoved(viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
Collections.swap(mAdapter.entityList, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
val games = mViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
Collections.swap(games, viewHolder.bindingAdapterPosition, target.bindingAdapterPosition)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onSwiped(viewHolder: RecyclerView.ViewHolder, direction: Int) {
|
||||
|
||||
}
|
||||
|
||||
override fun canDropOver(recyclerView: RecyclerView, current: RecyclerView.ViewHolder, target: RecyclerView.ViewHolder): Boolean {
|
||||
return true
|
||||
}
|
||||
|
||||
override fun isLongPressDragEnabled(): Boolean {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,12 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.util.SingletonHolder
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
|
||||
class ChooseGamesRepository private constructor() {
|
||||
|
||||
val chooseGamesLiveData: MutableLiveData<ArrayList<GameEntity>> = MutableLiveData()
|
||||
|
||||
companion object : SingletonHolder<ChooseGamesRepository>(::ChooseGamesRepository)
|
||||
}
|
||||
@ -0,0 +1,21 @@
|
||||
package com.gh.gamecenter.gamecollection.choose
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class ChooseGamesViewModel(application: Application, repository: ChooseGamesRepository) : AndroidViewModel(application) {
|
||||
|
||||
val chooseGamesLiveData = repository.chooseGamesLiveData
|
||||
|
||||
class Factory : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel?> create(modelClass: Class<T>): T {
|
||||
return ChooseGamesViewModel(
|
||||
HaloApp.getInstance().application,
|
||||
ChooseGamesRepository.getInstance()
|
||||
) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,38 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class GameCollectionDetailActivity : NormalActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
hideToolbar(true)
|
||||
DisplayUtils.setStatusBarColor(this, R.color.transparent, false)
|
||||
}
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, GameCollectionDetailActivity::class.java, GameCollectionDetailFragment::class.java)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, gameCollectionId: String, isFromSquare: Boolean = false): Intent {
|
||||
return getIntent(context, gameCollectionId, isFromSquare, false)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, gameCollectionId: String, isFromSquare: Boolean = false, isScrollToCommentArea: Boolean = false): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putString(EntranceUtils.KEY_GAME_COLLECTION_ID, gameCollectionId)
|
||||
bundle.putBoolean(EntranceUtils.KEY_IS_FROM_SQUARE, isFromSquare)
|
||||
bundle.putBoolean(EntranceUtils.KEY_SCROLL_TO_COMMENT_AREA, isScrollToCommentArea)
|
||||
return getTargetIntent(context, GameCollectionDetailActivity::class.java, GameCollectionDetailFragment::class.java, bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,552 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.text.SpannableStringBuilder
|
||||
import android.text.TextUtils
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.recyclerview.widget.GridLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.exposure.ExposureEvent
|
||||
import com.gh.common.exposure.ExposureSource
|
||||
import com.gh.common.exposure.ExposureType
|
||||
import com.gh.common.exposure.IExposable
|
||||
import com.gh.common.syncpage.SyncDataEntity
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.common.syncpage.SyncPageRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.GridSpacingItemColorDecoration
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.GameCollectionGameItemBinding
|
||||
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
|
||||
import com.gh.gamecenter.entity.CommentEntity
|
||||
import com.gh.gamecenter.entity.GameEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.gamecollection.detail.conversation.GameCollectionCommentConversationAdapter
|
||||
import com.gh.gamecenter.qa.article.detail.CommentItemData
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity
|
||||
import com.gh.gamecenter.qa.comment.CommentPictureAdapter
|
||||
import com.gh.gamecenter.qa.comment.OnCommentOptionClickListener
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import java.util.ArrayList
|
||||
import java.util.HashMap
|
||||
|
||||
open class GameCollectionDetailAdapter(
|
||||
context: Context,
|
||||
private val type: AdapterType,
|
||||
private val mEntrance: String,
|
||||
val mViewModel: GameCollectionDetailViewModel,
|
||||
commentClosure: ((CommentEntity) -> Unit)? = null
|
||||
) : BaseCommentAdapter(context, mViewModel, type, mEntrance, commentClosure), IExposable {
|
||||
|
||||
val positionAndPackageMap = HashMap<String, Int>()
|
||||
private var mExposureEventArray: SparseArray<ExposureEvent>? = null
|
||||
|
||||
override fun setListData(updateData: MutableList<CommentItemData>?) {
|
||||
mExposureEventArray = SparseArray(updateData?.size ?: 0)
|
||||
// 记录游戏位置
|
||||
if (updateData != null) {
|
||||
for (i in 0 until updateData.size) {
|
||||
val gameEntity = updateData[i].game
|
||||
if (gameEntity != null) {
|
||||
var packages = gameEntity.id
|
||||
for (apkEntity in gameEntity.getApk()) {
|
||||
packages += apkEntity.packageName
|
||||
}
|
||||
gameEntity.sequence = i
|
||||
positionAndPackageMap[packages + i] = i
|
||||
}
|
||||
}
|
||||
}
|
||||
super.setListData(updateData)
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
val item = mEntityList[position]
|
||||
return when {
|
||||
item.game != null -> ITEM_GAME
|
||||
item.gameEmpty != null -> ITEM_GAME_EMPTY
|
||||
item.divider != null -> ITEM_DIVIDER
|
||||
else -> super.getItemViewType(position)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ITEM_GAME -> GameCollectionGameItemViewHolder(
|
||||
GameCollectionGameItemBinding.bind(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_game_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
ITEM_GAME_EMPTY -> GameCollectionGameEmptyViewHolder(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_detail_none_game_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
ITEM_DIVIDER -> GameCollectionDividerViewHolder(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_detail_divider_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
else -> super.onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is CommentFilterViewHolder -> {
|
||||
holder.bindView(gameCollection = true)
|
||||
}
|
||||
|
||||
is GameCollectionGameItemViewHolder -> {
|
||||
holder.bindView(mEntityList[position].game!!, position)
|
||||
}
|
||||
|
||||
is CommentItemViewHolder -> {
|
||||
bindComment(holder.binding, mEntityList[position].commentNormal!!) { deleteCommentEntity ->
|
||||
val findEntity =
|
||||
mEntityList.find { it.commentNormal != null && it.commentNormal?.id == deleteCommentEntity.id }
|
||||
val index = mEntityList.indexOf(findEntity)
|
||||
mEntityList.remove(findEntity)
|
||||
notifyItemRemoved(index)
|
||||
|
||||
//刷新 ITEM_FILTER
|
||||
mViewModel.commentCount -= 1
|
||||
if (this !is GameCollectionCommentConversationAdapter) {
|
||||
notifyItemChanged(mViewModel.filterPos)
|
||||
}
|
||||
|
||||
mEntityList[0].commentTop?.reply = (mEntityList[0].commentTop?.reply
|
||||
?: 0) - 1
|
||||
SyncPageRepository.postSyncData(
|
||||
SyncDataEntity(
|
||||
mEntityList[0].commentTop?.id
|
||||
?: "",
|
||||
SyncFieldConstants.ARTICLE_COMMENT_REPLY_COUNT,
|
||||
mEntityList[0].commentTop?.reply,
|
||||
checkFieldEntity = true
|
||||
)
|
||||
)
|
||||
//刷新评论详情头部
|
||||
// notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
|
||||
is CommentFooterViewHolder -> {
|
||||
holder.bindView(mIsLoading, mIsNetworkError, mIsOver, R.string.game_collection_load_over_hint)
|
||||
}
|
||||
|
||||
else -> super.onBindViewHolder(holder, position)
|
||||
}
|
||||
}
|
||||
|
||||
fun bindComment(
|
||||
binding: ItemArticleDetailCommentBinding,
|
||||
comment: CommentEntity,
|
||||
deleteCallBack: ((comment: CommentEntity) -> Unit)? = null
|
||||
) {
|
||||
bindNormalComment(binding, comment, deleteCallBack)
|
||||
binding.run {
|
||||
if (type == AdapterType.COMMENT) {
|
||||
// 游戏单详情页面用的样式
|
||||
updateSubComment(this, comment)
|
||||
|
||||
floorHintTv.visibility = View.INVISIBLE
|
||||
bottomDivider.visibility = View.VISIBLE
|
||||
|
||||
root.setOnClickListener {
|
||||
mContext.startActivity(
|
||||
CommentActivity.getGameCollectionCommentReplyIntent(
|
||||
mContext,
|
||||
mViewModel.gameCollectionId,
|
||||
comment.id ?: "",
|
||||
mViewModel.commentCount,
|
||||
comment
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
commentCountTv.setOnClickListener {
|
||||
mContext.startActivity(
|
||||
CommentActivity.getGameCollectionCommentDetailIntent(
|
||||
mContext,
|
||||
comment.id ?: "",
|
||||
mViewModel.gameCollectionId,
|
||||
false,
|
||||
mEntrance,
|
||||
"游戏单详情"
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
contentTv.text = comment.content
|
||||
|
||||
commentCountTv.text = mViewModel.getCommentText(comment.reply, "回复")
|
||||
} else {
|
||||
// 评论详情用的样式
|
||||
floorHintTv.text = CommentUtils.getCommentTime(comment.time)
|
||||
commentCountTv.setOnClickListener { commentClosure?.invoke(comment) }
|
||||
commentCountTv.setCompoundDrawables(null, null, null, null)
|
||||
commentCountTv.text = "回复"
|
||||
likeCountTv.text = mViewModel.getLikeText(comment.vote, "")
|
||||
root.setOnClickListener { binding.commentCountTv.performClick() }
|
||||
|
||||
if (comment.parentUser != null && !TextUtils.isEmpty(comment.parentUser!!.id) ) {
|
||||
val prefix = "回复"
|
||||
val colon = " :"
|
||||
val parentUserName = " ${comment.parentUser?.name} "
|
||||
|
||||
val prefixSpan = SpanBuilder(prefix).color(
|
||||
binding.root.context,
|
||||
0,
|
||||
prefix.length,
|
||||
R.color.text_999999
|
||||
).build()
|
||||
val parentUserNameSpan = SpanBuilder(parentUserName)
|
||||
.bold(0, parentUserName.length)
|
||||
.click(0, parentUserName.length, R.color.text_666666) {
|
||||
DirectUtils.directToHomeActivity(
|
||||
binding.root.context,
|
||||
comment.user.id,
|
||||
1,
|
||||
mEntrance,
|
||||
PATH_ARTICLE_DETAIL_COMMENT
|
||||
)
|
||||
MtaHelper.onEvent("帖子详情", "引用回复区域", "用户名字")
|
||||
}.build()
|
||||
val colonSpan = SpanBuilder(colon).color(
|
||||
binding.root.context,
|
||||
0,
|
||||
colon.length,
|
||||
R.color.text_999999
|
||||
).build()
|
||||
val authorSpan = if (comment.parentUser?.me?.isCommentOwner == true) {
|
||||
SpanBuilder("作者").image(0, "作者".length, R.drawable.ic_hint_author).build()
|
||||
} else {
|
||||
""
|
||||
}
|
||||
|
||||
contentTv.text = SpannableStringBuilder()
|
||||
.append(prefixSpan)
|
||||
.append(parentUserNameSpan)
|
||||
.append(authorSpan)
|
||||
.append(colonSpan)
|
||||
.append(comment.content)
|
||||
} else {
|
||||
contentTv.text = comment.content
|
||||
}
|
||||
contentTv.maxLines = Int.MAX_VALUE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bindNormalComment(
|
||||
binding: ItemArticleDetailCommentBinding,
|
||||
comment: CommentEntity,
|
||||
deleteCallBack: ((comment: CommentEntity) -> Unit)?
|
||||
) {
|
||||
binding.run {
|
||||
if (!comment.images.isNullOrEmpty()) {
|
||||
if (commentPictureRv.adapter == null) {
|
||||
commentPictureRv.apply {
|
||||
layoutManager = GridLayoutManager(context, 3)
|
||||
adapter = CommentPictureAdapter(context, comment.images!!, "")
|
||||
if (itemDecorationCount == 0) {
|
||||
addItemDecoration(GridSpacingItemColorDecoration(context, 2, R.color.white))
|
||||
}
|
||||
visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
(commentPictureRv.adapter as CommentPictureAdapter).checkResetData(comment.images!!)
|
||||
}
|
||||
} else {
|
||||
binding.commentPictureRv.visibility = View.GONE
|
||||
}
|
||||
this.comment = comment
|
||||
contentTv.setExpandMaxLines(if (comment.isExpand) Int.MAX_VALUE else 4)
|
||||
contentTv.setIsExpanded(comment.isExpand)
|
||||
collapseTv.goneIf(!comment.isExpand)
|
||||
contentTv.setExpandCallback {
|
||||
comment.isExpand = true
|
||||
collapseTv.visibility = View.VISIBLE
|
||||
this.comment = comment
|
||||
}
|
||||
collapseTv.setOnClickListener {
|
||||
comment.isExpand = false
|
||||
collapseTv.visibility = View.GONE
|
||||
contentTv.setExpandMaxLines(4)
|
||||
contentTv.setIsExpanded(false)
|
||||
this.comment = comment
|
||||
}
|
||||
userIconIv.display(
|
||||
comment.user.border,
|
||||
comment.user.icon,
|
||||
comment.user.auth?.icon
|
||||
)
|
||||
|
||||
userIconIv.setOnClickListener {
|
||||
DirectUtils.directToHomeActivity(
|
||||
mContext,
|
||||
comment.user.id,
|
||||
"",
|
||||
"游戏单详情-评论"
|
||||
)
|
||||
}
|
||||
|
||||
userNameTv.setOnClickListener {
|
||||
DirectUtils.directToHomeActivity(
|
||||
mContext,
|
||||
comment.user.id,
|
||||
"",
|
||||
"游戏单详情-评论"
|
||||
)
|
||||
}
|
||||
|
||||
likeCountTv.text = mViewModel.getLikeText(comment.vote)
|
||||
|
||||
if (comment.me?.isCommentVoted == true) {
|
||||
likeCountTv.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.comment_vote_select,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
} else {
|
||||
likeCountTv.setCompoundDrawablesWithIntrinsicBounds(
|
||||
R.drawable.comment_vote_unselect,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
}
|
||||
|
||||
likeCountTv.setDebouncedClickListener {
|
||||
likeCountTv.context.ifLogin("游戏单详情-评论-点赞") {
|
||||
mViewModel.postVoteGameCollectionComment(comment) {
|
||||
likeCountTv.setCompoundDrawablesWithIntrinsicBounds(
|
||||
if (comment.me?.isCommentVoted == true) R.drawable.comment_vote_select else R.drawable.comment_vote_unselect,
|
||||
0,
|
||||
0,
|
||||
0
|
||||
)
|
||||
likeCountTv.text = mViewModel.getLikeText(comment.vote)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
badgeTv.setOnClickListener {
|
||||
DialogUtils.showViewBadgeDialog(mContext, comment.user.badge) {
|
||||
DirectUtils.directToBadgeWall(
|
||||
mContext,
|
||||
comment.user.id,
|
||||
comment.user.name,
|
||||
comment.user.icon
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
badgeIv.setOnClickListener { badgeTv.performClick() }
|
||||
|
||||
moreIv.setOnClickListener {
|
||||
CommentHelper.showGameCollectionCommentOption(
|
||||
it,
|
||||
comment,
|
||||
object : OnCommentOptionClickListener {
|
||||
override fun onCommentOptionClick(entity: CommentEntity, option: String) {
|
||||
when (option) {
|
||||
"删除评论" -> {
|
||||
DialogHelper.showDialog(
|
||||
binding.moreIv.context,
|
||||
"提示",
|
||||
"删除评论后,评论下所有的回复都将被删除",
|
||||
"删除",
|
||||
"取消",
|
||||
{
|
||||
mViewModel.deleteComment(comment) {
|
||||
deleteCallBack?.invoke(comment)
|
||||
}
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateSubComment(
|
||||
binding: ItemArticleDetailCommentBinding,
|
||||
comment: CommentEntity,
|
||||
) {
|
||||
val subCommentList = comment.subCommentList
|
||||
val ownerUserId = mViewModel.ownerUserId
|
||||
binding.moreSubCommentBtn.goneIf(comment.reply < 3)
|
||||
binding.moreSubCommentBtn.text = "查看全部${comment.reply}条回复"
|
||||
binding.subCommentContainer.goneIf(subCommentList.isNullOrEmpty())
|
||||
binding.firstSubCommentTv.goneIf(subCommentList?.firstOrNull() == null)
|
||||
binding.secondSubCommentTv.goneIf(subCommentList?.secondOrNull() == null)
|
||||
binding.subCommentContainer.setRoundedColorBackground(R.color.text_F5F5F5, 5F)
|
||||
|
||||
subCommentList?.firstOrNull()?.let {
|
||||
binding.firstSubCommentTv.text = getSubCommentSpanned(
|
||||
it.user.name,
|
||||
if (it.user.id == ownerUserId) "作者" else "",
|
||||
it.content
|
||||
)
|
||||
}
|
||||
subCommentList?.secondOrNull()?.let {
|
||||
binding.secondSubCommentTv.text = getSubCommentSpanned(
|
||||
it.user.name,
|
||||
if (it.user.id == ownerUserId) "作者" else "",
|
||||
it.content
|
||||
)
|
||||
}
|
||||
binding.subCommentContainer.setOnClickListener {
|
||||
mContext.startActivity(
|
||||
CommentActivity.getGameCollectionCommentDetailIntent(
|
||||
mContext,
|
||||
comment.id ?: "",
|
||||
mViewModel.gameCollectionId,
|
||||
false,
|
||||
mEntrance,
|
||||
"游戏单详情"
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSubCommentSpanned(
|
||||
name: String?,
|
||||
author: String?,
|
||||
content: String?
|
||||
): SpannableStringBuilder {
|
||||
val finalAuthor = author ?: ""
|
||||
val finalName = "$name "
|
||||
val colon = " :"
|
||||
|
||||
val nameSpan =
|
||||
SpanBuilder(finalName).color(0, finalName.length, R.color.theme_font).build()
|
||||
val authorSpan = if (finalAuthor.isNotEmpty()) SpanBuilder(finalAuthor).image(
|
||||
0,
|
||||
finalAuthor.length,
|
||||
R.drawable.ic_hint_author
|
||||
).build() else ""
|
||||
val colonSpan = SpanBuilder(colon).color(0, colon.length, R.color.theme_font).build()
|
||||
return SpannableStringBuilder().append(nameSpan).append(authorSpan).append(colonSpan)
|
||||
.append(content)
|
||||
}
|
||||
|
||||
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].game != null) {
|
||||
mEntityList[position].game!!.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].game != null) {
|
||||
mEntityList[position].game!!.getEntryMap().remove(status.platform)
|
||||
notifyItemChanged(position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
inner class GameCollectionGameEmptyViewHolder(view: View): RecyclerView.ViewHolder(view)
|
||||
|
||||
inner class GameCollectionDividerViewHolder(view: View): RecyclerView.ViewHolder(view)
|
||||
|
||||
inner class GameCollectionGameItemViewHolder(val binding: GameCollectionGameItemBinding): RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bindView(gameEntity: GameEntity, position: Int) {
|
||||
binding.run {
|
||||
game = gameEntity
|
||||
executePendingBindings()
|
||||
|
||||
val exposureSources = ArrayList<ExposureSource>().apply {
|
||||
add(ExposureSource("首页搜索", "${mViewModel.gameCollectionTitle}+${mViewModel.gameCollectionId}"))
|
||||
}
|
||||
val exposureEvent = ExposureEvent.createEvent(
|
||||
gameEntity,
|
||||
exposureSources,
|
||||
null,
|
||||
ExposureType.EXPOSURE
|
||||
)
|
||||
mExposureEventArray!!.put(position, exposureEvent)
|
||||
|
||||
recommendTv.post {
|
||||
if (!TextUtils.isEmpty(gameEntity.recommendText)) {
|
||||
ConstraintSet().apply {
|
||||
clone(container)
|
||||
clear(recommendTv.id, ConstraintSet.BOTTOM)
|
||||
if (recommendTv.lineCount == 1) {
|
||||
recommendTv.setPadding(0, 0, 0, 0)
|
||||
connect(recommendTv.id, ConstraintSet.BOTTOM, recommendPref.id, ConstraintSet.BOTTOM)
|
||||
} else {
|
||||
recommendTv.setPadding(0, 0, 0, 8F.dip2px())
|
||||
connect(recommendTv.id, ConstraintSet.BOTTOM, container.id, ConstraintSet.BOTTOM)
|
||||
}
|
||||
applyTo(container)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
root.setOnClickListener {
|
||||
GameDetailActivity.startGameDetailActivity(
|
||||
mContext,
|
||||
gameEntity.id,
|
||||
mEntrance,
|
||||
exposureEvent
|
||||
)
|
||||
}
|
||||
|
||||
DownloadItemUtils.setOnClickListener(
|
||||
mContext,
|
||||
binding.downloadBtn,
|
||||
gameEntity,
|
||||
position,
|
||||
this@GameCollectionDetailAdapter,
|
||||
mEntrance,
|
||||
"游戏单详情-游戏列表:${gameEntity.name}",
|
||||
exposureEvent)
|
||||
|
||||
DownloadItemUtils.updateDownloadButton(mContext, downloadBtn, gameEntity, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getEventByPosition(pos: Int): ExposureEvent? = mExposureEventArray!!.get(pos)
|
||||
|
||||
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? = null
|
||||
|
||||
companion object {
|
||||
const val ITEM_GAME = 900
|
||||
const val ITEM_GAME_EMPTY = 901
|
||||
const val ITEM_DIVIDER = 902
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,883 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.graphics.Color
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.annotation.ColorRes
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.history.HistoryHelper
|
||||
import com.gh.common.util.*
|
||||
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.baselist.ListFragment
|
||||
import com.gh.gamecenter.baselist.LoadType
|
||||
import com.gh.gamecenter.databinding.FragmentGameCollectionDetailBinding
|
||||
import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.eventbus.EBShare
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.gamedetail.GameDetailFragment
|
||||
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.article.detail.CommentItemData
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
|
||||
import com.gh.gamecenter.setting.VideoSettingFragment
|
||||
import com.gh.gamecenter.video.detail.CustomManager
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.lightgame.download.DataWatcher
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
|
||||
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import kotlinx.android.synthetic.main.item_article_detail_comment.view.*
|
||||
import kotlinx.android.synthetic.main.piece_game_detail_video.*
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import kotlin.math.abs
|
||||
|
||||
class GameCollectionDetailFragment :
|
||||
ListFragment<CommentItemData, GameCollectionDetailViewModel>() {
|
||||
|
||||
private var mBinding: FragmentGameCollectionDetailBinding? = null
|
||||
private var mAdapter: GameCollectionDetailAdapter? = null
|
||||
private var mEntity: GamesCollectionDetailEntity? = null
|
||||
private var mGameCollectionId = ""
|
||||
private var mGameCollectionTitle = ""
|
||||
private var mScrollToCommentArea = false
|
||||
private var mFromSquare = false
|
||||
private var mIsLight = false
|
||||
private var mIsPauseTopVideo = false
|
||||
private var mOrientationUtils: OrientationUtils? = 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 getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
FragmentGameCollectionDetailBinding.inflate(layoutInflater).apply { mBinding = this }.root
|
||||
|
||||
override fun provideListViewModel() =
|
||||
viewModelProvider<GameCollectionDetailViewModel>(GameCollectionDetailViewModel.Factory(mGameCollectionId))
|
||||
|
||||
override fun provideListAdapter() =
|
||||
mAdapter ?: GameCollectionDetailAdapter(requireContext(), BaseCommentAdapter.AdapterType.COMMENT, mEntrance, mListViewModel).apply { mAdapter = this }
|
||||
|
||||
override fun getItemDecoration() = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mGameCollectionId = arguments?.getString(EntranceUtils.KEY_GAME_COLLECTION_ID) ?: ""
|
||||
mFromSquare = arguments?.getBoolean(EntranceUtils.KEY_IS_FROM_SQUARE, false) ?: false
|
||||
mScrollToCommentArea = arguments?.getBoolean(EntranceUtils.KEY_SCROLL_TO_COMMENT_AREA, false) ?: false
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
initView()
|
||||
initObserver()
|
||||
mListViewModel.getGameCollectionDetail()
|
||||
|
||||
mListRv.addOnScrollListener(ExposureListener(this, provideListAdapter()))
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
mBinding?.run {
|
||||
inputContainer.run {
|
||||
bottomShareGroup.visibility = View.VISIBLE
|
||||
replyTv.setBackgroundResource(R.drawable.button_round_f5f5f5)
|
||||
replyTv.text = "说点什么吧"
|
||||
}
|
||||
fixedTopFilterView.run {
|
||||
(root.layoutParams as ViewGroup.MarginLayoutParams).apply {
|
||||
topMargin = DisplayUtils.getStatusBarHeight(resources) + 48F.dip2px()
|
||||
root.layoutParams = this
|
||||
}
|
||||
filterLatestTv.setOnClickListener {
|
||||
getFilterVH()?.binding?.filterLatestTv?.performClick()
|
||||
updateFilterView()
|
||||
}
|
||||
filterOldestTv.setOnClickListener {
|
||||
getFilterVH()?.binding?.filterOldestTv?.performClick()
|
||||
updateFilterView()
|
||||
}
|
||||
commentHintTv.text = "游戏单评论"
|
||||
filterLatestTv.text = "最新"
|
||||
filterOldestTv.text = "最早"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initObserver() {
|
||||
mListViewModel.loadResultLiveData.observeNonNull(this) {
|
||||
when (it) {
|
||||
BaseCommentViewModel.LoadResult.SUCCESS -> {
|
||||
mEntity = mListViewModel.gameCollectionDetail
|
||||
mGameCollectionTitle = mListViewModel.gameCollectionDetail?.title ?: ""
|
||||
|
||||
postDelayedRunnable({
|
||||
tryCatchInRelease {
|
||||
logEvent("view_game_collect_detail")
|
||||
}
|
||||
}, 2000)
|
||||
|
||||
updateView()
|
||||
initViewListener()
|
||||
}
|
||||
|
||||
else -> {
|
||||
if (it == BaseCommentViewModel.LoadResult.DELETED) {
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mReuseNoData?.visibility = View.VISIBLE
|
||||
toast(R.string.content_delete_toast)
|
||||
} else {
|
||||
mReuseNoConn?.visibility = View.VISIBLE
|
||||
mReuseNoData?.visibility = View.GONE
|
||||
mReuseNoConn?.setOnClickListener {
|
||||
mListViewModel.getGameCollectionDetail()
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mBinding?.root?.setBackgroundColor(Color.WHITE)
|
||||
}
|
||||
}
|
||||
|
||||
DisplayUtils.setStatusBarColor(requireActivity(), R.color.white, true)
|
||||
mBinding?.toolbarContainer?.setBackgroundColor(Color.WHITE)
|
||||
mBinding?.backIv?.setImageResource(R.drawable.ic_bar_back)
|
||||
|
||||
mListLoading?.visibility = View.GONE
|
||||
mBinding?.inputContainer?.bottomContainer?.visibility = View.GONE
|
||||
mBinding?.bottomShadowView?.visibility = View.GONE
|
||||
mBinding?.root?.setBackgroundColor(Color.TRANSPARENT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mListViewModel.followLiveData.observeNonNull(this) {
|
||||
updateFollowView()
|
||||
}
|
||||
|
||||
mListViewModel.favoriteLiveData.observeNonNull(this) {
|
||||
updateStartView()
|
||||
}
|
||||
|
||||
mListViewModel.likeLiveData.observeNonNull(this) {
|
||||
updateLikeView()
|
||||
}
|
||||
|
||||
mListViewModel.shareLiveData.observeNonNull(this) {
|
||||
updateShareView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun initViewListener() {
|
||||
mBinding?.appbar?.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { appBarLayout, verticalOffset ->
|
||||
if (!isAdded) return@OnOffsetChangedListener
|
||||
|
||||
val absVerticalOffset = abs(verticalOffset)
|
||||
mListRefresh?.isEnabled = absVerticalOffset <= 2
|
||||
|
||||
mBinding?.run {
|
||||
val bgOffset = if (mListViewModel.displayTopVideo) {
|
||||
videoItem.player.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px()
|
||||
} else {
|
||||
imageItem.root.bottom - DisplayUtils.getStatusBarHeight(resources) - 48F.dip2px()
|
||||
}
|
||||
mIsLight = absVerticalOffset < bgOffset
|
||||
updateFollowView()
|
||||
updateToolbar()
|
||||
|
||||
if (mListViewModel.displayTopVideo) {
|
||||
if (absVerticalOffset == appBarLayout.totalScrollRange
|
||||
&& videoItem.player.currentState == GSYVideoView.CURRENT_STATE_PLAYING
|
||||
) {
|
||||
pauseVideo()
|
||||
mIsPauseTopVideo = true
|
||||
} else if (mIsPauseTopVideo
|
||||
&& absVerticalOffset == 0
|
||||
&& videoItem.player.currentState == GSYVideoView.CURRENT_STATE_PAUSE
|
||||
) {
|
||||
resumeVideo()
|
||||
mIsPauseTopVideo = false
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
mListRv?.addOnScrollListener(object : RecyclerView.OnScrollListener() {
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
val firstCompletelyVisiblePosition = mLayoutManager.findFirstCompletelyVisibleItemPosition()
|
||||
if (RecyclerView.NO_POSITION == firstCompletelyVisiblePosition) return
|
||||
|
||||
val filterView = mLayoutManager.findViewByPosition(mListViewModel.filterPos)
|
||||
|
||||
if (firstCompletelyVisiblePosition >= mListViewModel.filterPos + 1 && filterView == null) {
|
||||
mBinding?.fixedTopFilterView?.root?.visibility = View.VISIBLE
|
||||
updateFilterView()
|
||||
} else {
|
||||
filterView?.let {
|
||||
if (it.top <= 0 && mBinding?.fixedTopFilterView?.root?.visibility == View.GONE) {
|
||||
mBinding?.fixedTopFilterView?.root?.visibility = View.VISIBLE
|
||||
updateFilterView()
|
||||
} else if (it.top > 0 && mBinding?.fixedTopFilterView?.root?.visibility == View.VISIBLE) {
|
||||
mBinding?.fixedTopFilterView?.root?.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
private fun updateToolbar() {
|
||||
mBinding?.run {
|
||||
if (mIsLight) {
|
||||
DisplayUtils.setStatusBarColor(requireActivity(), R.color.transparent, false)
|
||||
toolbarContainer.setBackgroundColor(Color.TRANSPARENT)
|
||||
backIv.setImageResource(R.drawable.ic_bar_back_light)
|
||||
squareIv.setImageResource(R.drawable.ic_game_collection_square_light)
|
||||
toolbarUserContainer.visibility = View.GONE
|
||||
if (!mListViewModel.displayTopVideo) {
|
||||
toolbarLightUserContainer.visibility = View.VISIBLE
|
||||
}
|
||||
} else {
|
||||
DisplayUtils.setStatusBarColor(requireActivity(), R.color.white, true)
|
||||
toolbarContainer.setBackgroundColor(Color.WHITE)
|
||||
backIv.setImageResource(R.drawable.ic_bar_back)
|
||||
squareIv.setImageResource(R.drawable.ic_game_collection_square)
|
||||
toolbarUserContainer.visibility = View.VISIBLE
|
||||
if (!mListViewModel.displayTopVideo) {
|
||||
toolbarLightUserContainer.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateView() {
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mListLoading?.visibility = View.GONE
|
||||
|
||||
scrollToComment()
|
||||
|
||||
initTopView()
|
||||
updateFollowView()
|
||||
updateStartView()
|
||||
updateLikeView()
|
||||
updateShareView()
|
||||
|
||||
mReuseNoConn?.setOnClickListener {
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mListLoading?.visibility = View.VISIBLE
|
||||
mListViewModel.getGameCollectionDetail()
|
||||
}
|
||||
}
|
||||
|
||||
private fun scrollToComment() {
|
||||
if (mScrollToCommentArea) {
|
||||
val listener = object : ViewTreeObserver.OnGlobalLayoutListener {
|
||||
override fun onGlobalLayout() {
|
||||
mBinding?.appbar?.setExpanded(false)
|
||||
mLayoutManager?.scrollToPositionWithOffset(mListViewModel?.filterPos ?: 0, 0)
|
||||
if (mListViewModel?.filterPos ?: 0 >= mLayoutManager?.findFirstVisibleItemPosition() ?: 0
|
||||
&& mListViewModel?.filterPos ?: 0 <= mLayoutManager?.findLastVisibleItemPosition() ?: 0) {
|
||||
mListRv?.viewTreeObserver?.removeOnGlobalLayoutListener(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
mListRv?.viewTreeObserver?.addOnGlobalLayoutListener(listener)
|
||||
mListRv.postDelayed({
|
||||
tryCatchInRelease {
|
||||
mListRv?.viewTreeObserver?.removeOnGlobalLayoutListener(listener)
|
||||
}
|
||||
}, 2000)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initTopView() {
|
||||
mBinding?.run {
|
||||
mBinding?.run {
|
||||
entity = mEntity
|
||||
executePendingBindings()
|
||||
squareIv.goneIf(mFromSquare)
|
||||
}
|
||||
|
||||
if (!mListViewModel.displayTopVideo) {
|
||||
imageItem.root.visibility = View.VISIBLE
|
||||
videoItem.root.visibility = View.GONE
|
||||
initImageTypeView()
|
||||
} else {
|
||||
imageItem.root.visibility = View.GONE
|
||||
videoItem.root.visibility = View.VISIBLE
|
||||
initVideoTypeView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initImageTypeView() {
|
||||
mBinding?.imageItem?.run {
|
||||
mEntity?.run {
|
||||
entity = this
|
||||
executePendingBindings()
|
||||
|
||||
desTv.text = "游戏单简介:${intro}"
|
||||
|
||||
when (stamp) {
|
||||
"special_choice" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_chosen_big)
|
||||
}
|
||||
"official" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_official_big)
|
||||
}
|
||||
}
|
||||
|
||||
if (!UserManager.getInstance().isLoggedIn) {
|
||||
tags?.forEachIndexed { index, tag ->
|
||||
tagList.addView(
|
||||
getTagView(
|
||||
tag.name,
|
||||
index == tags!!.size - 1,
|
||||
R.color.white,
|
||||
R.color.white_alpha_20
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initVideoTypeView() {
|
||||
mBinding?.videoItem?.run {
|
||||
mEntity?.run {
|
||||
entity = this
|
||||
executePendingBindings()
|
||||
|
||||
desTv.text = "游戏单简介:${intro}"
|
||||
|
||||
when (stamp) {
|
||||
"special_choice" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_chosen_big)
|
||||
}
|
||||
"official" -> {
|
||||
tagIv.setBackgroundResource(R.drawable.ic_official_big)
|
||||
}
|
||||
}
|
||||
|
||||
if (!UserManager.getInstance().isLoggedIn) {
|
||||
tags?.forEachIndexed { index, tag ->
|
||||
tagList.addView(
|
||||
getTagView(
|
||||
tag.name,
|
||||
index == tags!!.size - 1,
|
||||
R.color.theme,
|
||||
R.color.theme_alpha_20
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
userIcon.display(user?.border, user?.icon, user?.auth?.icon)
|
||||
|
||||
setUpTopVideo(video!!)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTagView(
|
||||
content: String,
|
||||
isLast: Boolean,
|
||||
@ColorRes tvColorRes: Int,
|
||||
@ColorRes dividerColorRes: Int
|
||||
): View {
|
||||
return LayoutGameCollectionTagBinding.inflate(layoutInflater).apply {
|
||||
divider.goneIf(isLast)
|
||||
divider.setBackgroundColor(dividerColorRes.toColor())
|
||||
contentTv.text = content
|
||||
contentTv.setTextColor(tvColorRes.toColor())
|
||||
}.root
|
||||
}
|
||||
|
||||
private fun updateFollowView() {
|
||||
if (!mListViewModel.displayTopVideo) {
|
||||
updateImageItemFollowView()
|
||||
} else {
|
||||
updateVideoItemFollowView()
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateImageItemFollowView() {
|
||||
mBinding?.toolbarFollowTv?.run {
|
||||
val isFollow = mEntity?.me?.isFollower ?: false
|
||||
visibility = View.VISIBLE
|
||||
text = if (isFollow) "已关注" else "关注"
|
||||
if (mIsLight) {
|
||||
setBackgroundResource(if (isFollow) R.drawable.button_round_black_alpha_10 else R.drawable.button_round_black_alpha_30)
|
||||
setTextColor(if (isFollow) R.color.white_alpha_60.toColor() else R.color.white.toColor())
|
||||
} else {
|
||||
setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff)
|
||||
setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateVideoItemFollowView() {
|
||||
val isFollow = mEntity?.me?.isFollower ?: false
|
||||
mBinding?.videoItem?.videoItemFollowTv?.run {
|
||||
text = if (isFollow) "已关注" else "关注"
|
||||
setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff)
|
||||
setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor())
|
||||
}
|
||||
mBinding?.toolbarFollowTv?.run {
|
||||
if (mIsLight) {
|
||||
visibility = View.GONE
|
||||
} else {
|
||||
visibility = View.VISIBLE
|
||||
text = if (isFollow) "已关注" else "关注"
|
||||
setBackgroundResource(if (isFollow) R.drawable.button_round_f5f5f5 else R.drawable.button_round_1a2496ff)
|
||||
setTextColor(if (isFollow) R.color.text_999999.toColor() else R.color.theme.toColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateStartView() {
|
||||
mBinding?.inputContainer?.run {
|
||||
bottomStarTv.text = mListViewModel.getStarText()
|
||||
if (mEntity?.me?.isFavorite == true) {
|
||||
bottomStarIv.setImageResource(R.drawable.ic_article_detail_stared_bottom_bar)
|
||||
} else {
|
||||
bottomStarIv.setImageResource(R.drawable.ic_article_detail_star_bottom_bar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateLikeView() {
|
||||
mBinding?.inputContainer?.run {
|
||||
bottomLikeTv.text = mListViewModel.getLikeText(mEntity?.count?.vote ?: 0)
|
||||
if (mEntity?.me?.vote == true) {
|
||||
bottomLikeIv.setImageResource(R.drawable.ic_article_detail_liked_bottom_bar)
|
||||
} else {
|
||||
bottomLikeIv.setImageResource(R.drawable.ic_article_detail_like_bottom_bar)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateShareView() {
|
||||
mBinding?.inputContainer?.run {
|
||||
bottomShareTv.text = mListViewModel.getShareText()
|
||||
}
|
||||
}
|
||||
|
||||
private fun setUpTopVideo(video: GamesCollectionDetailEntity.Video) {
|
||||
mBinding?.videoItem?.player?.run {
|
||||
mOrientationUtils = OrientationUtils(requireActivity(), this)
|
||||
mOrientationUtils?.isEnable = false
|
||||
|
||||
GSYVideoOptionBuilder()
|
||||
.setIsTouchWigetFull(false)
|
||||
.setIsTouchWiget(false)
|
||||
.setRotateViewAuto(false)
|
||||
.setShowFullAnimation(false)
|
||||
.setSeekRatio(1f)
|
||||
.setUrl(video.url)
|
||||
.setCacheWithPlay(true)
|
||||
.setVideoAllCallBack(object : GSYSampleCallBack() {
|
||||
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
|
||||
mOrientationUtils?.backToProtVideo()
|
||||
logVideoEvent("video_game_collect_detail_full_cancel")
|
||||
}
|
||||
})
|
||||
.build(this)
|
||||
|
||||
gameCollectionId = mGameCollectionId
|
||||
gameCollectionTitle = mGameCollectionTitle
|
||||
viewModel = mListViewModel
|
||||
this.video = video
|
||||
updateThumb(video.poster)
|
||||
|
||||
//val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false)
|
||||
val videoOption = SPUtils.getString(
|
||||
Constants.SP_HOME_OR_DETAIL_VIDEO_OPTION,
|
||||
VideoSettingFragment.VIDEO_OPTION_WIFI
|
||||
)
|
||||
?: VideoSettingFragment.VIDEO_OPTION_WIFI
|
||||
when (videoOption) {
|
||||
VideoSettingFragment.VIDEO_OPTION_ALL -> {
|
||||
startPlayLogic(isAutoPlay = true)
|
||||
}
|
||||
|
||||
VideoSettingFragment.VIDEO_OPTION_WIFI -> {
|
||||
if (NetworkUtils.isWifiConnected(requireContext()) /*|| !trafficVideo*/) {
|
||||
if (mListViewModel.isTopVideoPartlyCached(video.url)) {
|
||||
startPlayLogic(isAutoPlay = true)
|
||||
} else {
|
||||
// 未有缓存时,为避免影响页面加载,延迟自动播放视频
|
||||
postDelayedRunnable({
|
||||
if (activity != null && activity?.isFinishing != true) {
|
||||
startPlayLogic(isAutoPlay = true)
|
||||
}
|
||||
}, GameDetailFragment.INITIAL_DELAY)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
}
|
||||
}
|
||||
|
||||
fullscreenButton.setOnClickListener {
|
||||
val horizontalVideoView =
|
||||
startWindowFullscreen(requireContext(), true, true) as? GameCollectionVideoView
|
||||
if (horizontalVideoView == null) {
|
||||
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
|
||||
return@setOnClickListener
|
||||
}
|
||||
mOrientationUtils?.resolveByClick()
|
||||
horizontalVideoView.uuid = uuid
|
||||
horizontalVideoView.viewModel = mListViewModel
|
||||
horizontalVideoView.video = video
|
||||
horizontalVideoView.updateThumb(video.poster)
|
||||
horizontalVideoView.violenceUpdateMuteStatus()
|
||||
logVideoEvent("video_game_collect_detail_full")
|
||||
}
|
||||
|
||||
observeVolume(this@GameCollectionDetailFragment)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onLoadRefresh() {
|
||||
// showSkeleton(true)
|
||||
}
|
||||
|
||||
override fun onLoadDone() {
|
||||
// showSkeleton(false)
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mReuseNoData?.visibility = View.GONE
|
||||
mListLoading?.visibility = View.GONE
|
||||
|
||||
mListRv.visibility = View.VISIBLE
|
||||
hideRefreshingLayout()
|
||||
|
||||
mListRv.postDelayed({
|
||||
if (provideListAdapter().itemCount < theNumberNeededToFillAScreen()) {
|
||||
autoLoadMore()
|
||||
}
|
||||
}, autoLoadMoreDelay.toLong())
|
||||
}
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
// showSkeleton(false)
|
||||
mListRv.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onLoadError() {
|
||||
super.onLoadError()
|
||||
mListRv.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onResume() {
|
||||
if (isEverPause && mAdapter != null) mAdapter?.notifyDataSetChanged()
|
||||
super.onResume()
|
||||
DownloadManager.getInstance(context).addObserver(mDataWatcher)
|
||||
|
||||
if (!mIsPauseTopVideo) {
|
||||
resumeVideo()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPause() {
|
||||
super.onPause()
|
||||
DownloadManager.getInstance(context).removeObserver(mDataWatcher)
|
||||
|
||||
pauseVideo()
|
||||
if (mListViewModel.displayTopVideo) {
|
||||
mBinding?.videoItem?.player?.run {
|
||||
val currentPosition = getCurrentPosition()
|
||||
val topVideo = mEntity?.video
|
||||
if (topVideo != null) {
|
||||
ScrollCalculatorHelper.savePlaySchedule(
|
||||
MD5Utils.getContentMD5(topVideo.url),
|
||||
currentPosition
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
|
||||
if (requestCode == CommentActivity.REQUEST_CODE && resultCode == Activity.RESULT_OK) {
|
||||
val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0)
|
||||
if (commentCount != 0 && commentCount != null) {
|
||||
mEntity?.count?.comment = commentCount
|
||||
mBinding?.inputContainer?.bottomCommentTv?.text = mListViewModel.getCommentText(
|
||||
mEntity?.count?.comment
|
||||
?: 0, "评论"
|
||||
)
|
||||
updateFilterView()
|
||||
mListViewModel.load(LoadType.REFRESH)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
mEntity?.run {
|
||||
if (status == "pass" && display != "self_only") {
|
||||
HistoryHelper.insertGamesCollectionEntity(this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
mOrientationUtils?.backToProtVideo()
|
||||
|
||||
if (CustomManager.backFromWindowFull(requireActivity(), mBinding?.videoItem?.player?.getKey())) {
|
||||
return true
|
||||
}
|
||||
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
releaseVideo()
|
||||
}
|
||||
|
||||
private fun pauseVideo() {
|
||||
if (mListViewModel.displayTopVideo) {
|
||||
mBinding?.videoItem?.player?.onVideoPause()
|
||||
}
|
||||
}
|
||||
|
||||
private fun resumeVideo() {
|
||||
if (mListViewModel.displayTopVideo) {
|
||||
mBinding?.videoItem?.player?.onVideoResume()
|
||||
}
|
||||
}
|
||||
|
||||
private fun releaseVideo() {
|
||||
if (mListViewModel.displayTopVideo) {
|
||||
mBinding?.videoItem?.player?.release()
|
||||
mBinding?.videoItem?.player?.disposableTimer()
|
||||
}
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onUserFollow(change: EBUserFollow) {
|
||||
mEntity?.me?.isFollower = change.isFollow
|
||||
updateFollowView()
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(share: EBShare?) {
|
||||
postDelayedRunnable({
|
||||
tryCatchInRelease {
|
||||
if (share != null && share.shareEntrance == ShareUtils.ShareEntrance.gameCollection && isAdded) {
|
||||
mListViewModel.postShareGameCollection()
|
||||
}
|
||||
}
|
||||
}, 200)
|
||||
}
|
||||
|
||||
@OnClick(R.id.backIv, R.id.desContainer, R.id.videoDesContainer, R.id.toolbarLightUserContainer, R.id.toolbarUserContainer,
|
||||
R.id.videoItemUserContainer, R.id.toolbarFollowTv, R.id.videoItemFollowTv, R.id.squareIv, R.id.bottomStarIv,
|
||||
R.id.bottomStarTv, R.id.bottomLikeIv, R.id.bottomLikeTv, R.id.bottomShareIv, R.id.bottomShareTv, R.id.replyTv,
|
||||
R.id.bottomCommentIv, R.id.bottomCommentTv)
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.backIv -> requireActivity().finish()
|
||||
|
||||
R.id.desContainer,
|
||||
R.id.videoDesContainer -> {
|
||||
logEvent("click_game_collect_detail_product")
|
||||
startActivity(GameCollectionPosterActivity.getIntent(requireContext(), mEntity ?: GamesCollectionDetailEntity()))
|
||||
}
|
||||
|
||||
R.id.toolbarLightUserContainer,
|
||||
R.id.toolbarUserContainer,
|
||||
R.id.videoItemUserContainer -> {
|
||||
DirectUtils.directToHomeActivity(requireContext(), mEntity?.user?.id, "", "游戏单详情-导航栏")
|
||||
}
|
||||
|
||||
R.id.toolbarFollowTv,
|
||||
R.id.videoItemFollowTv -> {
|
||||
ifLogin("游戏单详情") {
|
||||
if (mEntity?.me?.isFollower == false) {
|
||||
logEvent("click_game_collect_detail_follow")
|
||||
mListViewModel?.followingCommand(mEntity?.user?.id ?: "", true)
|
||||
} else {
|
||||
DialogHelper.showDialog(
|
||||
requireContext(),
|
||||
"取消关注",
|
||||
"确定要取消关注 ${mEntity?.user?.name} 吗?",
|
||||
"确定取消",
|
||||
"暂不取消",
|
||||
{
|
||||
logEvent("click_game_collect_detail_follow_cancel")
|
||||
mListViewModel?.followingCommand(mEntity?.user?.id ?: "", false)
|
||||
},
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.squareIv -> {
|
||||
DirectUtils.directToGameCollectionSquare(
|
||||
requireContext(),
|
||||
"游戏单详情",
|
||||
"",
|
||||
mGameCollectionTitle,
|
||||
mGameCollectionId
|
||||
)
|
||||
}
|
||||
|
||||
R.id.replyTv -> {
|
||||
if (isSelfOnly()) return
|
||||
debounceActionWithInterval(v.id) {
|
||||
startCommentActivity()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.bottomCommentIv,
|
||||
R.id.bottomCommentTv -> {
|
||||
mListRv.post {
|
||||
mBinding?.appbar?.setExpanded(false)
|
||||
mLayoutManager.scrollToPositionWithOffset(mListViewModel?.filterPos ?: 0, 0)
|
||||
}
|
||||
}
|
||||
|
||||
R.id.bottomStarIv,
|
||||
R.id.bottomStarTv -> {
|
||||
if (isSelfOnly()) return
|
||||
debounceActionWithInterval(v.id) {
|
||||
ifLogin("游戏单详情") {
|
||||
if (mEntity?.me?.isFavorite == true) {
|
||||
logEvent("click_game_collect_detail_favorite_cancel")
|
||||
} else {
|
||||
logEvent("click_game_collect_detail_favorite")
|
||||
}
|
||||
mListViewModel?.postFavoriteGameCollection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.bottomLikeIv,
|
||||
R.id.bottomLikeTv -> {
|
||||
if (isSelfOnly()) return
|
||||
debounceActionWithInterval(v.id) {
|
||||
ifLogin("游戏单详情") {
|
||||
mListViewModel?.postVoteGameCollection()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
R.id.bottomShareIv,
|
||||
R.id.bottomShareTv -> {
|
||||
if (isSelfOnly()) return
|
||||
showMoreItemDialog()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateFilterView() {
|
||||
mBinding?.fixedTopFilterView?.run {
|
||||
getFilterVH()?.binding?.commentHintCountTv?.text = "${provideListViewModel().commentCount}"
|
||||
|
||||
filterLatestTv.setTextColor(getFilterVH()?.binding?.filterLatestTv?.textColors)
|
||||
filterOldestTv.setTextColor(getFilterVH()?.binding?.filterOldestTv?.textColors)
|
||||
commentHintTv.text = getFilterVH()?.binding?.commentHintTv?.text
|
||||
commentHintCountTv.text = getFilterVH()?.binding?.commentHintCountTv?.text
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFilterVH(): BaseCommentAdapter.CommentFilterViewHolder? {
|
||||
return (provideListAdapter() as BaseCommentAdapter).filterVH
|
||||
}
|
||||
|
||||
private fun isSelfOnly(): Boolean {
|
||||
val isSelfOnly = mEntity?.display == "self_only"
|
||||
if (isSelfOnly) toast("游戏单为仅自己可见状态")
|
||||
return isSelfOnly
|
||||
}
|
||||
|
||||
private fun showMoreItemDialog() {
|
||||
if (!lifecycle.currentState.isAtLeast(Lifecycle.State.STARTED)) return
|
||||
mEntity?.run {
|
||||
GameCollectionShareDialog.showMoreDialog(
|
||||
requireActivity() as AppCompatActivity,
|
||||
getShareEntity(this)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private fun startCommentActivity() {
|
||||
mEntity?.run {
|
||||
val intent = CommentActivity.getGameCollectionCommentIntent(
|
||||
requireContext(),
|
||||
id,
|
||||
count?.comment ?: 0
|
||||
)
|
||||
startActivityForResult(intent, CommentActivity.REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
|
||||
private fun getShareEntity(entity: GamesCollectionDetailEntity): NormalShareEntity {
|
||||
val shareUrl = if (isPublishEnv()) {
|
||||
Constants.GAME_COLLECTION_SHARE_ADDRESS
|
||||
} else {
|
||||
Constants.GAME_COLLECTION_SHARE_ADDRESS_DEV
|
||||
} + "&id=${entity.id}"
|
||||
return NormalShareEntity(
|
||||
id = entity.id,
|
||||
shareUrl = shareUrl,
|
||||
shareIcon = entity.cover,
|
||||
shareTitle = entity.title,
|
||||
shareSummary = entity.intro,
|
||||
shareEntrance = ShareUtils.ShareEntrance.gameCollection
|
||||
)
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun logEvent(event: String, shareType: String = "") {
|
||||
NewLogUtils.logViewOrClickGameCollectionDetail(
|
||||
event,
|
||||
mGameCollectionTitle,
|
||||
mGameCollectionId,
|
||||
shareType
|
||||
)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,340 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.text.TextUtils
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.baselist.LoadStatus
|
||||
import com.gh.gamecenter.entity.CommentEntity
|
||||
import com.gh.gamecenter.entity.GamesCollectionDetailEntity
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.article.detail.CommentItemData
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
import tv.danmaku.ijk.media.exo2.ExoSourceManager
|
||||
|
||||
open class GameCollectionDetailViewModel(application: Application,
|
||||
var gameCollectionId: String) :
|
||||
BaseCommentViewModel(application, "", "", "", "") {
|
||||
|
||||
var followLiveData = MutableLiveData<Boolean>()
|
||||
val favoriteLiveData = MutableLiveData<Boolean>()
|
||||
val likeLiveData = MutableLiveData<Boolean>()
|
||||
val shareLiveData = MutableLiveData<Boolean>()
|
||||
var gameCollectionDetail: GamesCollectionDetailEntity? = null
|
||||
var gameCollectionTitle = ""
|
||||
var ownerUserId = ""
|
||||
var filterPos = 0
|
||||
var displayTopVideo: Boolean = false
|
||||
var videoIsMuted = SPUtils.getBoolean(Constants.SP_VIDEO_PLAY_MUTE, true)
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getGameCollectionDetail() {
|
||||
mApi.getGameCollectionDetail(gameCollectionId)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<GamesCollectionDetailEntity>() {
|
||||
override fun onSuccess(data: GamesCollectionDetailEntity) {
|
||||
// 4.4以下设备不显示顶部视频
|
||||
displayTopVideo = data.video != null
|
||||
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT
|
||||
gameCollectionDetail = data
|
||||
gameCollectionTitle = data.title
|
||||
commentCount = data.count?.comment ?: 0
|
||||
ownerUserId = data.user?.id ?: ""
|
||||
filterPos = (data.games?.size ?: 1) + 1
|
||||
loadResultLiveData.postValue(LoadResult.SUCCESS)
|
||||
mergeData(mListLiveData.value)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
if (exception is HttpException) {
|
||||
if (exception.code().toString().startsWith("404")) {
|
||||
loadResultLiveData.postValue(LoadResult.DELETED)
|
||||
} else {
|
||||
loadResultLiveData.postValue(LoadResult.NETWORK_ERROR)
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int) = null
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<CommentEntity>> {
|
||||
val sortType = if (currentSortType == SortType.LATEST) "earliest" else "latest"
|
||||
return mApi.getGameCollectionComments(gameCollectionId, page, sortType)
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) { mergeData(it) }
|
||||
}
|
||||
|
||||
fun mergeData(list: List<CommentEntity>?) {
|
||||
gameCollectionDetail?.run {
|
||||
val itemDataList = arrayListOf<CommentItemData>().apply {
|
||||
if (games?.isEmpty() == true) {
|
||||
add(CommentItemData(gameEmpty = true))
|
||||
} else {
|
||||
val gameLastIndex = games!!.size - 1
|
||||
if (mResultLiveData.value?.get(0)?.game != null
|
||||
&& mResultLiveData.value?.get(gameLastIndex)?.game != null) {
|
||||
for (i in 0..gameLastIndex) {
|
||||
add(mResultLiveData.value!![i])
|
||||
}
|
||||
} else {
|
||||
games?.forEach {
|
||||
add(CommentItemData(game = it))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
add(CommentItemData(divider = true))
|
||||
add(CommentItemData(filter = true))
|
||||
|
||||
if (list.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_EMPTY) {
|
||||
add(CommentItemData(errorEmpty = true))
|
||||
} else if (list.isNullOrEmpty() && mLoadStatusLiveData.value == LoadStatus.INIT_FAILED) {
|
||||
add(CommentItemData(errorConnection = true))
|
||||
} else {
|
||||
list?.forEach {
|
||||
add(CommentItemData(commentNormal = it))
|
||||
}
|
||||
add(CommentItemData(footer = true))
|
||||
}
|
||||
}
|
||||
|
||||
mResultLiveData.postValue(itemDataList)
|
||||
}
|
||||
}
|
||||
|
||||
fun followingCommand(userId: String, isFollow: Boolean) {
|
||||
val observable = if (isFollow) {
|
||||
RetrofitManager.getInstance(getApplication()).api.postFollowing(userId)
|
||||
} else {
|
||||
RetrofitManager.getInstance(getApplication()).api.deleteFollowing(userId)
|
||||
}
|
||||
observable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
if (isFollow) {
|
||||
Utils.toast(getApplication(), R.string.concern_success)
|
||||
} else {
|
||||
Utils.toast(getApplication(), R.string.concern_cancel)
|
||||
}
|
||||
gameCollectionDetail?.me?.isFollower =
|
||||
gameCollectionDetail?.me?.isFollower != true
|
||||
followLiveData.postValue(isFollow)
|
||||
EventBus.getDefault().post(EBUserFollow(userId, isFollow))
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
Utils.toast(getApplication(), R.string.loading_failed_hint)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun postFavoriteGameCollection() {
|
||||
if (gameCollectionDetail == null) return
|
||||
val single = if (gameCollectionDetail?.me?.isFavorite == true) {
|
||||
mApi.deleteFavoriteGameCollection(UserManager.getInstance().userId, gameCollectionId)
|
||||
} else {
|
||||
mApi.favoriteGameCollection(UserManager.getInstance().userId, gameCollectionId)
|
||||
}
|
||||
single.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
if (gameCollectionDetail?.me?.isFavorite == true) {
|
||||
gameCollectionDetail!!.count!!.favorite--
|
||||
ToastUtils.showToast("取消收藏")
|
||||
} else {
|
||||
gameCollectionDetail!!.count!!.favorite++
|
||||
ToastUtils.showToast("收藏成功")
|
||||
}
|
||||
gameCollectionDetail?.me?.isFavorite =
|
||||
gameCollectionDetail?.me?.isFavorite != true
|
||||
favoriteLiveData.postValue(true)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(
|
||||
getApplication(),
|
||||
exception.response()?.errorBody()?.string()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun postVoteGameCollection() {
|
||||
if (gameCollectionDetail == null) return
|
||||
val single = if (gameCollectionDetail?.me?.vote == true) {
|
||||
mApi.unVoteGameCollection(gameCollectionId)
|
||||
} else {
|
||||
mApi.voteGameCollection(gameCollectionId)
|
||||
}
|
||||
single.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
if (gameCollectionDetail?.me?.vote == true) {
|
||||
gameCollectionDetail!!.count!!.vote--
|
||||
ToastUtils.showToast("取消点赞")
|
||||
} else {
|
||||
gameCollectionDetail!!.count!!.vote++
|
||||
ToastUtils.showToast("点赞成功")
|
||||
}
|
||||
gameCollectionDetail?.me?.vote =
|
||||
gameCollectionDetail?.me?.vote != true
|
||||
likeLiveData.postValue(true)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(
|
||||
getApplication(),
|
||||
exception.response()?.errorBody()?.string()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@SuppressLint("CheckResult")
|
||||
fun postShareGameCollection() {
|
||||
if (gameCollectionDetail == null) return
|
||||
mApi.shareGameCollection(gameCollectionId)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
gameCollectionDetail!!.count!!.share++
|
||||
shareLiveData.postValue(true)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(
|
||||
getApplication(),
|
||||
exception.response()?.errorBody()?.string()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun postVoteGameCollectionComment(comment: CommentEntity, successCallback: (() -> Unit)) {
|
||||
mApi.voteGameCollectionComment(gameCollectionId, comment.id)
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
if (comment.me?.isCommentVoted == true) {
|
||||
comment.vote--
|
||||
ToastUtils.showToast("取消点赞")
|
||||
} else {
|
||||
comment.vote++
|
||||
ToastUtils.showToast("点赞成功")
|
||||
}
|
||||
comment.me?.isCommentVoted = !(comment.me?.isCommentVoted ?: false)
|
||||
successCallback.invoke()
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
|
||||
if (exception is HttpException) {
|
||||
ErrorHelper.handleError(
|
||||
getApplication(),
|
||||
exception.response()?.errorBody()?.string()
|
||||
)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
override fun deleteComment(entity: CommentEntity, callback: () -> Unit) {
|
||||
mApi.deleteGameCollectionComment(gameCollectionId, entity.id)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
hideCommentSuccess()
|
||||
callback.invoke()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
e?.let { httpException ->
|
||||
if (httpException.code() == 403) {
|
||||
val string = e.response().errorBody()?.string() ?: ""
|
||||
val errorJson = JSONObject(string)
|
||||
val errorCode = errorJson.getInt("code")
|
||||
if (errorCode == 403059) {
|
||||
Utils.toast(getApplication(), "权限错误,请刷新后重试")
|
||||
return
|
||||
} else {
|
||||
Utils.toast(getApplication(), e.message())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getStarText(): String {
|
||||
return if (gameCollectionDetail?.count?.favorite == 0) "收藏" else "${gameCollectionDetail?.count?.favorite}"
|
||||
}
|
||||
|
||||
fun getShareText(): String {
|
||||
return if (gameCollectionDetail?.count?.share == 0) "分享" else "${gameCollectionDetail?.count?.share}"
|
||||
}
|
||||
|
||||
fun isTopVideoPartlyCached(topVideoUrl: String): Boolean {
|
||||
val cache = ExoSourceManager.getCacheSingleInstance(HaloApp.getInstance().application, null)
|
||||
val key = Uri.parse(topVideoUrl).toString()
|
||||
return if (!TextUtils.isEmpty(key)) {
|
||||
val cachedSpans = cache.getCachedSpans(key)
|
||||
cachedSpans.size != 0
|
||||
} else {
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
class Factory(private val gameCollectionId: String) : ViewModelProvider.NewInstanceFactory() {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return GameCollectionDetailViewModel(HaloApp.getInstance().application, gameCollectionId) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,28 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GamesCollectionDetailEntity
|
||||
|
||||
class GameCollectionPosterActivity : NormalActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
hideToolbar(true)
|
||||
DisplayUtils.setStatusBarColor(this, R.color.transparent, false)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, entity: GamesCollectionDetailEntity): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putParcelable(EntranceUtils.KEY_DATA, entity)
|
||||
return getTargetIntent(context, GameCollectionPosterActivity::class.java, GameCollectionPosterFragment::class.java, bundle)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,119 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import androidx.annotation.RequiresApi
|
||||
import butterknife.OnClick
|
||||
import com.gh.common.runOnUiThread
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.FragmentGameCollectionPosterBinding
|
||||
import com.gh.gamecenter.databinding.LayoutGameCollectionTagBinding
|
||||
import com.gh.gamecenter.entity.GamesCollectionDetailEntity
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
|
||||
class GameCollectionPosterFragment : NormalFragment() {
|
||||
|
||||
private var mBinding: FragmentGameCollectionPosterBinding? = null
|
||||
private var mViewModel: GameCollectionPosterViewModel? = null
|
||||
private var mEntity = GamesCollectionDetailEntity()
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
FragmentGameCollectionPosterBinding.inflate(layoutInflater).apply { mBinding = this }.root
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
mViewModel = viewModelProvider()
|
||||
mEntity = arguments?.getParcelable(EntranceUtils.KEY_DATA) ?: GamesCollectionDetailEntity()
|
||||
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mViewModel?.followLiveData?.observeNonNull(viewLifecycleOwner) {
|
||||
setFollowStatus(it)
|
||||
}
|
||||
|
||||
mBinding?.apply {
|
||||
mEntity.apply {
|
||||
entity = this
|
||||
executePendingBindings()
|
||||
|
||||
ImageUtils.getBitmap(cover, object : BiCallback<Bitmap, Boolean> {
|
||||
@RequiresApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
|
||||
override fun onFirst(first: Bitmap) {
|
||||
val blurBitmap = BitmapUtils.getBlurBitmap(requireContext(), first, 16)
|
||||
runOnUiThread {
|
||||
posterBg.setImageBitmap(blurBitmap)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onSecond(second: Boolean) {}
|
||||
})
|
||||
|
||||
userIcon.display(user?.border, user?.icon, user?.auth?.icon)
|
||||
|
||||
tags?.forEachIndexed { index, tag ->
|
||||
tagContainer.addView(getTagView(tag.name, index == tags!!.size - 1))
|
||||
}
|
||||
|
||||
setFollowStatus(me?.isFollower == true)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun setFollowStatus(isFollow: Boolean) {
|
||||
mBinding?.followTv?.run {
|
||||
setBackgroundResource(if (isFollow) R.drawable.button_round_white_alpha_10 else R.drawable.button_round_white_alpha_20)
|
||||
setTextColor(if (isFollow) R.color.white_alpha_60.toColor() else R.color.white.toColor())
|
||||
text = if (isFollow) "已关注" else "关注"
|
||||
}
|
||||
}
|
||||
|
||||
private fun getTagView(content: String, isLast: Boolean): View {
|
||||
return LayoutGameCollectionTagBinding.inflate(layoutInflater).apply {
|
||||
root.setPadding(0, 12F.dip2px(), 0, 0)
|
||||
divider.goneIf(isLast)
|
||||
contentTv.text = content
|
||||
}.root
|
||||
}
|
||||
|
||||
@OnClick(R.id.backIv, R.id.userIcon, R.id.userName, R.id.followTv)
|
||||
override fun onClick(view: View) {
|
||||
when (view.id) {
|
||||
R.id.backIv -> requireActivity().finish()
|
||||
|
||||
R.id.userIcon -> {
|
||||
DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-封面页")
|
||||
}
|
||||
|
||||
R.id.userName -> {
|
||||
DirectUtils.directToHomeActivity(requireContext(), mEntity.user?.id, "", "游戏单详情-封面页")
|
||||
}
|
||||
|
||||
R.id.followTv -> {
|
||||
ifLogin("游戏单详情-封面页") {
|
||||
if (mBinding?.followTv?.text == "关注") {
|
||||
mViewModel?.followingCommand(mEntity.user?.id ?: "", true)
|
||||
} else {
|
||||
DialogHelper.showDialog(
|
||||
requireContext(),
|
||||
"取消关注",
|
||||
"确定要取消关注 ${mEntity.user?.name} 吗?",
|
||||
"确定取消",
|
||||
"暂不取消",
|
||||
{ mViewModel?.followingCommand(mEntity.user?.id ?: "", false) },
|
||||
extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,48 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.eventbus.EBUserFollow
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
import retrofit2.HttpException
|
||||
|
||||
class GameCollectionPosterViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
var followLiveData = MutableLiveData<Boolean>()
|
||||
|
||||
fun followingCommand(userId: String, isFollow: Boolean) {
|
||||
val observable = if (isFollow) {
|
||||
RetrofitManager.getInstance(getApplication()).api.postFollowing(userId)
|
||||
} else {
|
||||
RetrofitManager.getInstance(getApplication()).api.deleteFollowing(userId)
|
||||
}
|
||||
observable
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
if (isFollow) {
|
||||
Utils.toast(getApplication(), R.string.concern_success)
|
||||
} else {
|
||||
Utils.toast(getApplication(), R.string.concern_cancel)
|
||||
}
|
||||
followLiveData.postValue(isFollow)
|
||||
EventBus.getDefault().post(EBUserFollow(userId, isFollow))
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
Utils.toast(getApplication(), R.string.loading_failed_hint)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,131 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.appcompat.app.AppCompatActivity
|
||||
import com.gh.common.dialog.BaseDraggableDialogFragment
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.databinding.DialogGameCollectionShareBinding
|
||||
import com.gh.gamecenter.entity.NormalShareEntity
|
||||
import com.gh.gamecenter.eventbus.EBShare
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class GameCollectionShareDialog : BaseDraggableDialogFragment() {
|
||||
|
||||
private lateinit var mBinding: DialogGameCollectionShareBinding
|
||||
private var mShareEntity: NormalShareEntity? = null
|
||||
private var mShareUtils: ShareUtils? = null
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mShareEntity = requireArguments().getParcelable(KEY_SHARE)
|
||||
mShareUtils = getShareUtils()
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?
|
||||
): View {
|
||||
return DialogGameCollectionShareBinding.inflate(inflater, container, false)
|
||||
.apply { mBinding = this }.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
mBinding.run {
|
||||
wechatItem.setOnClickListener {
|
||||
logEvent("微信")
|
||||
mShareUtils?.wechatShare()
|
||||
}
|
||||
|
||||
wechatMomentsItem.setOnClickListener {
|
||||
logEvent("朋友圈")
|
||||
mShareUtils?.wechatMomentsShare()
|
||||
}
|
||||
|
||||
qqItem.setOnClickListener {
|
||||
logEvent("QQ好友")
|
||||
mShareUtils?.qqShare()
|
||||
}
|
||||
|
||||
qqZoneItem.setOnClickListener {
|
||||
logEvent("QQ空间")
|
||||
mShareUtils?.qZoneShare()
|
||||
}
|
||||
|
||||
weiboItem.setOnClickListener {
|
||||
logEvent("新浪微博")
|
||||
mShareUtils?.sinaWeiboShare()
|
||||
}
|
||||
|
||||
smsItem.setOnClickListener {
|
||||
logEvent("短信")
|
||||
EventBus.getDefault().post(EBShare(ShareUtils.shareEntrance))
|
||||
mShareUtils?.shortMessageShare()
|
||||
}
|
||||
|
||||
copyItem.setOnClickListener {
|
||||
logEvent("复制链接")
|
||||
EventBus.getDefault().post(EBShare(ShareUtils.shareEntrance))
|
||||
mShareUtils?.copyLink(mShareUtils?.shareUrl ?: "")
|
||||
}
|
||||
|
||||
cancelTv.setOnClickListener {
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getShareUtils(): ShareUtils {
|
||||
val shareUtils = ShareUtils.getInstance(requireContext())
|
||||
mShareEntity?.run {
|
||||
shareUtils.shareParamsDetail(
|
||||
requireActivity(),
|
||||
shareUrl,
|
||||
shareIcon,
|
||||
shareTitle,
|
||||
shareSummary,
|
||||
shareEntrance,
|
||||
id,
|
||||
null
|
||||
)
|
||||
}
|
||||
return shareUtils
|
||||
}
|
||||
|
||||
private fun logEvent(shareType: String) {
|
||||
NewLogUtils.logViewOrClickGameCollectionDetail(
|
||||
"click_game_collect_detail_share",
|
||||
mShareEntity?.shareTitle ?: "",
|
||||
mShareEntity?.id ?: "",
|
||||
shareType
|
||||
)
|
||||
}
|
||||
|
||||
override fun getRootView(): View = mBinding.root
|
||||
override fun getDragCloseView(): View = mBinding.dragClose
|
||||
|
||||
companion object {
|
||||
const val KEY_SHARE = "share"
|
||||
const val REQUEST_CODE = 1105
|
||||
|
||||
@JvmStatic
|
||||
fun showMoreDialog(
|
||||
activity: AppCompatActivity,
|
||||
share: NormalShareEntity,
|
||||
) {
|
||||
GameCollectionShareDialog().apply {
|
||||
arguments = Bundle().apply {
|
||||
putParcelable(KEY_SHARE, share)
|
||||
}
|
||||
}.show(
|
||||
activity.supportFragmentManager,
|
||||
GameCollectionShareDialog::class.java.name
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,430 @@
|
||||
package com.gh.gamecenter.gamecollection.detail
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import android.view.GestureDetector
|
||||
import android.view.MotionEvent
|
||||
import android.view.Surface
|
||||
import android.view.View
|
||||
import android.widget.ImageView
|
||||
import android.widget.SeekBar
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.fragment.app.Fragment
|
||||
import androidx.fragment.app.FragmentManager
|
||||
import com.gh.common.observer.MuteCallback
|
||||
import com.gh.common.observer.VolumeObserver
|
||||
import com.gh.common.runOnIoThread
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.GamesCollectionDetailEntity
|
||||
import com.gh.gamecenter.home.video.ScrollCalculatorHelper
|
||||
import com.gh.gamecenter.video.detail.CustomManager
|
||||
import com.lightgame.utils.Utils
|
||||
import com.shuyu.gsyvideoplayer.utils.CommonUtil
|
||||
import com.shuyu.gsyvideoplayer.video.StandardGSYVideoPlayer
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge
|
||||
import com.squareup.picasso.Picasso
|
||||
import io.reactivex.disposables.Disposable
|
||||
import kotlinx.android.synthetic.main.layout_game_detail_video_portrait.view.*
|
||||
import kotlinx.android.synthetic.main.piece_video_control.view.*
|
||||
import kotlinx.android.synthetic.main.piece_video_replay.view.*
|
||||
import java.text.DecimalFormat
|
||||
import java.util.*
|
||||
|
||||
class GameCollectionVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) : StandardGSYVideoPlayer(context, attrs) {
|
||||
|
||||
private var mMuteCallback: MuteCallback
|
||||
private var mVolumeObserver: VolumeObserver
|
||||
|
||||
var gameName = ""
|
||||
var gameCollectionId = ""
|
||||
var gameCollectionTitle = ""
|
||||
var video: GamesCollectionDetailEntity.Video? = null
|
||||
var viewModel: GameCollectionDetailViewModel? = null
|
||||
var uuid = UUID.randomUUID().toString()
|
||||
private var mMuteDisposable: Disposable? = null
|
||||
private var mIsAutoPlay = false
|
||||
private var mIsManualStop = false
|
||||
|
||||
init {
|
||||
post {
|
||||
gestureDetector = GestureDetector(getContext().applicationContext, object : GestureDetector.SimpleOnGestureListener() {
|
||||
override fun onSingleTapConfirmed(e: MotionEvent): Boolean {
|
||||
if (!mChangePosition && !mChangeVolume && !mBrightness) {
|
||||
onClickUiToggle(e)
|
||||
}
|
||||
return super.onSingleTapConfirmed(e)
|
||||
}
|
||||
})
|
||||
|
||||
if (mIfCurrentIsFullscreen) {
|
||||
showBackBtn()
|
||||
} else {
|
||||
hideBackBtn()
|
||||
}
|
||||
|
||||
volume.setOnClickListener { toggleMute() }
|
||||
}
|
||||
|
||||
mMuteCallback = object : MuteCallback {
|
||||
override fun onMute(isMute: Boolean) {
|
||||
if (isMute) {
|
||||
mute()
|
||||
} else {
|
||||
unMute()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mVolumeObserver = VolumeObserver(mMuteCallback)
|
||||
|
||||
setBackFromFullScreenListener {
|
||||
clearFullscreenLayout()
|
||||
}
|
||||
|
||||
errorBtn?.setOnClickListener {
|
||||
debounceActionWithInterval(errorBtn.id, 1000) {
|
||||
if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) {
|
||||
Utils.toast(context, "网络异常,请检查手机网络状态")
|
||||
setViewShowState(mStartButton, View.INVISIBLE)
|
||||
errorContainer.visibility = View.VISIBLE
|
||||
return@debounceActionWithInterval
|
||||
}
|
||||
startPlayLogic(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//这个必须配置最上面的构造才能生效
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.layout_game_detail_video_portrait
|
||||
}
|
||||
|
||||
fun observeVolume(fragment: Fragment?) {
|
||||
fragment?.context?.applicationContext?.contentResolver?.registerContentObserver(
|
||||
android.provider.Settings.System.CONTENT_URI, true, mVolumeObserver)
|
||||
|
||||
fragment?.fragmentManager?.registerFragmentLifecycleCallbacks(
|
||||
object : FragmentManager.FragmentLifecycleCallbacks() {
|
||||
override fun onFragmentPaused(fm: FragmentManager, f: Fragment) {
|
||||
if (f === fragment) {
|
||||
fragment.context?.applicationContext?.contentResolver?.unregisterContentObserver(mVolumeObserver)
|
||||
fragment.fragmentManager?.unregisterFragmentLifecycleCallbacks(this)
|
||||
}
|
||||
}
|
||||
}, false)
|
||||
}
|
||||
|
||||
fun startPlayLogic(isAutoPlay: Boolean) {
|
||||
mIsAutoPlay = isAutoPlay
|
||||
violenceUpdateMuteStatus()
|
||||
logVideoEvent("video_game_collect_detail_play", if (isAutoPlay) "自动播放" else "手动播放")
|
||||
if (isAutoPlay) {
|
||||
val seekTime = ScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(video?.url))
|
||||
seekOnStart = seekTime
|
||||
}
|
||||
startPlayLogic()
|
||||
}
|
||||
|
||||
fun violenceUpdateMuteStatus() {
|
||||
if (mMuteDisposable != null) {
|
||||
mMuteDisposable?.dispose()
|
||||
mMuteDisposable = null
|
||||
}
|
||||
mMuteDisposable = rxTimer(25) {
|
||||
if (it >= 400) {
|
||||
mMuteDisposable?.dispose()
|
||||
mMuteDisposable = null
|
||||
}
|
||||
updateMuteStatus()
|
||||
}
|
||||
}
|
||||
|
||||
fun disposableTimer() {
|
||||
if (mMuteDisposable != null && !mMuteDisposable!!.isDisposed) {
|
||||
mMuteDisposable?.dispose()
|
||||
mMuteDisposable = null
|
||||
}
|
||||
}
|
||||
|
||||
private fun toggleMute() {
|
||||
if (viewModel?.videoIsMuted == true) {
|
||||
unMute(true)
|
||||
} else {
|
||||
mute(true)
|
||||
}
|
||||
}
|
||||
|
||||
fun updateMuteStatus() {
|
||||
if (viewModel?.videoIsMuted == true) {
|
||||
mute()
|
||||
} else {
|
||||
unMute()
|
||||
}
|
||||
}
|
||||
|
||||
private fun mute(isManual: Boolean = false) {
|
||||
viewModel?.videoIsMuted = true
|
||||
volume.setImageResource(R.drawable.ic_game_detail_volume_off)
|
||||
CustomManager.getCustomManager(getKey()).isNeedMute = true
|
||||
if (isManual) {
|
||||
Utils.toast(context, "当前处于静音状态")
|
||||
logVideoEvent("video_game_collect_detail_mute")
|
||||
}
|
||||
}
|
||||
|
||||
private fun unMute(isManual: Boolean = false) {
|
||||
viewModel?.videoIsMuted = false
|
||||
volume.setImageResource(R.drawable.ic_game_detail_volume_on)
|
||||
CustomManager.getCustomManager(getKey()).isNeedMute = false
|
||||
if (isManual) {
|
||||
logVideoEvent("video_game_collect_detail_mute_cancel")
|
||||
}
|
||||
}
|
||||
|
||||
// 重载以减少横竖屏切换的时间
|
||||
override fun checkoutState() {
|
||||
removeCallbacks(mCheckoutTask)
|
||||
postDelayed(mCheckoutTask, 300)
|
||||
}
|
||||
|
||||
override fun clearFullscreenLayout() {
|
||||
super.clearFullscreenLayout()
|
||||
updateMuteStatus()
|
||||
hideBackBtn()
|
||||
}
|
||||
|
||||
fun updateThumb(url: String) {
|
||||
Picasso.with(context).load(url).fit().into(findViewById<ImageView>(R.id.thumbImage))
|
||||
}
|
||||
|
||||
override fun touchDoubleUp(e: MotionEvent?) {
|
||||
// we do not need double click to play or pause
|
||||
}
|
||||
|
||||
// 不需要弹弹窗,直接播放
|
||||
override fun showWifiDialog() {
|
||||
startPlayLogic(false)
|
||||
//val trafficVideo = PreferenceManager.getDefaultSharedPreferences(context).getBoolean(SettingsFragment.TRAFFIC_VIDEO_SP_KEY, false)
|
||||
//if (trafficVideo) {
|
||||
// 不延迟的话 isCacheFile 可能直接返回 false
|
||||
postDelayed({
|
||||
// 这个库的 NetworkUtils.isWifiConnected 可能会触发空指针,这里换为我们自己的
|
||||
if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext) && !gsyVideoManager.isCacheFile) {
|
||||
Utils.toast(context, "网络异常,请检查手机网络状态")
|
||||
} else if (!NetworkUtils.isWifiConnected(mContext) && !gsyVideoManager.isCacheFile) {
|
||||
Utils.toast(context, "当前为非Wi-Fi环境,请注意流量消耗")
|
||||
}
|
||||
}, 100)
|
||||
// }
|
||||
}
|
||||
|
||||
override fun getGSYVideoManager(): GSYVideoViewBridge {
|
||||
CustomManager.getCustomManager(getKey()).initContext(context.applicationContext)
|
||||
return CustomManager.getCustomManager(getKey())
|
||||
}
|
||||
|
||||
fun getKey(): String {
|
||||
return uuid
|
||||
}
|
||||
|
||||
override fun onAutoCompletion() {
|
||||
// 这个方法在内核被释放时也会被回调,所以可以用来统计播放量和播放时长
|
||||
val playedTime = (gsyVideoManager.currentPosition / 1000).toInt()
|
||||
|
||||
//播放完成后判断是否已缓冲完毕,没有完成显示播放错误状态
|
||||
if (mBufferPoint != 0 && mBufferPoint != 100 && isShown) {
|
||||
gsyVideoManager.releaseMediaPlayer()
|
||||
changeUiToPreparingShow()
|
||||
postDelayed({
|
||||
if (!com.shuyu.gsyvideoplayer.utils.NetworkUtils.isAvailable(mContext)) {
|
||||
Utils.toast(context, "网络错误,视频播放失败")
|
||||
changeUiToError()
|
||||
}
|
||||
}, 10 * 1000)
|
||||
}
|
||||
logVideoEvent("video_game_collect_detail_stop", stopAction = "播放完毕")
|
||||
super.onAutoCompletion()
|
||||
}
|
||||
|
||||
override fun onStopTrackingTouch(seekBar: SeekBar?) {
|
||||
super.onStopTrackingTouch(seekBar)
|
||||
logVideoEvent("video_game_collect_detail_progress_drag")
|
||||
}
|
||||
|
||||
override fun isShowNetConfirm(): Boolean {
|
||||
return (!mOriginUrl.startsWith("file") && !mOriginUrl.startsWith("android.resource") && !CommonUtil.isWifiConnected(context)
|
||||
&& mNeedShowWifiTip)
|
||||
}
|
||||
|
||||
override fun updateStartImage() {
|
||||
if (mStartButton is ImageView) {
|
||||
val imageView = mStartButton as ImageView
|
||||
when (mCurrentState) {
|
||||
GSYVideoView.CURRENT_STATE_PLAYING -> imageView.setImageResource(R.drawable.ic_game_detail_pause)
|
||||
GSYVideoView.CURRENT_STATE_ERROR -> imageView.setImageResource(R.drawable.ic_game_detail_play)
|
||||
else -> imageView.setImageResource(R.drawable.ic_game_detail_play)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun setStateAndUi(state: Int) {
|
||||
super.setStateAndUi(state)
|
||||
|
||||
if (state == CURRENT_STATE_AUTO_COMPLETE) {
|
||||
hideAllWidget()
|
||||
replayContainer.visibility = View.VISIBLE
|
||||
mTopContainer.visibility = View.VISIBLE
|
||||
replayIv.setOnClickListener {
|
||||
startButton.performClick()
|
||||
violenceUpdateMuteStatus()
|
||||
logVideoEvent("video_game_collect_detail_play", "再次播放")
|
||||
}
|
||||
updateThumb(video!!.poster)
|
||||
} else {
|
||||
replayContainer.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
|
||||
private fun showBackBtn() {
|
||||
mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg)
|
||||
back.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
private fun hideBackBtn() {
|
||||
mTopContainer?.setBackgroundResource(0)
|
||||
back.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun getEnlargeImageRes(): Int {
|
||||
return R.drawable.ic_game_detail_enter_full_screen
|
||||
}
|
||||
|
||||
override fun getShrinkImageRes(): Int {
|
||||
return R.drawable.ic_game_detail_exit_full_screen
|
||||
}
|
||||
|
||||
override fun onInterceptTouchEvent(ev: MotionEvent): Boolean {
|
||||
if (!isIfCurrentIsFullscreen) {
|
||||
parent.requestDisallowInterceptTouchEvent(true)
|
||||
}
|
||||
return super.onInterceptTouchEvent(ev)
|
||||
}
|
||||
|
||||
/******************* 下方两个重载方法,在播放开始前不屏蔽封面,不需要可屏蔽 ********************/
|
||||
|
||||
override fun onSurfaceUpdated(surface: Surface) {
|
||||
super.onSurfaceUpdated(surface)
|
||||
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
|
||||
mThumbImageViewLayout.visibility = View.INVISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun setViewShowState(view: View?, visibility: Int) {
|
||||
if (view === mThumbImageViewLayout && visibility != View.VISIBLE) {
|
||||
return
|
||||
}
|
||||
super.setViewShowState(view, visibility)
|
||||
}
|
||||
|
||||
/********************************各类UI的状态显示*********************************************/
|
||||
|
||||
override fun changeUiToNormal() {
|
||||
super.changeUiToNormal()
|
||||
errorContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToPreparingShow() {
|
||||
super.changeUiToPreparingShow()
|
||||
errorContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToPlayingShow() {
|
||||
super.changeUiToPlayingShow()
|
||||
errorContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToPauseShow() {
|
||||
super.changeUiToPauseShow()
|
||||
errorContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToCompleteShow() {
|
||||
super.changeUiToCompleteShow()
|
||||
errorContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun changeUiToError() {
|
||||
super.changeUiToError()
|
||||
setViewShowState(mStartButton, View.INVISIBLE)
|
||||
errorContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun netWorkErrorLogic() {
|
||||
super.netWorkErrorLogic()
|
||||
Utils.toast(context, "网络错误,视频播放失败")
|
||||
setViewShowState(mStartButton, View.INVISIBLE)
|
||||
errorContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
//监控播放错误
|
||||
override fun onError(what: Int, extra: Int) {
|
||||
super.onError(what, extra)
|
||||
Utils.toast(context, "网络错误,视频播放失败")
|
||||
setViewShowState(mStartButton, View.INVISIBLE)
|
||||
errorContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun releaseVideos() {
|
||||
CustomManager.releaseAllVideos(getKey())
|
||||
}
|
||||
|
||||
override fun onVideoPause() {
|
||||
super.onVideoPause()
|
||||
logVideoEvent("video_game_collect_detail_stop", stopAction = if (mIsManualStop) "手动停止" else "自动停止")
|
||||
mIsManualStop = false
|
||||
}
|
||||
|
||||
override fun onVideoResume() {
|
||||
super.onVideoResume()
|
||||
logVideoEvent("video_game_collect_detail_play", "继续播放")
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
when (v.id) {
|
||||
R.id.start -> {
|
||||
when (currentState) {
|
||||
GSYVideoView.CURRENT_STATE_PLAYING -> mIsManualStop = true
|
||||
|
||||
GSYVideoView.CURRENT_STATE_PAUSE -> logVideoEvent("video_game_collect_detail_play", "继续播放")
|
||||
}
|
||||
super.onClick(v)
|
||||
}
|
||||
else -> super.onClick(v)
|
||||
}
|
||||
}
|
||||
|
||||
fun getCurrentPosition(): Long {
|
||||
return mCurrentPosition
|
||||
}
|
||||
|
||||
fun logVideoEvent(event: String, playAction: String = "", stopAction: String = "") {
|
||||
if (video == null || video?.url.isNullOrEmpty()) return
|
||||
val videoPlayTs = currentPositionWhenPlaying / 1000
|
||||
val videoTotalTime = duration / 1000
|
||||
val progress = if (videoTotalTime != 0) videoPlayTs.toFloat() / videoTotalTime.toFloat() * 100 else 0f
|
||||
//https://exoplayer.dev/hello-world.html#a-note-on-threading
|
||||
runOnIoThread {
|
||||
NewLogUtils.logGameCollectionVideoEvent(
|
||||
event,
|
||||
gameCollectionTitle,
|
||||
gameCollectionId,
|
||||
DecimalFormat("#.00").format(progress).toDouble(),
|
||||
videoPlayTs,
|
||||
playAction,
|
||||
stopAction
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,93 @@
|
||||
package com.gh.gamecenter.gamecollection.detail.conversation
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.constraintlayout.widget.ConstraintLayout
|
||||
import androidx.constraintlayout.widget.ConstraintSet
|
||||
import androidx.databinding.DataBindingUtil
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.ItemArticleDetailCommentBinding
|
||||
import com.gh.gamecenter.entity.CommentEntity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailAdapter
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailViewModel
|
||||
|
||||
class GameCollectionCommentConversationAdapter(
|
||||
context: Context,
|
||||
mViewModel: GameCollectionDetailViewModel,
|
||||
type: AdapterType,
|
||||
mEntrance: String,
|
||||
commentClosure: ((CommentEntity) -> Unit)? = null
|
||||
) : GameCollectionDetailAdapter(context, type, mEntrance, mViewModel, commentClosure) {
|
||||
|
||||
var topCommentVH: TopCommentItemViewHolder? = null
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ITEM_COMMENT_TOP -> {
|
||||
val binding: ItemArticleDetailCommentBinding =
|
||||
DataBindingUtil.inflate(mLayoutInflater, R.layout.item_article_detail_comment, parent, false)
|
||||
TopCommentItemViewHolder(binding).apply { topCommentVH = this }
|
||||
}
|
||||
|
||||
else -> super.onCreateViewHolder(parent, viewType)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is TopCommentItemViewHolder -> {
|
||||
holder.bindView(mEntityList[position].commentTop!!)
|
||||
}
|
||||
|
||||
else -> super.onBindViewHolder(holder, position)
|
||||
}
|
||||
}
|
||||
|
||||
inner class TopCommentItemViewHolder(var binding: ItemArticleDetailCommentBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
|
||||
fun bindView(comment: CommentEntity) {
|
||||
ConstraintSet().apply {
|
||||
clone(binding.contentTv.parent as ConstraintLayout)
|
||||
clear(binding.contentTv.id, ConstraintSet.START)
|
||||
connect(binding.contentTv.id, ConstraintSet.START, binding.userIconIv.id, ConstraintSet.START)
|
||||
|
||||
clear(binding.commentPictureRv.id, ConstraintSet.START)
|
||||
connect(binding.commentPictureRv.id, ConstraintSet.START, binding.userIconIv.id, ConstraintSet.START)
|
||||
|
||||
clear(binding.floorHintTv.id, ConstraintSet.START)
|
||||
connect(binding.floorHintTv.id, ConstraintSet.START, binding.userIconIv.id, ConstraintSet.START)
|
||||
|
||||
applyTo(binding.contentTv.parent as ConstraintLayout)
|
||||
}
|
||||
(binding.contentTv.layoutParams as ConstraintLayout.LayoutParams).apply {
|
||||
topMargin = 16F.dip2px()
|
||||
leftMargin = 9F.dip2px()
|
||||
binding.contentTv.layoutParams = this
|
||||
}
|
||||
|
||||
(binding.commentPictureRv.layoutParams as ConstraintLayout.LayoutParams).apply {
|
||||
leftMargin = 9F.dip2px()
|
||||
binding.commentPictureRv.layoutParams = this
|
||||
}
|
||||
|
||||
(binding.floorHintTv.layoutParams as ConstraintLayout.LayoutParams).apply {
|
||||
leftMargin = 9F.dip2px()
|
||||
binding.floorHintTv.layoutParams = this
|
||||
}
|
||||
|
||||
binding.comment = comment
|
||||
binding.moreIv.visibility = View.GONE
|
||||
binding.divider.visibility = View.VISIBLE
|
||||
binding.commentCountTv.visibility = View.GONE
|
||||
binding.floorHintTv.text = if (comment.floor != 0) "${comment.floor}楼" else ""
|
||||
binding.contentTv.text = comment.content
|
||||
binding.contentTv.maxLines = Int.MAX_VALUE
|
||||
|
||||
bindComment(binding, comment)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,171 @@
|
||||
package com.gh.gamecenter.gamecollection.detail.conversation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import com.ethanhua.skeleton.Skeleton
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.syncpage.SyncDataEntity
|
||||
import com.gh.common.syncpage.SyncFieldConstants
|
||||
import com.gh.common.syncpage.SyncPageRepository
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.baselist.LoadType
|
||||
import com.gh.gamecenter.databinding.FragmentArticleDetailCommentBinding
|
||||
import com.gh.gamecenter.entity.CommentEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentAdapter
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentFragment
|
||||
import com.gh.gamecenter.qa.comment.base.BaseCommentViewModel
|
||||
import com.gh.gamecenter.qa.article.detail.CommentItemData
|
||||
import com.gh.gamecenter.qa.comment.CommentActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
|
||||
class GameCollectionCommentConversationFragment : BaseCommentFragment<CommentItemData, GameCollectionCommentConversationViewModel>() {
|
||||
private lateinit var mViewModel: GameCollectionCommentConversationViewModel
|
||||
private lateinit var mBinding: FragmentArticleDetailCommentBinding
|
||||
private var mAdapterCommunity: GameCollectionCommentConversationAdapter? = null
|
||||
|
||||
override fun getLayoutId() = R.layout.fragment_article_detail_comment
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mViewModel = provideListViewModel()
|
||||
super.onCreate(savedInstanceState)
|
||||
mViewModel.getComment()
|
||||
mViewModel.positionInOriginList = arguments?.getInt(EntranceUtils.KEY_POSITION)
|
||||
?: -1
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
initView()
|
||||
initObserver()
|
||||
}
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
return FragmentArticleDetailCommentBinding.inflate(LayoutInflater.from(requireContext()), null, false).apply { mBinding = this }.root
|
||||
}
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<*> {
|
||||
return mAdapterCommunity
|
||||
?: GameCollectionCommentConversationAdapter(requireContext(), mViewModel, BaseCommentAdapter.AdapterType.SUB_COMMENT, mEntrance) {
|
||||
if (it.user.id == UserManager.getInstance().userId) {
|
||||
toast("不能回复自己")
|
||||
} else {
|
||||
startCommentActivity(it)
|
||||
}
|
||||
}.apply {
|
||||
mAdapterCommunity = this
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
val commentCount = data?.getIntExtra(CommentActivity.COMMENT_COUNT, 0)
|
||||
debounceActionWithInterval(Activity.RESULT_OK) {
|
||||
if (commentCount != null && commentCount != 0) {
|
||||
mViewModel.commentDetail?.reply = commentCount
|
||||
mViewModel.commentCount = commentCount
|
||||
mViewModel.load(LoadType.REFRESH)
|
||||
mBinding.toolbarContainer.commentDialogCountTv.text = "${mViewModel.commentDetail?.reply}条回复"
|
||||
SyncPageRepository.postSyncData(SyncDataEntity(mViewModel.commentId, SyncFieldConstants.ARTICLE_COMMENT_REPLY_COUNT, commentCount))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initView() {
|
||||
mSkeletonScreen = Skeleton.bind(skeletonView).shimmer(false).load(R.layout.fragment_article_detail_comment_skeleton).show()
|
||||
|
||||
mBinding.inputContainer.bottomCommentIv.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomCommentTv.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomLikeIv.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomLikeTv.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomStarIv.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomStarTv.visibility = View.GONE
|
||||
|
||||
mBinding.inputContainer.replyTv.setRoundedColorBackground(R.color.text_F0F0F0, 19F)
|
||||
mBinding.inputContainer.replyTv.setDebouncedClickListener {
|
||||
mViewModel.commentDetail?.let { startCommentActivity(it) }
|
||||
}
|
||||
mBinding.toolbarContainer.commentCloseIv.setOnClickListener { requireActivity().finish() }
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
private fun initObserver() {
|
||||
mViewModel.loadResultLiveData.observeNonNull(this) {
|
||||
when (it) {
|
||||
BaseCommentViewModel.LoadResult.SUCCESS -> {
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mListLoading?.visibility = View.GONE
|
||||
|
||||
val showKeyboard = arguments?.getBoolean(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED)
|
||||
?: false
|
||||
if (showKeyboard) {
|
||||
mBinding.inputContainer.replyTv.performClick()
|
||||
}
|
||||
|
||||
mBinding.inputContainer.replyTv.text = "回复:${mViewModel.commentDetail?.user?.name}"
|
||||
mBinding.toolbarContainer.commentDialogCountTv.text = "${mViewModel.commentDetail?.reply}条回复"
|
||||
AppExecutor.uiExecutor.executeWithDelay(Runnable {
|
||||
if ((mAdapterCommunity?.entityList?.size ?: 0) > 2) {
|
||||
mListRv.smoothScrollToPosition(1)
|
||||
}
|
||||
}, 100)
|
||||
}
|
||||
else -> {
|
||||
if (it == BaseCommentViewModel.LoadResult.DELETED) {
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
mReuseNoData?.visibility = View.VISIBLE
|
||||
toast(R.string.content_delete_toast)
|
||||
} else {
|
||||
mReuseNoConn?.visibility = View.VISIBLE
|
||||
mReuseNoData?.visibility = View.GONE
|
||||
mReuseNoConn?.setOnClickListener {
|
||||
mViewModel.getComment()
|
||||
mReuseNoConn?.visibility = View.GONE
|
||||
showSkeleton(true)
|
||||
}
|
||||
}
|
||||
|
||||
mListLoading?.visibility = View.GONE
|
||||
mBinding.inputContainer.bottomContainer.visibility = View.GONE
|
||||
mBinding.bottomShadowView.visibility = View.GONE
|
||||
showSkeleton(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): GameCollectionCommentConversationViewModel {
|
||||
return viewModelProvider(
|
||||
GameCollectionCommentConversationViewModel.Factory(
|
||||
HaloApp.getInstance().application,
|
||||
arguments?.getString(CommentActivity.GAME_COLLECTION_ID) ?: "",
|
||||
arguments?.getString(EntranceUtils.KEY_COMMENT_ID) ?: ""
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBackPressed(): Boolean {
|
||||
requireActivity().finish()
|
||||
return super.onBackPressed()
|
||||
}
|
||||
|
||||
override fun onLoadDone() {
|
||||
showSkeleton(false)
|
||||
super.onLoadDone()
|
||||
}
|
||||
|
||||
private fun startCommentActivity(comment: CommentEntity) {
|
||||
val intent = CommentActivity.getGameCollectionCommentReplyIntent(requireContext(),
|
||||
mViewModel.gameCollectionId,
|
||||
arguments?.getString(EntranceUtils.KEY_COMMENT_ID) ?: "",
|
||||
mViewModel.commentCount,
|
||||
comment)
|
||||
startActivityForResult(intent, CommentActivity.REQUEST_CODE)
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,69 @@
|
||||
package com.gh.gamecenter.gamecollection.detail.conversation
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.ViewModelProvider
|
||||
import com.gh.gamecenter.entity.CommentEntity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailViewModel
|
||||
import com.gh.gamecenter.qa.article.detail.CommentItemData
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import retrofit2.HttpException
|
||||
|
||||
class GameCollectionCommentConversationViewModel(application: Application,
|
||||
gameCollectionId: String = "",
|
||||
var commentId: String = "")
|
||||
: GameCollectionDetailViewModel(application, gameCollectionId) {
|
||||
var commentDetail: CommentEntity? = null
|
||||
var positionInOriginList = -1
|
||||
|
||||
override fun provideDataObservable(page: Int) = null
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<CommentEntity>> {
|
||||
return mApi.getGameCollectionCommentReply(gameCollectionId, commentId, page)
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getComment() {
|
||||
mApi.getGameCollectionComment(gameCollectionId, commentId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<CommentEntity>() {
|
||||
@SuppressLint("CheckResult")
|
||||
override fun onSuccess(data: CommentEntity) {
|
||||
commentDetail = data
|
||||
commentDetail?.floor = positionInOriginList
|
||||
commentCount = data.reply
|
||||
topItemData = CommentItemData(commentTop = data)
|
||||
loadResultLiveData.postValue(LoadResult.SUCCESS)
|
||||
mergeListData(mListLiveData.value, hasFilter = false)
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
if (exception is HttpException && exception.code().toString().contains("404")) {
|
||||
loadResultLiveData.postValue(LoadResult.DELETED)
|
||||
} else {
|
||||
loadResultLiveData.postValue(LoadResult.NETWORK_ERROR)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) { mergeListData(it, hasFilter = false) }
|
||||
}
|
||||
|
||||
|
||||
class Factory(private val application: Application,
|
||||
private val gameCollectionId: String = "",
|
||||
private val commentId: String) : ViewModelProvider.NewInstanceFactory() {
|
||||
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return GameCollectionCommentConversationViewModel(
|
||||
application = application,
|
||||
gameCollectionId = gameCollectionId,
|
||||
commentId = commentId) as T
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,18 @@
|
||||
package com.gh.gamecenter.gamecollection.mine
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
|
||||
class MyGameCollectionActivity : NormalActivity() {
|
||||
|
||||
override fun provideNormalIntent(): Intent {
|
||||
return getTargetIntent(this, MyGameCollectionActivity::class.java, MyGameCollectionFragment::class.java)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun getIntent(context: Context): Intent {
|
||||
return getTargetIntent(context, MyGameCollectionActivity::class.java, MyGameCollectionFragment::class.java)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,74 @@
|
||||
package com.gh.gamecenter.gamecollection.mine
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.toBinding
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
|
||||
class MyGameCollectionAdapter(
|
||||
context: Context,
|
||||
val mViewModel: MyGameCollectionViewModel,
|
||||
val entrance: String,
|
||||
val path: String
|
||||
) : ListAdapter<GamesCollectionEntity>(context) {
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (position == itemCount - 1) {
|
||||
ItemViewType.ITEM_FOOTER
|
||||
} else {
|
||||
ItemViewType.ITEM_BODY
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ItemViewType.ITEM_BODY -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel, entrance, path)
|
||||
ItemViewType.ITEM_FOOTER -> {
|
||||
FooterViewHolder(mLayoutInflater.inflate(R.layout.refresh_footerview, parent, false))
|
||||
}
|
||||
else -> MyGameCollectionViewHolder(parent.toBinding(), mViewModel, entrance, path)
|
||||
}
|
||||
}
|
||||
|
||||
override fun areItemsTheSame(oldItem: GamesCollectionEntity?, newItem: GamesCollectionEntity?): Boolean {
|
||||
return oldItem == newItem || oldItem?.id == newItem?.id
|
||||
}
|
||||
|
||||
override fun areContentsTheSame(oldItem: GamesCollectionEntity?, newItem: GamesCollectionEntity?): Boolean {
|
||||
return oldItem?.id == newItem?.id
|
||||
&& oldItem?.title == newItem?.title
|
||||
&& oldItem?.intro == newItem?.intro
|
||||
&& oldItem?.cover == newItem?.cover
|
||||
&& oldItem?.display == newItem?.display
|
||||
&& oldItem?.status == newItem?.status
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is MyGameCollectionViewHolder -> {
|
||||
val entity = mEntityList[position]
|
||||
holder.bindGameCollectionItem(entity)
|
||||
}
|
||||
is FooterViewHolder -> {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(mIsLoading, mIsNetworkError, mIsOver)
|
||||
holder.hint.setTextColor(ContextCompat.getColor(mContext, R.color.aaaaaa))
|
||||
val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams
|
||||
lp.height = DisplayUtils.dip2px(48F)
|
||||
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
holder.itemView.layoutParams = lp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return if (mEntityList == null || mEntityList.isEmpty()) return 0 else mEntityList.size + 1
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,105 @@
|
||||
package com.gh.gamecenter.gamecollection.mine
|
||||
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.common.util.showRegulationTestDialogIfNeeded
|
||||
import com.gh.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.baselist.ListFragment
|
||||
import com.gh.gamecenter.baselist.LoadType
|
||||
import com.gh.gamecenter.databinding.FragmentMyGameCollectionListBinding
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.eventbus.EBReuse
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionSquareActivity
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
|
||||
class MyGameCollectionFragment : ListFragment<GamesCollectionEntity, MyGameCollectionViewModel>() {
|
||||
|
||||
private var mAdapter: MyGameCollectionAdapter? = null
|
||||
private lateinit var mBinding: FragmentMyGameCollectionListBinding
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout(): View {
|
||||
return FragmentMyGameCollectionListBinding.inflate(layoutInflater, null, false).apply {
|
||||
mBinding = this
|
||||
}.root
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(menuItem: MenuItem?) {
|
||||
super.onMenuItemClick(menuItem)
|
||||
menuItem?.run {
|
||||
if (itemId == R.id.menu_game_collection_square) {
|
||||
startActivity(Intent(requireContext(), GameCollectionSquareActivity::class.java))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<*> {
|
||||
return mAdapter ?: MyGameCollectionAdapter(requireContext(), mListViewModel, mEntrance, "我的游戏单").apply {
|
||||
mAdapter = this
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
NewLogUtils.logEnterMyGameCollection()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
setNavigationTitle("我的游戏单")
|
||||
initMenu(R.menu.menu_my_game_collection)
|
||||
|
||||
mListViewModel.deleteLiveData.observe(viewLifecycleOwner) {
|
||||
val index = mAdapter?.entityList?.indexOf(it) ?: -1
|
||||
mAdapter?.entityList?.removeAt(index)
|
||||
if (mAdapter?.entityList?.isNullOrEmpty() == true) {
|
||||
onLoadEmpty()
|
||||
} else {
|
||||
mAdapter?.notifyItemRemoved(index)
|
||||
}
|
||||
|
||||
toast("删除成功")
|
||||
}
|
||||
|
||||
mListViewModel.publishLiveData.observe(viewLifecycleOwner) {
|
||||
mListViewModel.load(LoadType.REFRESH)
|
||||
}
|
||||
|
||||
mBinding.createBtn.setOnClickListener {
|
||||
showRegulationTestDialogIfNeeded {
|
||||
startActivity(GameCollectionEditActivity.getIntent(requireContext(), mEntrance, "我的游戏单"))
|
||||
}
|
||||
}
|
||||
|
||||
mBinding.reuseNoneDataIv.setOnClickListener {
|
||||
mBinding.createBtn.performClick()
|
||||
}
|
||||
|
||||
mBinding.specificationLinkTv.setOnClickListener {
|
||||
startActivity(WebActivity.getWebIntent(requireContext(), "游戏单管理规范", Constants.GAME_COLLECTION_RULE))
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemDecoration(): RecyclerView.ItemDecoration {
|
||||
return VerticalItemDecoration(requireContext(), 16F, true, R.color.white)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(changed: EBReuse) {
|
||||
if (changed.type == "createGameCollectionSuccess") {
|
||||
mListViewModel.load(LoadType.REFRESH)
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,87 @@
|
||||
package com.gh.gamecenter.gamecollection.mine
|
||||
|
||||
import android.view.LayoutInflater
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.util.DialogHelper
|
||||
import com.gh.common.util.goneIf
|
||||
import com.gh.gamecenter.databinding.ItemGameCollectionFlexTagBinding
|
||||
import com.gh.gamecenter.databinding.ItemMyGameCollectionBinding
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
|
||||
class MyGameCollectionViewHolder(
|
||||
val binding: ItemMyGameCollectionBinding,
|
||||
val mViewModel: MyGameCollectionViewModel,
|
||||
val entrance: String,
|
||||
val path: String
|
||||
) :
|
||||
BaseRecyclerViewHolder<GamesCollectionEntity>(binding.root) {
|
||||
|
||||
fun bindGameCollectionItem(entity: GamesCollectionEntity) {
|
||||
binding.entity = entity
|
||||
initTagsUI(entity.tags ?: arrayListOf())
|
||||
val statusLabelRes = entity.getStatusLabelRes()
|
||||
if (statusLabelRes > 0) {
|
||||
binding.statusView.setImageResource(statusLabelRes)
|
||||
}
|
||||
|
||||
binding.statusView.setOnClickListener {
|
||||
when {
|
||||
entity.display == "self_only" && entity.status == "draft" -> {
|
||||
DialogHelper.showDialog(binding.root.context, "仅自己可见", "游戏单开启“仅自己可见”后,将不会对其他用户展示,您可以通过关闭仅自己可见或者投稿分享游戏单", "我知道了", "", {
|
||||
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
entity.status == "pending" -> {
|
||||
DialogHelper.showDialog(binding.root.context, "审核中", "游戏单会在1-2个工作日内审核完成,请您耐心等候", "我知道了", "", {
|
||||
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
entity.status == "failed" -> {
|
||||
DialogHelper.showDialog(binding.root.context, "未通过", "您的游戏单可能含有违规内容或语意不明确,请您仔细检查后再次尝试提交", "我知道了", "", {
|
||||
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
binding.root.setOnClickListener {
|
||||
if (entity.status == "pass" || (entity.display == "self_only" && entity.status == "draft")) {
|
||||
binding.root.context.startActivity(GameCollectionDetailActivity.getIntent(binding.root.context, entity.id))
|
||||
} else {
|
||||
binding.statusView.performClick()
|
||||
}
|
||||
}
|
||||
|
||||
binding.publishBtn.setOnClickListener {
|
||||
if ((entity.count?.game ?: 0) >= 8) {
|
||||
DialogHelper.showDialog(binding.root.context, "温馨提示", "投稿通过后,将自动关闭“仅自己可见”,所有用户都能浏览到游戏单,确定投稿?", "确定", "取消", {
|
||||
mViewModel.publishGameCollection(entity)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
} else {
|
||||
DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场哦~", "添加游戏", "我知道了", {
|
||||
binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity, entrance, path))
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
binding.editBtn.setOnClickListener {
|
||||
binding.root.context.startActivity(GameCollectionEditActivity.getIntent(binding.root.context, entity, entrance, path))
|
||||
}
|
||||
binding.deleteBtn.setOnClickListener {
|
||||
DialogHelper.showDialog(binding.root.context, "温馨提示", "游戏单删除后将无法恢复,是否确认删除", "确定", "取消", {
|
||||
mViewModel.deleteGameCollection(entity)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
|
||||
private fun initTagsUI(tags: ArrayList<TagInfoEntity>) {
|
||||
binding.gameCollectionTagsContainer.removeAllViews()
|
||||
tags.forEachIndexed { index, tag ->
|
||||
val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(binding.root.context), null, false)
|
||||
tagBinding.tagNameTv.text = tag.name
|
||||
tagBinding.divider.goneIf(index == tags.size - 1)
|
||||
binding.gameCollectionTagsContainer.addView(tagBinding.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,60 @@
|
||||
package com.gh.gamecenter.gamecollection.mine
|
||||
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.common.util.ToastUtils
|
||||
import com.gh.common.util.observableToMain
|
||||
import com.gh.gamecenter.baselist.ListViewModel
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.halo.assistant.HaloApp
|
||||
import io.reactivex.Observable
|
||||
import okhttp3.ResponseBody
|
||||
import retrofit2.HttpException
|
||||
|
||||
class MyGameCollectionViewModel(application: Application) : ListViewModel<GamesCollectionEntity, GamesCollectionEntity>(application) {
|
||||
private val mApi = RetrofitManager.getInstance(HaloApp.getInstance()).api
|
||||
val deleteLiveData = MutableLiveData<GamesCollectionEntity>()
|
||||
val publishLiveData = MutableLiveData<GamesCollectionEntity>()
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<GamesCollectionEntity>>? {
|
||||
return mApi.getGameCollectionList(UserManager.getInstance().userId)
|
||||
}
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) { mResultLiveData.postValue(it) }
|
||||
}
|
||||
|
||||
fun deleteGameCollection(entity: GamesCollectionEntity) {
|
||||
mApi.deleteGameCollection(entity.id)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
deleteLiveData.postValue(entity)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
ToastUtils.showToast("删除失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun publishGameCollection(entity: GamesCollectionEntity){
|
||||
mApi.deleteGameCollection(entity.id)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
publishLiveData.postValue(entity)
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
ToastUtils.showToast("投稿失败")
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,439 @@
|
||||
package com.gh.gamecenter.gamecollection.publish
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.core.widget.doOnTextChanged
|
||||
import com.gh.base.ToolBarActivity
|
||||
import com.gh.base.fragment.WaitingDialogFragment
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.CropImageActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.WebActivity
|
||||
import com.gh.gamecenter.databinding.ActivityGameCollectionEditBinding
|
||||
import com.gh.gamecenter.databinding.ItemGameCollectionFlexTagBinding
|
||||
import com.gh.gamecenter.entity.GameCollectionDraft
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.eventbus.EBReuse
|
||||
import com.gh.gamecenter.gamecollection.choose.ChooseGamesActivity
|
||||
import com.gh.gamecenter.gamecollection.choose.ChooseGamesViewModel
|
||||
import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity
|
||||
import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectFragment
|
||||
import com.gh.gamecenter.mvvm.Status
|
||||
import com.gh.gamecenter.qa.editor.LocalMediaActivity
|
||||
import com.zhihu.matisse.Matisse
|
||||
import com.zhihu.matisse.internal.utils.PathUtils
|
||||
import org.greenrobot.eventbus.EventBus
|
||||
|
||||
class GameCollectionEditActivity : ToolBarActivity() {
|
||||
|
||||
private lateinit var mMenuPost: MenuItem
|
||||
private lateinit var mBinding: ActivityGameCollectionEditBinding
|
||||
private lateinit var mViewModel: GameCollectionEditViewModel
|
||||
private lateinit var mChooseGamesViewModel: ChooseGamesViewModel
|
||||
private var mProcessingDialog: WaitingDialogFragment? = null
|
||||
private var mPatchCommitCount = 0
|
||||
|
||||
override fun getLayoutId(): Int = R.layout.activity_game_collection_edit
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mBinding = ActivityGameCollectionEditBinding.bind(mContentView)
|
||||
mViewModel = viewModelProvider()
|
||||
mChooseGamesViewModel = viewModelProvider(ChooseGamesViewModel.Factory())
|
||||
setToolbarMenu(R.menu.menu_game_collection_edit)
|
||||
mMenuPost = getMenuItem(R.id.menu_game_collection_post)
|
||||
setNavigationTitle("创建游戏单")
|
||||
mViewModel.gameCollectionPatch = intent.getParcelableExtra(GamesCollectionEntity::class.java.name)
|
||||
val path = intent.getStringExtra(EntranceUtils.KEY_PATH) ?: ""
|
||||
if (path.isNotEmpty()) {
|
||||
NewLogUtils.logEnterGameCollectionEdit(path)
|
||||
}
|
||||
|
||||
initData()
|
||||
observeData()
|
||||
initListener()
|
||||
}
|
||||
|
||||
private fun initListener() {
|
||||
mBinding.gameCollectionTitleEt.run {
|
||||
doOnTextChanged { text, start, _, _ ->
|
||||
if (text?.contains("\n") == true) {
|
||||
setText(text.toString().replace("\n", ""))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
if (PatternUtils.isHasSpace(text.toString())) {
|
||||
setText(PatternUtils.replaceSpace(text.toString()))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
mBinding.gameCollectionIntroduceEt.run {
|
||||
doOnTextChanged { text, start, _, _ ->
|
||||
if (PatternUtils.isHasWrap(text.toString())) {
|
||||
setText(PatternUtils.replaceWrap(text.toString()))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
if (PatternUtils.isHasSpace(text.toString())) {
|
||||
setText(PatternUtils.replaceSpace(text.toString()))
|
||||
setSelection(start)
|
||||
return@doOnTextChanged
|
||||
}
|
||||
}
|
||||
}
|
||||
mBinding.uploadPictureBtn.setOnClickListener {
|
||||
PermissionHelper.checkStoragePermissionBeforeAction(
|
||||
this,
|
||||
object : EmptyCallback {
|
||||
override fun onCallback() {
|
||||
startActivityForResult(
|
||||
LocalMediaActivity.getIntent(
|
||||
this@GameCollectionEditActivity,
|
||||
LocalMediaActivity.ChooseType.IMAGE,
|
||||
1,
|
||||
"创建游戏单"
|
||||
), REQUEST_CODE_IMAGE
|
||||
)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mBinding.changePosterBtn.setOnClickListener { mBinding.uploadPictureBtn.performClick() }
|
||||
|
||||
mBinding.deleteBtn.setOnClickListener {
|
||||
mViewModel.imageUrl = ""
|
||||
mViewModel.imagePath = ""
|
||||
mBinding.uploadPictureBtn.text = "点击上传图片"
|
||||
initPosterUI()
|
||||
}
|
||||
|
||||
mBinding.gameCollectionIntroduceEt.doOnTextChanged { text, _, _, _ ->
|
||||
mBinding.introduceSizeTv.text = "${text?.length ?: 0}/200"
|
||||
}
|
||||
|
||||
mBinding.chooseLabelContainer.setOnClickListener {
|
||||
startActivityForResult(GameCollectionTagSelectActivity.getIntent(this), REQUEST_CHOOSE_TAG)
|
||||
}
|
||||
|
||||
mBinding.chooseGameContainer.setOnClickListener {
|
||||
startActivity(ChooseGamesActivity.getIntent(this))
|
||||
}
|
||||
mBinding.normsLinkTv.setOnClickListener {
|
||||
startActivity(WebActivity.getWebIntent(this, "游戏单管理规范", Constants.GAME_COLLECTION_RULE))
|
||||
}
|
||||
}
|
||||
|
||||
private fun initData(isFirst: Boolean = true) {
|
||||
mBinding.gameCollectionTitleEt.filters = arrayOf(TextHelper.getFilter(20, "最多输入20个字"))
|
||||
mBinding.gameCollectionIntroduceEt.filters = arrayOf(TextHelper.getFilter(200, "最多输入200个字"))
|
||||
initTagsUI(mViewModel.tags)
|
||||
|
||||
mViewModel.gameCollectionPatch?.run {
|
||||
mViewModel.imageUrl = cover
|
||||
if (status.isNotEmpty() && (status != "draft" || display == "self_only")) {
|
||||
setNavigationTitle("编辑游戏单")
|
||||
}
|
||||
initPosterUI()
|
||||
mBinding.gameCollectionTitleEt.setText(title)
|
||||
mBinding.gameCollectionTitleEt.setSelection(title.length)
|
||||
mBinding.gameCollectionIntroduceEt.setText(intro)
|
||||
mBinding.gameCollectionIntroduceEt.setSelection(intro.length)
|
||||
mBinding.selfOnlyCb.isChecked = display == "self_only"
|
||||
if (isFirst) {
|
||||
mViewModel.getGameCollectionDetail(id)
|
||||
}
|
||||
} ?: mViewModel.getDraft()
|
||||
}
|
||||
|
||||
private fun observeData() {
|
||||
mChooseGamesViewModel.chooseGamesLiveData.observe(this) {
|
||||
mBinding.gamesTv.text = if (it.isEmpty()) "选择游戏" else "已选 ${it.size} 款游戏"
|
||||
mBinding.gamesTipTv.goneIf(it.isNotEmpty())
|
||||
}
|
||||
mViewModel.processDialog.observe(this) {
|
||||
if (it.isShow) {
|
||||
if (mProcessingDialog?.dialog?.isShowing == true) {
|
||||
mProcessingDialog?.uploadWaitingHint(it.msg)
|
||||
} else {
|
||||
mProcessingDialog = WaitingDialogFragment.newInstance(it.msg, false)
|
||||
mProcessingDialog?.show(supportFragmentManager, null)
|
||||
}
|
||||
} else {
|
||||
mProcessingDialog?.dismiss()
|
||||
}
|
||||
}
|
||||
mViewModel.uploadImageSuccessLiveData.observe(this) {
|
||||
if (it.isNotEmpty()) {
|
||||
initPosterUI()
|
||||
}
|
||||
mBinding.uploadPictureBtn.text = "点击上传图片"
|
||||
}
|
||||
mViewModel.postLiveData.observe(this) {
|
||||
if (it.status == Status.SUCCESS) {
|
||||
toast("提交成功")
|
||||
EventBus.getDefault().post(EBReuse("createGameCollectionSuccess"))
|
||||
finish()
|
||||
} else {
|
||||
ErrorHelper.handleError(this, it.exception?.response()?.errorBody()?.string())
|
||||
}
|
||||
}
|
||||
mViewModel.detailLiveData.observe(this) {
|
||||
mViewModel.gameCollectionPatch?.apply {
|
||||
games = it.games
|
||||
tags = it.tags
|
||||
title = it.title
|
||||
intro = it.intro
|
||||
}
|
||||
mViewModel.gameCollectionPatch?.run {
|
||||
initData(false)
|
||||
mViewModel.tags = tags ?: arrayListOf()
|
||||
initTagsUI(mViewModel.tags)
|
||||
val simpleGames = games?.map { game -> game.toGameEntity() }?.toList()
|
||||
mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames))
|
||||
}
|
||||
}
|
||||
mViewModel.draftLiveData.observe(this) {
|
||||
mViewModel.gameCollectionPatch = it.convertGameCollectionEntity()
|
||||
mViewModel.gameCollectionPatch?.run {
|
||||
initData(false)
|
||||
mViewModel.tags = tags ?: arrayListOf()
|
||||
initTagsUI(mViewModel.tags)
|
||||
val simpleGames = games?.map { game -> game.toGameEntity() }
|
||||
mChooseGamesViewModel.chooseGamesLiveData.postValue(ArrayList(simpleGames))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
item?.run {
|
||||
if (itemId == R.id.menu_game_collection_post) {
|
||||
debounceActionWithInterval(R.id.menu_game_collection_post, 3000L) {
|
||||
verifyData()
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.onMenuItemClick(item)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (data == null || resultCode != Activity.RESULT_OK) return
|
||||
when (requestCode) {
|
||||
REQUEST_CODE_IMAGE -> {
|
||||
val selectedPaths = Matisse.obtainResult(data)
|
||||
if (!selectedPaths.isNullOrEmpty()) {
|
||||
val path = PathUtils.getPath(this, selectedPaths[0])
|
||||
val intent = CropImageActivity.getIntent(this, path, 142 / 328F, false, mEntrance)
|
||||
startActivityForResult(intent, REQUEST_CODE_IMAGE_CROP)
|
||||
}
|
||||
}
|
||||
REQUEST_CODE_IMAGE_CROP -> {
|
||||
val imagePath = data.getStringExtra(CropImageActivity.RESULT_CLIP_PATH) ?: ""
|
||||
mViewModel.imageUrl = ""
|
||||
mViewModel.imagePath = imagePath
|
||||
if (imagePath.isEmpty()) {
|
||||
mBinding.uploadPictureBtn.text = "点击上传图片"
|
||||
} else {
|
||||
mBinding.uploadPictureBtn.text = "图片上传中..."
|
||||
mViewModel.uploadPoster()
|
||||
}
|
||||
initPosterUI()
|
||||
}
|
||||
REQUEST_CHOOSE_TAG -> {
|
||||
val tags = data.getParcelableArrayListExtra<TagInfoEntity>(GameCollectionTagSelectFragment.SELECTED_TAG) ?: arrayListOf()
|
||||
mViewModel.tags = tags
|
||||
initTagsUI(tags)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun initPosterUI() {
|
||||
mBinding.placeholderView.goneIf(mViewModel.imageUrl.isNotEmpty())
|
||||
mBinding.uploadPictureBtn.goneIf(mViewModel.imageUrl.isNotEmpty())
|
||||
mBinding.posterView.goneIf(mViewModel.imageUrl.isEmpty())
|
||||
mBinding.changePosterBtn.goneIf(mViewModel.imageUrl.isEmpty())
|
||||
mBinding.deleteBtn.goneIf(mViewModel.imageUrl.isEmpty())
|
||||
if (mViewModel.imageUrl.isNotEmpty()) {
|
||||
ImageUtils.display(mBinding.posterView, mViewModel.imageUrl)
|
||||
}
|
||||
}
|
||||
|
||||
private fun initTagsUI(tags: ArrayList<TagInfoEntity>) {
|
||||
mBinding.gameCollectionTagsContainer.removeAllViews()
|
||||
mBinding.labelTipTv.goneIf(tags.isNotEmpty())
|
||||
if (tags.isEmpty()) {
|
||||
val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(this), null, false)
|
||||
tagBinding.tagNameTv.run {
|
||||
text = "选择标签"
|
||||
setTextColor(R.color.text_333333.toColor())
|
||||
textSize = 14f
|
||||
}
|
||||
tagBinding.divider.visibility = View.GONE
|
||||
mBinding.gameCollectionTagsContainer.addView(tagBinding.root)
|
||||
} else {
|
||||
tags.forEachIndexed { index, tag ->
|
||||
val tagBinding = ItemGameCollectionFlexTagBinding.inflate(LayoutInflater.from(this), null, false)
|
||||
tagBinding.tagNameTv.run {
|
||||
text = tag.name
|
||||
setTextColor(R.color.text_333333.toColor())
|
||||
textSize = 14f
|
||||
}
|
||||
tagBinding.divider.run {
|
||||
alpha = 0.2f
|
||||
setBackgroundColor(R.color.text_333333.toColor())
|
||||
goneIf(index == tags.size - 1)
|
||||
setPadding(8f.dip2px(), 0, 8f.dip2px(), 0)
|
||||
}
|
||||
mBinding.gameCollectionTagsContainer.addView(tagBinding.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun handleBackPressed(): Boolean {
|
||||
val patch = mViewModel.gameCollectionPatch
|
||||
if (patch != null && patch.status.isNotEmpty() && (patch.status != "draft" || patch.display == "self_only")) {
|
||||
DialogHelper.showDialog(this, "温馨提示", "退出将不会保留本次游戏单编辑的内容,是否确定退出", "确定", "取消", {
|
||||
finish()
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
return true
|
||||
} else {
|
||||
val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
val title = mBinding.gameCollectionTitleEt.text.toString()
|
||||
val introduce = mBinding.gameCollectionIntroduceEt.text.toString()
|
||||
if (mViewModel.imageUrl.isNotEmpty()
|
||||
|| mViewModel.tags.isNotEmpty()
|
||||
|| games.isNotEmpty()
|
||||
|| title.isNotEmpty()
|
||||
|| introduce.isNotEmpty()
|
||||
) {
|
||||
val entity = GameCollectionDraft()
|
||||
entity.tags = mViewModel.tags
|
||||
entity.title = title
|
||||
entity.intro = introduce
|
||||
entity.cover = mViewModel.imageUrl
|
||||
entity.display = if (mBinding.selfOnlyCb.isChecked) "self_only" else ""
|
||||
entity.games = arrayListOf()
|
||||
entity.games?.addAll(games.map { it.toSimpleGame() }.toList())
|
||||
mViewModel.addDraft(entity)
|
||||
}
|
||||
}
|
||||
return super.handleBackPressed()
|
||||
}
|
||||
|
||||
private fun verifyData() {
|
||||
if (mViewModel.imageUrl.isEmpty()) {
|
||||
toast("请上传游戏单的封面")
|
||||
return
|
||||
}
|
||||
if (mViewModel.tags.isEmpty()) {
|
||||
toast("请选择游戏单的标签")
|
||||
return
|
||||
}
|
||||
val games = mChooseGamesViewModel.chooseGamesLiveData.value ?: arrayListOf()
|
||||
|
||||
val title = mBinding.gameCollectionTitleEt.text.toString().trim()
|
||||
if (title.isEmpty()) {
|
||||
toast("请填写游戏单的标题")
|
||||
return
|
||||
}
|
||||
val introduce = mBinding.gameCollectionIntroduceEt.text.toString().trim()
|
||||
if (introduce.length < 10) {
|
||||
toast("介绍至少10个字")
|
||||
return
|
||||
}
|
||||
|
||||
val isSelfOnly = mBinding.selfOnlyCb.isChecked
|
||||
val requestMap = hashMapOf(
|
||||
"id" to (mViewModel.gameCollectionPatch?.id ?: ""),
|
||||
"title" to title,
|
||||
"intro" to introduce,
|
||||
"tag_ids" to mViewModel.tags.map { it.id }.toList(),
|
||||
"cover" to mViewModel.imageUrl,
|
||||
"display" to if (isSelfOnly) "self_only" else "",
|
||||
"games" to games.map {
|
||||
hashMapOf<String, Any>(
|
||||
"_id" to it.id,
|
||||
"recommend_star" to it.recommendStar,
|
||||
"recommend_text" to it.recommendText,
|
||||
)
|
||||
}.toList()
|
||||
)
|
||||
|
||||
val patch = mViewModel.gameCollectionPatch
|
||||
if (patch != null
|
||||
&& patch.status.isNotEmpty()
|
||||
&& patch.status != "draft"
|
||||
&& games.size < 8
|
||||
) {
|
||||
if (mPatchCommitCount < 2) {
|
||||
toast("游戏单收录的游戏不能少于8款")
|
||||
mPatchCommitCount++
|
||||
return
|
||||
} else {
|
||||
if (!mBinding.selfOnlyCb.isChecked) {
|
||||
toast("没想好收录的游戏,开启仅自己可见试试")
|
||||
mPatchCommitCount++
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (isSelfOnly) {
|
||||
DialogHelper.showDialog(this, "温馨提示", "游戏单开启“仅自己可见”后,将不会对其他用户展示,是否继续提交", "确定", "取消", {
|
||||
mViewModel.uploadContent(requestMap)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
} else {
|
||||
if (games.size < 8) {
|
||||
DialogHelper.showDialog(this, "温馨提示", "游戏单需要收录至少8个游戏,才可以投稿至游戏单广场,是否在“我的光环-我的游戏单”继续保存为草稿", "继续保存", "添加游戏", {
|
||||
mViewModel.uploadContent(requestMap)
|
||||
}, {
|
||||
startActivity(ChooseGamesActivity.getIntent(this))
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
} else {
|
||||
DialogHelper.showDialog(this, "温馨提示", "游戏单会在1-2个工作日内审核完成,您可以在“我的光环-我的游戏单”查看进度", "继续提交", "取消", {
|
||||
mViewModel.uploadContent(requestMap)
|
||||
}, extraConfig = DialogHelper.Config(centerTitle = true, centerContent = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mChooseGamesViewModel.chooseGamesLiveData.postValue(arrayListOf())
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
|
||||
const val REQUEST_CODE_IMAGE = 100
|
||||
const val REQUEST_CODE_IMAGE_CROP = 101
|
||||
const val REQUEST_CHOOSE_GAMES = 102
|
||||
const val REQUEST_CHOOSE_TAG = 103
|
||||
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, entrance: String = "", path: String = ""): Intent {
|
||||
val intent = Intent(context, GameCollectionEditActivity::class.java)
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path))
|
||||
intent.putExtra(EntranceUtils.KEY_PATH, path)
|
||||
return intent
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, entity: GamesCollectionEntity, entrance: String = "", path: String = ""): Intent {
|
||||
val intent = Intent(context, GameCollectionEditActivity::class.java)
|
||||
intent.putExtra(GamesCollectionEntity::class.java.name, entity)
|
||||
intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path))
|
||||
intent.putExtra(EntranceUtils.KEY_PATH, path)
|
||||
return intent
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,139 @@
|
||||
package com.gh.gamecenter.gamecollection.publish
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MediatorLiveData
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.base.fragment.WaitingDialogFragment
|
||||
import com.gh.common.runOnIoThread
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.entity.GameCollectionDraft
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.mvvm.Resource
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import com.gh.gamecenter.room.AppDatabase
|
||||
import com.halo.assistant.HaloApp
|
||||
import okhttp3.ResponseBody
|
||||
import org.json.JSONObject
|
||||
import retrofit2.HttpException
|
||||
|
||||
class GameCollectionEditViewModel(application: Application) : AndroidViewModel(application) {
|
||||
var imagePath = ""
|
||||
var imageUrl = ""
|
||||
var tags = ArrayList<TagInfoEntity>()
|
||||
var gameCollectionPatch: GamesCollectionEntity? = null
|
||||
var uploadImageSuccessLiveData = MutableLiveData<String>()
|
||||
var detailLiveData = MutableLiveData<GamesCollectionEntity>()
|
||||
var draftLiveData = MutableLiveData<GameCollectionDraft>()
|
||||
val postLiveData = MutableLiveData<Resource<String>>()
|
||||
val processDialog = MediatorLiveData<WaitingDialogFragment.WaitingDialogData>()
|
||||
private val mApi = RetrofitManager.getInstance(HaloApp.getInstance()).api
|
||||
private val mDraftDao = AppDatabase.getInstance(HaloApp.getInstance()).gameCollectionDraftDao()
|
||||
|
||||
fun uploadPoster() {
|
||||
if (imagePath.isEmpty()) return
|
||||
UploadImageUtils.uploadImage(UploadImageUtils.UploadType.poster, imagePath, object : UploadImageUtils.OnUploadImageListener {
|
||||
override fun onSuccess(imageUrl: String) {
|
||||
this@GameCollectionEditViewModel.imageUrl = imageUrl
|
||||
uploadImageSuccessLiveData.postValue(imageUrl)
|
||||
}
|
||||
|
||||
override fun onError(e: Throwable?) {
|
||||
uploadImageSuccessLiveData.postValue("")
|
||||
ToastUtils.showToast("图片上传失败")
|
||||
}
|
||||
|
||||
override fun onProgress(total: Long, progress: Long) {}
|
||||
})
|
||||
}
|
||||
|
||||
fun uploadContent(requestMap: HashMap<String, Any>) {
|
||||
val id = requestMap["id"]?.toString() ?: ""
|
||||
requestMap.remove("id")
|
||||
postContent(requestMap, id)
|
||||
}
|
||||
|
||||
private fun postContent(requestMap: HashMap<String, Any>, gameCollectionId: String) {
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true))
|
||||
val requestBody = requestMap.toRequestBody()
|
||||
val observable = if (gameCollectionId.isEmpty()) {
|
||||
mApi.createGameCollection(requestBody)
|
||||
} else {
|
||||
mApi.patchGameCollection(requestBody, gameCollectionId)
|
||||
}
|
||||
observable
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<ResponseBody>() {
|
||||
override fun onResponse(response: ResponseBody?) {
|
||||
super.onResponse(response)
|
||||
val data = response?.string()
|
||||
val id = if (!data.isNullOrEmpty()) {
|
||||
JSONObject(data).optString("_id")
|
||||
} else ""
|
||||
postLiveData.postValue(Resource.success(id))
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false))
|
||||
deleteAllDraft()
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
postLiveData.postValue(Resource.error(e))
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun getGameCollectionDetail(gameCollectionId: String) {
|
||||
if (gameCollectionId.isEmpty()) return
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("加载中...", true))
|
||||
mApi.getGameCollectionDetailForDraft(gameCollectionId)
|
||||
.compose(observableToMain())
|
||||
.subscribe(object : Response<GamesCollectionEntity>() {
|
||||
override fun onResponse(response: GamesCollectionEntity?) {
|
||||
super.onResponse(response)
|
||||
if (response != null) {
|
||||
detailLiveData.postValue(response)
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(e: HttpException?) {
|
||||
super.onFailure(e)
|
||||
processDialog.postValue(WaitingDialogFragment.WaitingDialogData("", false))
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getDraft() {
|
||||
mDraftDao.getAllDraft()
|
||||
.compose(singleToMain())
|
||||
.subscribe(object : BiResponse<List<GameCollectionDraft>>() {
|
||||
override fun onSuccess(data: List<GameCollectionDraft>) {
|
||||
if (data.isNotEmpty()) {
|
||||
val draftEntity = data[0]
|
||||
draftLiveData.postValue(draftEntity)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
fun addDraft(entity: GameCollectionDraft) {
|
||||
runOnIoThread {
|
||||
val drafts = mDraftDao.getDrafts()
|
||||
mDraftDao.delete(drafts)
|
||||
mDraftDao.addDraft(entity)
|
||||
}
|
||||
}
|
||||
|
||||
fun deleteAllDraft(){
|
||||
runOnIoThread {
|
||||
val drafts = mDraftDao.getDrafts()
|
||||
mDraftDao.delete(drafts)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,54 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.TextHelper
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.GameCollectionAmwayContentItemBinding
|
||||
import com.gh.gamecenter.entity.AmwayCommentEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class GameCollectionAmwayAdapter(context: Context) :
|
||||
BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private var mAmwayList = listOf<AmwayCommentEntity>()
|
||||
|
||||
fun setAmwayList(amwayList: List<AmwayCommentEntity>) {
|
||||
mAmwayList = amwayList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
|
||||
GameCollectionAmwayContentItemViewHolder(GameCollectionAmwayContentItemBinding.inflate(mLayoutInflater, parent, false))
|
||||
|
||||
override fun getItemCount() = if (getRealCount() > 1) getRealCount() + INCREASE_COUNT else getRealCount()
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is GameCollectionAmwayContentItemViewHolder) {
|
||||
holder.binding.entity = mAmwayList[getRealPosition(position)]
|
||||
holder.binding.amwayTv.text = TextHelper.getCommentLabelSpannableStringBuilder(mAmwayList[getRealPosition(position)].comment.content, R.color.white)
|
||||
}
|
||||
}
|
||||
|
||||
class GameCollectionAmwayContentItemViewHolder(var binding: GameCollectionAmwayContentItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
fun getRealCount(): Int = mAmwayList.size
|
||||
|
||||
fun getRealPosition(position: Int) = when (position) {
|
||||
0 -> {
|
||||
getRealCount() - 1
|
||||
}
|
||||
getRealCount() + 1 -> {
|
||||
0
|
||||
}
|
||||
else -> {
|
||||
position - 1
|
||||
}
|
||||
}
|
||||
|
||||
companion object{
|
||||
const val INCREASE_COUNT = 2
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,114 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.animation.Animator
|
||||
import android.animation.TimeInterpolator
|
||||
import android.animation.ValueAnimator
|
||||
import android.view.animation.AccelerateDecelerateInterpolator
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import androidx.viewpager2.widget.ViewPager2
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding
|
||||
import com.gh.gamecenter.entity.AmwayCommentEntity
|
||||
import java.lang.ref.WeakReference
|
||||
|
||||
class GameCollectionAmwayViewHolder(var binding: GameCollectionSquareAmwayItemBinding) : RecyclerView.ViewHolder(binding.root) {
|
||||
val mAdapter = GameCollectionAmwayAdapter(binding.root.context)
|
||||
val mLoopTask = object : Runnable {
|
||||
private val reference: WeakReference<ViewPager2> = WeakReference(binding.amwayVp)
|
||||
override fun run() {
|
||||
val vp: ViewPager2? = reference.get()
|
||||
if (vp != null) {
|
||||
val count = mAdapter.itemCount
|
||||
if (count == 0) return
|
||||
val next = (vp.currentItem + 1) % count
|
||||
vp.setCurrentItem(next, 1000)
|
||||
vp.postDelayed(this, AMWAY_LOOP_TIME)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun bindAmway(amwayList: List<AmwayCommentEntity>) {
|
||||
mAdapter.setAmwayList(amwayList)
|
||||
binding.amwayVp.run {
|
||||
if (adapter is GameCollectionAmwayAdapter) return
|
||||
isUserInputEnabled = false
|
||||
adapter = mAdapter
|
||||
orientation = ViewPager2.ORIENTATION_VERTICAL
|
||||
registerOnPageChangeCallback(object : ViewPager2.OnPageChangeCallback() {
|
||||
private var mTempPosition = GameCollectionSquareAdapter.INVALID_VALUE
|
||||
private var isScrolled = false
|
||||
|
||||
override fun onPageSelected(position: Int) {
|
||||
if (isScrolled) {
|
||||
mTempPosition = position
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPageScrollStateChanged(state: Int) {
|
||||
//手势滑动中,代码执行滑动中
|
||||
if (state == ViewPager2.SCROLL_STATE_DRAGGING || state == ViewPager2.SCROLL_STATE_SETTLING) {
|
||||
isScrolled = true
|
||||
} else if (state == ViewPager2.SCROLL_STATE_IDLE) {
|
||||
//滑动闲置或滑动结束
|
||||
isScrolled = false
|
||||
if (mTempPosition != GameCollectionSquareAdapter.INVALID_VALUE) {
|
||||
if (mTempPosition == 0) {
|
||||
setCurrentItem(mAdapter.getRealCount(), false)
|
||||
} else if (mTempPosition == mAdapter.itemCount - 1) {
|
||||
setCurrentItem(1, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
setCurrentItem(1, false)
|
||||
postDelayed(mLoopTask, AMWAY_LOOP_TIME)
|
||||
}
|
||||
binding.root.setOnClickListener {
|
||||
NewLogUtils.logClickGameCollectionAmway()
|
||||
DirectUtils.directToAmway(binding.root.context)
|
||||
NewLogUtils.logEnterGameCollectionAmway()
|
||||
}
|
||||
}
|
||||
|
||||
fun start() {
|
||||
stop()
|
||||
binding.amwayVp.postDelayed(mLoopTask, AMWAY_LOOP_TIME)
|
||||
}
|
||||
|
||||
fun stop() {
|
||||
binding.amwayVp.removeCallbacks(mLoopTask)
|
||||
}
|
||||
|
||||
fun ViewPager2.setCurrentItem(
|
||||
item: Int,
|
||||
duration: Long,
|
||||
interpolator: TimeInterpolator = AccelerateDecelerateInterpolator(),
|
||||
pagePxHeight: Int = height
|
||||
) {
|
||||
val pxToDrag: Int = pagePxHeight * (item - currentItem)
|
||||
val animator = ValueAnimator.ofInt(0, pxToDrag)
|
||||
var previousValue = 0
|
||||
animator.addUpdateListener { valueAnimator ->
|
||||
val currentValue = valueAnimator.animatedValue as Int
|
||||
val currentPxToDrag = (currentValue - previousValue).toFloat()
|
||||
fakeDragBy(-currentPxToDrag)
|
||||
previousValue = currentValue
|
||||
}
|
||||
animator.addListener(object : Animator.AnimatorListener {
|
||||
override fun onAnimationStart(animation: Animator?) { beginFakeDrag() }
|
||||
override fun onAnimationEnd(animation: Animator?) { endFakeDrag() }
|
||||
override fun onAnimationCancel(animation: Animator?) { }
|
||||
override fun onAnimationRepeat(animation: Animator?) { }
|
||||
})
|
||||
animator.interpolator = interpolator
|
||||
animator.duration = duration
|
||||
animator.start()
|
||||
}
|
||||
|
||||
companion object {
|
||||
// 安利墙卡片轮播时间
|
||||
const val AMWAY_LOOP_TIME = 5000L
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,7 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.home.LegacyHomeItemData
|
||||
|
||||
data class GameCollectionListItemData(var gameCollectionItem: GamesCollectionEntity? = null, var gameStartPosition: Int = 0) :
|
||||
LegacyHomeItemData()
|
||||
@ -0,0 +1,23 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.os.Bundle
|
||||
import com.gh.base.BaseActivity
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.gamecenter.R
|
||||
|
||||
class GameCollectionSquareActivity : BaseActivity() {
|
||||
override fun getLayoutId(): Int {
|
||||
return R.layout.activity_amway
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
DisplayUtils.transparentStatusBar(this)
|
||||
|
||||
val containerFragment = supportFragmentManager.findFragmentByTag(
|
||||
GameCollectionSquareFragment::class.java.simpleName)
|
||||
?: GameCollectionSquareFragment().with(intent.extras)
|
||||
supportFragmentManager.beginTransaction().replace(R.id.placeholder, containerFragment, GameCollectionSquareFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,237 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.graphics.Color
|
||||
import android.util.SparseArray
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.LinearLayout
|
||||
import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.BaseActivity
|
||||
import com.gh.common.constant.ItemViewType
|
||||
import com.gh.common.exposure.ExposureEvent
|
||||
import com.gh.common.exposure.ExposureSource
|
||||
import com.gh.common.exposure.IExposable
|
||||
import com.gh.common.util.*
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.databinding.GameCollectionSquareAmwayItemBinding
|
||||
import com.gh.gamecenter.databinding.GameCollectionSquareItemBinding
|
||||
import com.gh.gamecenter.entity.AmwayCommentEntity
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
|
||||
class GameCollectionSquareAdapter(
|
||||
context: Context,
|
||||
private val isHome: Boolean = false,
|
||||
private val mViewModel: GameCollectionSquareViewModel,
|
||||
private var mBasicExposureSource: List<ExposureSource>,
|
||||
private val mOuterSequence: Int = -1
|
||||
) :
|
||||
ListAdapter<GameCollectionListItemData>(context), IExposable {
|
||||
|
||||
private var mAmwayList = listOf<AmwayCommentEntity>()
|
||||
|
||||
fun setAmwayList(amwayList: List<AmwayCommentEntity>) {
|
||||
if (isHome) {
|
||||
mAmwayList = amwayList
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (isHome && position == 0) ItemViewType.ITEM_HEADER else if (position == itemCount - 1) ItemViewType.ITEM_FOOTER else ItemViewType.ITEM_BODY
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): RecyclerView.ViewHolder {
|
||||
return when (viewType) {
|
||||
ItemViewType.ITEM_HEADER -> {
|
||||
GameCollectionAmwayViewHolder(
|
||||
GameCollectionSquareAmwayItemBinding.bind(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_square_amway_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ItemViewType.ITEM_BODY -> {
|
||||
GameCollectionSquareItemViewHolder(
|
||||
GameCollectionSquareItemBinding.bind(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_square_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
ItemViewType.ITEM_FOOTER -> FooterViewHolder(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.refresh_footerview,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
|
||||
else -> GameCollectionSquareItemViewHolder(
|
||||
GameCollectionSquareItemBinding.bind(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.game_collection_square_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is GameCollectionAmwayViewHolder -> {
|
||||
holder.bindAmway(mAmwayList)
|
||||
}
|
||||
is GameCollectionSquareItemViewHolder -> {
|
||||
val realPosition = if (isHome) position - 1 else position
|
||||
val itemData = mEntityList[realPosition]
|
||||
val filterTagName = mViewModel.selectedTagName
|
||||
val exposureEventList = arrayListOf<ExposureEvent>()
|
||||
itemData.gameCollectionItem?.games?.get(0)?.let {
|
||||
exposureEventList.add(
|
||||
ExposureEvent.createEventWithSourceConcat(
|
||||
it.toGameEntity().apply {
|
||||
outerSequence = mOuterSequence; sequence =
|
||||
itemData.gameStartPosition + 1
|
||||
},
|
||||
mBasicExposureSource,
|
||||
listOf(
|
||||
ExposureSource(
|
||||
"游戏单",
|
||||
if (isHome) "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id}" else "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id} + $filterTagName"
|
||||
)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
itemData.gameCollectionItem?.games?.get(1)?.let {
|
||||
exposureEventList.add(ExposureEvent.createEventWithSourceConcat(it.toGameEntity().apply { outerSequence = mOuterSequence; sequence = itemData.gameStartPosition + 2 }, mBasicExposureSource, listOf(ExposureSource("游戏单", if (isHome) "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id}" else "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id} + $filterTagName"))))
|
||||
}
|
||||
itemData.gameCollectionItem?.games?.get(2)?.let {
|
||||
exposureEventList.add(ExposureEvent.createEventWithSourceConcat(it.toGameEntity().apply { outerSequence = mOuterSequence; sequence = itemData.gameStartPosition + 3 }, mBasicExposureSource, listOf(ExposureSource("游戏单", if (isHome) "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id}" else "${itemData.gameCollectionItem?.title} + ${itemData.gameCollectionItem?.id} + $filterTagName"))))
|
||||
}
|
||||
itemData.exposureEventList = exposureEventList
|
||||
itemData.gameCollectionItem?.let {
|
||||
holder.bindGameCollection(mViewModel, it, itemData)
|
||||
}
|
||||
}
|
||||
is FooterViewHolder -> {
|
||||
holder.initItemPadding()
|
||||
holder.initFooterViewHolder(mViewModel, mIsLoading, mIsNetworkError, mIsOver)
|
||||
holder.hint.setTextColor(ContextCompat.getColor(mContext, R.color.text_B3B3B3))
|
||||
val lp = holder.itemView.layoutParams as RecyclerView.LayoutParams
|
||||
lp.height = DisplayUtils.dip2px(48F)
|
||||
lp.width = ViewGroup.LayoutParams.MATCH_PARENT
|
||||
holder.itemView.layoutParams = lp
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = if (mEntityList.isNullOrEmpty()) 0 else if (isHome) mEntityList.size + 2 else mEntityList.size + 1
|
||||
|
||||
class GameCollectionSquareItemViewHolder(var binding: GameCollectionSquareItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
@SuppressLint("SetTextI18n")
|
||||
fun bindGameCollection(viewModel: GameCollectionSquareViewModel, gamesCollectionEntity: GamesCollectionEntity, itemData: GameCollectionListItemData) {
|
||||
binding.run {
|
||||
val context = root.context
|
||||
entity = gamesCollectionEntity
|
||||
stampIv.setImageDrawable(if (gamesCollectionEntity.stamp == "official") R.drawable.ic_official.toDrawable() else R.drawable.ic_chosen.toDrawable())
|
||||
tagContainer.removeAllViews()
|
||||
if (!gamesCollectionEntity.tags.isNullOrEmpty()) {
|
||||
for ((index, tagEntity) in gamesCollectionEntity.tags!!.withIndex()) {
|
||||
// 添加分隔线
|
||||
if (index != 0) tagContainer.addView(View(context).apply {
|
||||
layoutParams = ViewGroup.LayoutParams(1F.dip2px(), 14F.dip2px())
|
||||
setBackgroundColor(Color.parseColor("#33FFFFFF"))
|
||||
})
|
||||
// 添加标签
|
||||
tagContainer.addView(TextView(context).apply {
|
||||
layoutParams = LinearLayout.LayoutParams(LinearLayout.LayoutParams.WRAP_CONTENT, LinearLayout.LayoutParams.WRAP_CONTENT)
|
||||
setPadding(if (index == 0) 0 else 6F.dip2px(), 0, 6F.dip2px(), 0)
|
||||
text = tagEntity.name
|
||||
setTextColor(R.color.white.toColor())
|
||||
textSize = 10F
|
||||
})
|
||||
}
|
||||
}
|
||||
tagContainer.goneIf(CheckLoginUtils.isLogin())
|
||||
playedGamesContainer.goneIf(!CheckLoginUtils.isLogin())
|
||||
iconIvOne.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(0)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(viewModel.entrance, "游戏单广场"), itemData.exposureEventList?.safelyGetInRelease(0))
|
||||
}
|
||||
}
|
||||
iconIvTwo.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(1)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(viewModel.entrance, "游戏单广场"), itemData.exposureEventList?.safelyGetInRelease(1))
|
||||
}
|
||||
}
|
||||
iconIvThree.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(2)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(viewModel.entrance, "游戏单广场"), itemData.exposureEventList?.safelyGetInRelease(2))
|
||||
}
|
||||
}
|
||||
userContainer.setOnClickListener {
|
||||
NewLogUtils.logClickGameCollectionAuthor(gamesCollectionEntity.title, gamesCollectionEntity.id)
|
||||
DirectUtils.directToHomeActivity(context, gamesCollectionEntity.user?.id, 0, viewModel.entrance, "游戏单广场")
|
||||
}
|
||||
root.setOnClickListener {
|
||||
NewLogUtils.logEnterGameCollectionDetail(gamesCollectionEntity.title, gamesCollectionEntity.id)
|
||||
context.startActivity(GameCollectionDetailActivity.getIntent(context, gamesCollectionEntity.id, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val INVALID_VALUE = -1
|
||||
}
|
||||
|
||||
override fun getEventByPosition(pos: Int): ExposureEvent? {
|
||||
return mEntityList[pos].exposureEvent
|
||||
}
|
||||
|
||||
override fun getEventListByPosition(pos: Int): List<ExposureEvent>? {
|
||||
return mEntityList[pos].exposureEventList
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,259 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.view.ViewCompat
|
||||
import com.gh.common.exposure.ExposureListener
|
||||
import com.gh.common.exposure.ExposureSource
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.VerticalItemDecoration
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.baselist.LazyListFragment
|
||||
import com.gh.gamecenter.baselist.ListAdapter
|
||||
import com.gh.gamecenter.baselist.LoadType
|
||||
import com.gh.gamecenter.databinding.FragmentGameCollectionSquareAlBinding
|
||||
import com.gh.gamecenter.databinding.FragmentGameCollectionSquareBinding
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.eventbus.EBReuse
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectActivity
|
||||
import com.gh.gamecenter.gamecollection.tag.GameCollectionTagSelectFragment
|
||||
import com.gh.gamecenter.personal.PersonalFragment
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import org.greenrobot.eventbus.Subscribe
|
||||
import org.greenrobot.eventbus.ThreadMode
|
||||
import kotlin.math.abs
|
||||
|
||||
class GameCollectionSquareFragment : LazyListFragment<GamesCollectionEntity, GameCollectionSquareViewModel>() {
|
||||
private lateinit var mViewModel: GameCollectionSquareViewModel
|
||||
private lateinit var mExposureListener: ExposureListener
|
||||
|
||||
private lateinit var mDefaultBinding: FragmentGameCollectionSquareBinding
|
||||
private lateinit var mAlternativeBinding: FragmentGameCollectionSquareAlBinding
|
||||
|
||||
private var mAdapter: GameCollectionSquareAdapter? = null
|
||||
|
||||
private var mUseAlternativeLayout = false
|
||||
private var mForumName = ""
|
||||
private var mGameCollectionTitle = ""
|
||||
private var mGameCollectionId = ""
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
mUseAlternativeLayout = arguments?.getBoolean(EntranceUtils.KEY_IS_HOME, false) ?: false
|
||||
mForumName = arguments?.getString(EntranceUtils.KEY_FORUM_NAME, "") ?: ""
|
||||
mGameCollectionTitle = arguments?.getString(EntranceUtils.KEY_GAME_COLLECTION_TITLE, "") ?: ""
|
||||
mGameCollectionId = arguments?.getString(EntranceUtils.KEY_GAME_COLLECTION_ID, "") ?: ""
|
||||
super.onCreate(savedInstanceState)
|
||||
}
|
||||
|
||||
override fun getLayoutId() = R.layout.fragment_stub
|
||||
|
||||
override fun getRealLayoutId(): Int {
|
||||
return if (mUseAlternativeLayout) R.layout.fragment_game_collection_square_al else R.layout.fragment_game_collection_square
|
||||
}
|
||||
|
||||
override fun onFragmentFirstVisible() {
|
||||
super.onFragmentFirstVisible()
|
||||
mViewModel.entrance = mEntrance
|
||||
mViewModel.isHome = mUseAlternativeLayout
|
||||
mListViewModel.load(LoadType.NORMAL)
|
||||
|
||||
NewLogUtils.logEnterGameCollectionSquare(mEntrance, mForumName, mGameCollectionTitle, mGameCollectionId)
|
||||
}
|
||||
|
||||
override fun isAutomaticLoad() = false
|
||||
|
||||
override fun onRealLayoutInflated(inflatedView: View) {
|
||||
super.onRealLayoutInflated(inflatedView)
|
||||
if (mUseAlternativeLayout) {
|
||||
mAlternativeBinding = FragmentGameCollectionSquareAlBinding.bind(inflatedView)
|
||||
} else {
|
||||
mDefaultBinding = FragmentGameCollectionSquareBinding.bind(inflatedView)
|
||||
}
|
||||
}
|
||||
|
||||
override fun initRealView() {
|
||||
super.initRealView()
|
||||
|
||||
mExposureListener = ExposureListener(this, mAdapter!!)
|
||||
mListRv.addOnScrollListener(mExposureListener)
|
||||
|
||||
if (!mUseAlternativeLayout) {
|
||||
initDefaultLayout()
|
||||
} else {
|
||||
initAlternativeLayout()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
super.onActivityResult(requestCode, resultCode, data)
|
||||
if (data == null || resultCode != Activity.RESULT_OK) return
|
||||
if (requestCode == REQUEST_SELECT_TAG) {
|
||||
val tagInfoEntity = data.getParcelableExtra<TagInfoEntity>(GameCollectionTagSelectFragment.SELECTED_TAG)
|
||||
mDefaultBinding.tagTv.text = tagInfoEntity?.name ?: "标签筛选"
|
||||
mViewModel.selectedTagEntity = tagInfoEntity
|
||||
mViewModel.selectedTagId = tagInfoEntity?.id ?: ""
|
||||
mViewModel.selectedTagName = tagInfoEntity?.name ?: "全部标签"
|
||||
onLoadRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideListViewModel(): GameCollectionSquareViewModel {
|
||||
mViewModel = viewModelProvider()
|
||||
return mViewModel
|
||||
}
|
||||
|
||||
override fun getItemDecoration() = VerticalItemDecoration(context, 16F, false, R.color.white)
|
||||
|
||||
override fun provideListAdapter(): ListAdapter<*> {
|
||||
if (mAdapter == null) {
|
||||
val outerSequence = arguments?.getInt(EntranceUtils.KEY_TAB_INDEX)
|
||||
val tabName = arguments?.getString(EntranceUtils.KEY_NAME) ?: ""
|
||||
val basicExposureSource = arrayListOf<ExposureSource>().apply {
|
||||
add(ExposureSource(if (mUseAlternativeLayout) "顶部tab" else "游戏单广场", if (mUseAlternativeLayout) tabName else ""))
|
||||
}
|
||||
mAdapter = GameCollectionSquareAdapter(requireContext(), mUseAlternativeLayout, mViewModel, basicExposureSource, outerSequence?:(-1))
|
||||
}
|
||||
return mAdapter!!
|
||||
}
|
||||
|
||||
private fun initDefaultLayout() {
|
||||
// toolbar 消费 fitsSystemWindows 避免在 collapsingToolbar 下面出现多出来的 padding
|
||||
// [https://stackoverflow.com/questions/48137666/viewgroup-inside-collapsingtoolbarlayout-show-extra-bottom-padding-when-set-fits]
|
||||
ViewCompat.setOnApplyWindowInsetsListener(mDefaultBinding.appbar) { _, insets ->
|
||||
(mDefaultBinding.toolbar.layoutParams as ViewGroup.MarginLayoutParams).topMargin = insets.systemWindowInsetTop
|
||||
insets.consumeSystemWindowInsets()
|
||||
}
|
||||
|
||||
val collapsingTrigger = 66F.dip2px() + DisplayUtils.getStatusBarHeight(context?.resources)
|
||||
|
||||
mDefaultBinding.toolbar.setNavigationOnClickListener { requireActivity().finish() }
|
||||
|
||||
mDefaultBinding.collapsingToolbar.scrimVisibleHeightTrigger = collapsingTrigger
|
||||
mDefaultBinding.collapsingToolbar.scrimShownAction = {
|
||||
DisplayUtils.setLightStatusBar(requireActivity(), it)
|
||||
if (it) {
|
||||
mDefaultBinding.titleTv.alpha = 1F
|
||||
mDefaultBinding.titleTv.visibility = View.VISIBLE
|
||||
mDefaultBinding.titleTv.setTextColor(R.color.black.toColor())
|
||||
mDefaultBinding.toolbar.navigationIcon = R.drawable.ic_bar_back.toDrawable()
|
||||
} else {
|
||||
mDefaultBinding.titleTv.visibility = View.GONE
|
||||
mDefaultBinding.toolbar.navigationIcon = R.drawable.ic_bar_back_light.toDrawable()
|
||||
}
|
||||
}
|
||||
|
||||
mDefaultBinding.titleTv.setOnClickListener {
|
||||
if (ClickUtils.isFastDoubleClick(mDefaultBinding.titleTv.id, 300)) {
|
||||
scrollToTop()
|
||||
}
|
||||
}
|
||||
|
||||
mDefaultBinding.appbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
|
||||
val absOffset = abs(verticalOffset)
|
||||
val invisibleOffset = DisplayUtils.dip2px(30F)
|
||||
|
||||
if (absOffset <= invisibleOffset) {
|
||||
mDefaultBinding.titleTv.alpha = 1 - (absOffset.toFloat() / invisibleOffset)
|
||||
} else {
|
||||
mDefaultBinding.titleTv.alpha = 0F
|
||||
}
|
||||
|
||||
mListRefresh?.isEnabled = absOffset <= 2
|
||||
})
|
||||
|
||||
mDefaultBinding.orderRg.setOnCheckedChangeListener { _, checkedId ->
|
||||
mViewModel.view = if (checkedId == R.id.hotRb) "hot" else "new"
|
||||
onLoadRefresh()
|
||||
}
|
||||
|
||||
mDefaultBinding.fab.setOnClickListener {
|
||||
// 创建游戏单
|
||||
ifLogin(mEntrance) {
|
||||
showRegulationTestDialogIfNeeded {
|
||||
startActivity(GameCollectionEditActivity.getIntent(requireContext(), mEntrance, "游戏单广场"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mDefaultBinding.tagFilter.setOnClickListener {
|
||||
startActivityForResult(GameCollectionTagSelectActivity.getIntent(requireContext(), true, mViewModel.selectedTagEntity), REQUEST_SELECT_TAG)
|
||||
}
|
||||
|
||||
mListRefresh?.setProgressViewOffset(false, 0, 118F.dip2px() + DisplayUtils.getStatusBarHeight(requireContext().resources))
|
||||
// mSkeletonScreen = Skeleton.bind(mDefaultBinding?.skeletonPlaceholder).shimmer(false).load(R.layout.fragment_amway_skeleton).show()
|
||||
}
|
||||
|
||||
private fun initAlternativeLayout() {
|
||||
mAlternativeBinding.fab.setOnClickListener {
|
||||
// 创建游戏单
|
||||
ifLogin(mEntrance) {
|
||||
showRegulationTestDialogIfNeeded {
|
||||
startActivity(GameCollectionEditActivity.getIntent(requireContext(), mEntrance, "游戏单广场"))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mViewModel.mAmwayCommentList.observeNonNull(this) {
|
||||
mAdapter?.setAmwayList(it)
|
||||
}
|
||||
|
||||
// mSkeletonScreen = Skeleton.bind(mAlternativeBinding?.skeletonPlaceholder).shimmer(false).load(
|
||||
// R.layout.fragment_amway_skeleton_al).show()
|
||||
}
|
||||
|
||||
@SuppressLint("NotifyDataSetChanged")
|
||||
override fun onResume() {
|
||||
if (isEverPause) mAdapter?.notifyDataSetChanged()
|
||||
super.onResume()
|
||||
}
|
||||
|
||||
override fun onLoadEmpty() {
|
||||
super.onLoadEmpty()
|
||||
if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onLoadDone() {
|
||||
super.onLoadDone()
|
||||
if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onLoadError() {
|
||||
super.onLoadError()
|
||||
if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.GONE
|
||||
}
|
||||
|
||||
override fun onLoadRefresh() {
|
||||
super.onLoadRefresh()
|
||||
if (!mUseAlternativeLayout) mDefaultBinding.tagFilterContainer.visibility = View.VISIBLE
|
||||
mViewModel.getAmwayCommentList()
|
||||
}
|
||||
|
||||
private fun scrollToTop() {
|
||||
val firstItemPosition = mLayoutManager.findFirstVisibleItemPosition()
|
||||
if (firstItemPosition >= 10) {
|
||||
mListRv.scrollToPosition(6)
|
||||
}
|
||||
mListRv.smoothScrollToPosition(0)
|
||||
mDefaultBinding.appbar.setExpanded(true)
|
||||
}
|
||||
|
||||
@Subscribe(threadMode = ThreadMode.MAIN)
|
||||
fun onEventMainThread(reuse: EBReuse) {
|
||||
if ("Refresh" == reuse.type) {
|
||||
mAdapter?.notifyDataSetChanged()
|
||||
} else if (reuse.type == PersonalFragment.LOGIN_TAG) { // 登入
|
||||
scrollToTop()
|
||||
onRefresh()
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val REQUEST_SELECT_TAG = 100
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,76 @@
|
||||
package com.gh.gamecenter.gamecollection.square
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.baselist.ListViewModel
|
||||
import com.gh.gamecenter.baselist.LoadParams
|
||||
import com.gh.gamecenter.baselist.LoadStatus
|
||||
import com.gh.gamecenter.entity.AmwayCommentEntity
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.Observable
|
||||
import io.reactivex.Single
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import java.util.*
|
||||
|
||||
class GameCollectionSquareViewModel(application: Application) :
|
||||
ListViewModel<GamesCollectionEntity, GameCollectionListItemData>(application) {
|
||||
|
||||
var entrance: String? = null
|
||||
var selectedTagEntity: TagInfoEntity? = null
|
||||
var selectedTagId = ""
|
||||
var selectedTagName = "全部标签"
|
||||
var view = "hot"
|
||||
var isHome = false
|
||||
|
||||
val mAmwayCommentList = MutableLiveData<List<AmwayCommentEntity>>()
|
||||
|
||||
init {
|
||||
getAmwayCommentList()
|
||||
}
|
||||
|
||||
override fun initLoadParams() {
|
||||
mCurLoadParams = LoadParams(PAGE_SIZE, LoadParams.DEFAULT_OFFSET)
|
||||
mLoadStatusLiveData.value = LoadStatus.INIT
|
||||
}
|
||||
|
||||
override fun provideDataSingle(page: Int): Single<MutableList<GamesCollectionEntity>> =
|
||||
if (isHome) RetrofitManager.getInstance(getApplication())
|
||||
.api.getHomeGameCollectionSquareList(UUID.randomUUID().toString(), page, PAGE_SIZE)
|
||||
else RetrofitManager.getInstance(getApplication())
|
||||
.api.getGameCollectionSquareList(view, selectedTagId, page, PAGE_SIZE)
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) { list ->
|
||||
val itemDataList = arrayListOf<GameCollectionListItemData>().apply {
|
||||
var position = 0
|
||||
for (item in list) {
|
||||
add(GameCollectionListItemData(gameCollectionItem = item, gameStartPosition = position))
|
||||
position += if (item.count?.game!! > 2) 3 else item.count?.game ?: 0
|
||||
}
|
||||
}
|
||||
mResultLiveData.postValue(itemDataList)
|
||||
}
|
||||
}
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<GamesCollectionEntity>>? = null
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getAmwayCommentList() {
|
||||
RetrofitManager.getInstance(getApplication()).api
|
||||
.getAmwayCommentList(1, 10)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.subscribe(object : BiResponse<List<AmwayCommentEntity>>() {
|
||||
override fun onSuccess(data: List<AmwayCommentEntity>) {
|
||||
mAmwayCommentList.postValue(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val PAGE_SIZE = 15
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,145 @@
|
||||
package com.gh.gamecenter.gamecollection.tag
|
||||
|
||||
import android.content.Context
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.util.goneIf
|
||||
import com.gh.gamecenter.databinding.*
|
||||
import com.gh.gamecenter.entity.GameCollectionTagEntity
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class GameCollectionTagAdapter(
|
||||
context: Context,
|
||||
val singleChoice: Boolean = true,
|
||||
val singleSelectedTag: TagInfoEntity? = null,
|
||||
private val updateCallback: (() -> Unit)
|
||||
) :
|
||||
BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private var mTagViewList = arrayListOf<ItemGameCollectionTagBinding>()
|
||||
var mTagList = arrayListOf<GameCollectionTagEntity>()
|
||||
var selectedTagEntity: TagInfoEntity? = null
|
||||
var selectedTagCategory = ""
|
||||
var selectedTagEntityList = arrayListOf<TagInfoEntity>()
|
||||
private var flagFirst = true
|
||||
|
||||
fun setTagList(tagList: ArrayList<GameCollectionTagEntity>) {
|
||||
mTagList = tagList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (!singleChoice && position == 0) SELECTED_TAGS else TAG_ITEM
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = when (viewType){
|
||||
SELECTED_TAGS -> GameCollectionSelectedTagViewHolder(
|
||||
GameCollectionSelectedTagItemBinding.inflate(mLayoutInflater, parent, false)
|
||||
)
|
||||
else -> GameCollectionTagItemViewHolder(
|
||||
GameCollectionTagItemBinding.inflate(mLayoutInflater, parent, false)
|
||||
)
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
when (holder) {
|
||||
is GameCollectionSelectedTagViewHolder -> {
|
||||
holder.binding.selectedTagFlexbox.removeAllViews()
|
||||
holder.binding.hintTv.goneIf(selectedTagEntityList.size != 0)
|
||||
for (tag in selectedTagEntityList) {
|
||||
val selectedTagView =
|
||||
ItemGameCollectionSelectedTagBinding.inflate(mLayoutInflater)
|
||||
.apply {
|
||||
tagTv.text = tag.name
|
||||
root.setOnClickListener {
|
||||
selectedTagEntityList.remove(tag)
|
||||
updateCallback.invoke()
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
}.root
|
||||
holder.binding.selectedTagFlexbox.addView(selectedTagView)
|
||||
}
|
||||
}
|
||||
is GameCollectionTagItemViewHolder -> {
|
||||
val data = mTagList[if (singleChoice) position else position - 1]
|
||||
holder.binding.tagFlexbox.removeAllViews()
|
||||
holder.binding.titleTv.text = data.categoryName
|
||||
holder.binding.titleTv.setPadding(
|
||||
16F.dip2px(),
|
||||
if (position == 0) 40F.dip2px() else 10F.dip2px(),
|
||||
16F.dip2px(),
|
||||
10F.dip2px()
|
||||
)
|
||||
holder.binding.tagFlexbox.setPadding(
|
||||
12F.dip2px(),
|
||||
5F.dip2px(),
|
||||
12F.dip2px(),
|
||||
if (position == itemCount - 1) 97F.dip2px() else 15F.dip2px()
|
||||
)
|
||||
for (tagEntity in data.tags) {
|
||||
val tag = if (singleChoice) getSingleTag(tagEntity, data.categoryName) else getMultipleTag(tagEntity)
|
||||
mTagViewList.add(tag)
|
||||
holder.binding.tagFlexbox.addView(tag.root)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getSingleTag(tag: TagInfoEntity, tagCategory: String) = ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply {
|
||||
tagTv.text = tag.name
|
||||
tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 }
|
||||
singleSelectedTag?.let {
|
||||
if (flagFirst && tag == it) {
|
||||
tagTv.isChecked = true
|
||||
selectedTagEntity = tag
|
||||
}
|
||||
}
|
||||
root.setOnClickListener {
|
||||
for (tagView in mTagViewList) {
|
||||
if (tagView.tagTv != tagTv) tagView.tagTv.isChecked = false
|
||||
}
|
||||
tagTv.isChecked = !tagTv.isChecked
|
||||
if (tagTv.isChecked) {
|
||||
selectedTagEntity = tag
|
||||
selectedTagCategory = tagCategory
|
||||
} else {
|
||||
selectedTagEntity = null
|
||||
selectedTagCategory = ""
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getMultipleTag(tag: TagInfoEntity) =
|
||||
ItemGameCollectionTagBinding.inflate(mLayoutInflater).apply {
|
||||
tagTv.layoutParams = tagTv.layoutParams.apply { width = (DisplayUtils.getScreenWidth() - 56F.dip2px()) / 4 }
|
||||
tagTv.text = tag.name
|
||||
if (selectedTagEntityList.contains(tag)) tagTv.isChecked = true
|
||||
root.setOnClickListener {
|
||||
tagTv.isChecked = !tagTv.isChecked
|
||||
if (tagTv.isChecked) {
|
||||
selectedTagEntityList.add(tag)
|
||||
} else {
|
||||
if (selectedTagEntityList.contains(tag)) selectedTagEntityList.remove(tag)
|
||||
}
|
||||
updateCallback.invoke()
|
||||
notifyItemChanged(0)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = if (singleChoice) mTagList.size else mTagList.size + 1
|
||||
|
||||
class GameCollectionSelectedTagViewHolder(var binding: GameCollectionSelectedTagItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
class GameCollectionTagItemViewHolder(var binding: GameCollectionTagItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root)
|
||||
|
||||
companion object {
|
||||
const val SELECTED_TAGS = 100
|
||||
const val TAG_ITEM = 101
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,35 @@
|
||||
package com.gh.gamecenter.gamecollection.tag
|
||||
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import com.gh.gamecenter.NormalActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
|
||||
class GameCollectionTagSelectActivity : NormalActivity() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setNavigationTitle("选择标签")
|
||||
setToolbarMenu(R.menu.menu_save)
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val KEY_IS_SINGLE_CHOICE = "single_choice"
|
||||
const val KEY_SINGLE_SELECTED_TAG = "single_selected_tag"
|
||||
|
||||
@JvmStatic
|
||||
fun getIntent(context: Context, singleChoice: Boolean = false, singleSelectedTag: TagInfoEntity? = null): Intent {
|
||||
val bundle = Bundle()
|
||||
bundle.putBoolean(KEY_IS_SINGLE_CHOICE, singleChoice)
|
||||
bundle.putParcelable(KEY_SINGLE_SELECTED_TAG, singleSelectedTag)
|
||||
return getTargetIntent(
|
||||
context,
|
||||
GameCollectionTagSelectActivity::class.java,
|
||||
GameCollectionTagSelectFragment::class.java,
|
||||
bundle
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,102 @@
|
||||
package com.gh.gamecenter.gamecollection.tag
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.common.util.viewModelProvider
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.FragmentGameCollectionTagSelectBinding
|
||||
import com.gh.gamecenter.databinding.ItemGameCollectionSelectedTagBinding
|
||||
import com.gh.gamecenter.entity.TagInfoEntity
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
|
||||
class GameCollectionTagSelectFragment : NormalFragment() {
|
||||
|
||||
private var mSingleChoice = false
|
||||
private var mSelectedTag: TagInfoEntity? = null
|
||||
private lateinit var mBinding: FragmentGameCollectionTagSelectBinding
|
||||
private lateinit var mAdapter: GameCollectionTagAdapter
|
||||
private lateinit var mViewModel: GameCollectionTagViewModel
|
||||
private var firstVisibleItemPosition = 0
|
||||
|
||||
private val updateSelectedTagView: (() -> Unit) = {
|
||||
mAdapter.selectedTagEntityList.let { list ->
|
||||
mBinding.selectedTagContainer.removeAllViews()
|
||||
for (tag in list) {
|
||||
val selectedTagView = ItemGameCollectionSelectedTagBinding.inflate(layoutInflater)
|
||||
selectedTagView.run {
|
||||
tagTv.text = tag.name
|
||||
root.setOnClickListener {
|
||||
list.remove(tag)
|
||||
mBinding.selectedTagContainer.removeView(selectedTagView.root)
|
||||
mAdapter.notifyDataSetChanged()
|
||||
mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
mBinding.selectedTagContainer.addView(selectedTagView.root)
|
||||
}
|
||||
mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || list.isEmpty()) View.GONE else View.VISIBLE
|
||||
}
|
||||
}
|
||||
|
||||
override fun getLayoutId(): Int = 0
|
||||
|
||||
override fun getInflatedLayout() =
|
||||
FragmentGameCollectionTagSelectBinding.inflate(layoutInflater).apply {
|
||||
mBinding = this
|
||||
}.root
|
||||
|
||||
override fun onMenuItemClick(menuItem: MenuItem?) {
|
||||
if (menuItem?.itemId == R.id.layout_menu_save) {
|
||||
if (mSingleChoice) {
|
||||
if (mAdapter.selectedTagEntity != null) {
|
||||
NewLogUtils.logFilterGameCollectionTag(mAdapter.selectedTagCategory, mAdapter.selectedTagEntity!!.name)
|
||||
}
|
||||
requireActivity().setResult(
|
||||
Activity.RESULT_OK,
|
||||
Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntity)
|
||||
)
|
||||
} else {
|
||||
requireActivity().setResult(
|
||||
Activity.RESULT_OK,
|
||||
Intent().putExtra(SELECTED_TAG, mAdapter.selectedTagEntityList)
|
||||
)
|
||||
}
|
||||
requireActivity().finish()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
mSingleChoice = arguments?.getBoolean(GameCollectionTagSelectActivity.KEY_IS_SINGLE_CHOICE) ?: false
|
||||
mSelectedTag = arguments?.getParcelable(GameCollectionTagSelectActivity.KEY_SINGLE_SELECTED_TAG)
|
||||
mAdapter = GameCollectionTagAdapter(requireContext(), mSingleChoice, mSelectedTag, updateSelectedTagView)
|
||||
mViewModel = viewModelProvider()
|
||||
if (mSingleChoice) NewLogUtils.logEnterGameCollectionTag()
|
||||
mBinding.tagRv.run {
|
||||
layoutManager = LinearLayoutManager(requireContext())
|
||||
adapter = mAdapter
|
||||
addOnScrollListener(object :RecyclerView.OnScrollListener() {
|
||||
|
||||
override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) {
|
||||
super.onScrolled(recyclerView, dx, dy)
|
||||
firstVisibleItemPosition = (layoutManager as LinearLayoutManager).findFirstVisibleItemPosition()
|
||||
mBinding.selectedTagScrollView.visibility = if (firstVisibleItemPosition == 0 || mAdapter.selectedTagEntityList.isEmpty()) View.GONE else View.VISIBLE
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
mViewModel.tagListLiveData.observe(viewLifecycleOwner) {
|
||||
mAdapter.setTagList(it)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val SELECTED_TAG = "selected_tag"
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,33 @@
|
||||
package com.gh.gamecenter.gamecollection.tag
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.app.Application
|
||||
import androidx.lifecycle.AndroidViewModel
|
||||
import androidx.lifecycle.MutableLiveData
|
||||
import com.gh.gamecenter.entity.GameCollectionTagEntity
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.RetrofitManager
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
|
||||
class GameCollectionTagViewModel(application: Application) : AndroidViewModel(application) {
|
||||
|
||||
var tagListLiveData = MutableLiveData<ArrayList<GameCollectionTagEntity>>()
|
||||
private val mApi = RetrofitManager.getInstance(getApplication()).api
|
||||
|
||||
init {
|
||||
getGameCollectionTagList()
|
||||
}
|
||||
|
||||
@SuppressLint("CheckResult")
|
||||
fun getGameCollectionTagList() {
|
||||
mApi.gameCollectionTagList
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object: BiResponse<ArrayList<GameCollectionTagEntity>>(){
|
||||
override fun onSuccess(data: ArrayList<GameCollectionTagEntity>) {
|
||||
tagListLiveData.postValue(data)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -6,15 +6,16 @@ import com.gh.base.fragment.BaseFragment_TabLayout
|
||||
import com.gh.common.util.EntranceUtils
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.collection.AnswerFragment
|
||||
import com.gh.gamecenter.collection.ArticleFragment
|
||||
import com.gh.gamecenter.collection.CommunityArticleFragment
|
||||
import com.gh.gamecenter.collection.VideoFragment
|
||||
import com.gh.gamecenter.collection.*
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
|
||||
class HistoryWrapperFragment : BaseFragment_TabLayout() {
|
||||
|
||||
override fun getLayoutId() = R.layout.fragment_no_padding_tablayout_viewpager
|
||||
|
||||
override fun initTabTitleList(tabTitleList: MutableList<String>) {
|
||||
tabTitleList.add(getString(R.string.main_game))
|
||||
tabTitleList.add(getString(R.string.game_collection))
|
||||
tabTitleList.add(getString(R.string.video))
|
||||
tabTitleList.add(getString(R.string.answer))
|
||||
tabTitleList.add(getString(R.string.collection_article))
|
||||
@ -23,6 +24,10 @@ class HistoryWrapperFragment : BaseFragment_TabLayout() {
|
||||
|
||||
override fun initFragmentList(fragments: MutableList<Fragment>) {
|
||||
fragments.add(HistoryGameListFragment().with(arguments))
|
||||
fragments.add(GamesCollectionFragment().with(arguments?.apply {
|
||||
putString(EntranceUtils.KEY_USER_ID, UserManager.getInstance().userId)
|
||||
putString(EntranceUtils.KEY_TYPE, GamesCollectionFragment.TYPE_HISTORY)
|
||||
}))
|
||||
fragments.add(VideoFragment().with(arguments?.apply {
|
||||
putString("videoStyle", VideoFragment.VideoStyle.BROWSING_HISTORY.value)
|
||||
}))
|
||||
|
||||
@ -21,10 +21,13 @@ import com.gh.gamecenter.baselist.DiffUtilAdapter
|
||||
import com.gh.gamecenter.baselist.LoadStatus
|
||||
import com.gh.gamecenter.databinding.*
|
||||
import com.gh.gamecenter.entity.AmwayCommentEntity
|
||||
import com.gh.gamecenter.entity.GamesCollectionEntity
|
||||
import com.gh.gamecenter.eventbus.EBDownloadStatus
|
||||
import com.gh.gamecenter.game.GameAndPosition
|
||||
import com.gh.gamecenter.game.rank.RankCollectionAdapter
|
||||
import com.gh.gamecenter.game.vertical.GameVerticalAdapter
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
import com.gh.gamecenter.home.gamecollection.HomeGameCollectionViewHolder
|
||||
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
|
||||
import com.gh.gamecenter.home.amway.HomeAmwayListViewHolder
|
||||
import com.gh.gamecenter.home.slide.HomeSlideListAdapter
|
||||
@ -32,6 +35,7 @@ import com.gh.gamecenter.home.slide.HomeSlideListViewHolder
|
||||
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
|
||||
import com.halo.assistant.fragment.game.GamePluginAdapter
|
||||
import com.lightgame.download.DownloadEntity
|
||||
import com.lightgame.utils.Utils
|
||||
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
|
||||
import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
|
||||
import java.util.*
|
||||
@ -96,6 +100,7 @@ class HomeFragmentAdapter(
|
||||
if (itemData.recommends != null) return RECOMMENDS_ITEM
|
||||
if (itemData.attachGame != null) return GAME_ITEM
|
||||
if (itemData.amway != null) return AMWAY_ITEM
|
||||
if (itemData.gameCollection != null) return GAME_COLLECTION_ITEM
|
||||
if (itemData.lineDivider != null) return DIVIDER_ITEM
|
||||
if (itemData.unknownData != null) return UNKNOWN_ITEM
|
||||
|
||||
@ -121,6 +126,10 @@ class HomeFragmentAdapter(
|
||||
view = mLayoutInflater.inflate(R.layout.home_amway_list, parent, false)
|
||||
HomeAmwayListViewHolder(HomeAmwayListBinding.bind(view))
|
||||
}
|
||||
GAME_COLLECTION_ITEM -> {
|
||||
view = mLayoutInflater.inflate(R.layout.home_game_collection_item, parent, false)
|
||||
HomeGameCollectionViewHolder(HomeGameCollectionItemBinding.bind(view))
|
||||
}
|
||||
DIVIDER_ITEM -> {
|
||||
view = mLayoutInflater.inflate(R.layout.home_divider_item, parent, false)
|
||||
HomeDividerViewHolder(HomeDividerItemBinding.bind(view))
|
||||
@ -160,6 +169,7 @@ class HomeFragmentAdapter(
|
||||
is FooterViewHolder -> bindFooterView(holder)
|
||||
is ReuseViewHolder -> bindUnknown(holder)
|
||||
is HomeDividerViewHolder -> holder.bindView(mDataList[position].lineDivider ?: 1F)
|
||||
is HomeGameCollectionViewHolder -> bindGameCollection(holder, position)
|
||||
|
||||
else -> mLegacyHomeFragmentAdapterAssistant.bindLegacyViewHolder(
|
||||
holder,
|
||||
@ -222,6 +232,53 @@ class HomeFragmentAdapter(
|
||||
holder.bindAmwayList(amwayList, clickClosure)
|
||||
}
|
||||
|
||||
private fun bindGameCollection(holder: HomeGameCollectionViewHolder, position: Int) {
|
||||
val homeItemData = mDataList[position]
|
||||
val gameCollectionItemDataList = homeItemData.gameCollection?: listOf()
|
||||
|
||||
val exposureList = arrayListOf<ExposureEvent>()
|
||||
for (gameCollectionItemData in gameCollectionItemDataList) {
|
||||
runOnIoThread(true) {
|
||||
val gameCollection = gameCollectionItemData.gameCollectionItem
|
||||
val gameCollectionSource = listOf(ExposureSource("游戏单", "${gameCollection?.title} + ${gameCollection?.id}"))
|
||||
val gameExposureList = arrayListOf<ExposureEvent>()
|
||||
gameCollection?.games?.get(0)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = homeItemData.blockPosition; sequence = gameCollectionItemData.gameStartPosition + 1 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollection?.games?.get(1)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = homeItemData.blockPosition; sequence = gameCollectionItemData.gameStartPosition + 2 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollection?.games?.get(2)?.let {
|
||||
gameExposureList.add(ExposureEvent.createEventWithSourceConcat(
|
||||
gameEntity = it.toGameEntity().apply { outerSequence = homeItemData.blockPosition; sequence = gameCollectionItemData.gameStartPosition + 3 },
|
||||
basicSource = mBasicExposureSource,
|
||||
source = gameCollectionSource
|
||||
))
|
||||
}
|
||||
gameCollectionItemData.exposureEventList = gameExposureList
|
||||
exposureList.addAll(gameExposureList)
|
||||
}
|
||||
}
|
||||
homeItemData.exposureEventList = exposureList
|
||||
holder.bindGameCollectionList(gameCollectionItemDataList, "首页内容列表")
|
||||
// val testData = arrayListOf(
|
||||
// GameCollectionListItemData(gameCollectionItem = GamesCollectionEntity(title = "1111"), gameStartPosition = 0),
|
||||
// GameCollectionListItemData(gameCollectionItem = GamesCollectionEntity(title = "2222"), gameStartPosition = 0),
|
||||
// GameCollectionListItemData(gameCollectionItem = GamesCollectionEntity(title = "3333"), gameStartPosition = 0),
|
||||
// GameCollectionListItemData(gameCollectionItem = GamesCollectionEntity(title = "4444"), gameStartPosition = 0),
|
||||
// GameCollectionListItemData(gameCollectionItem = GamesCollectionEntity(title = "55555"), gameStartPosition = 0)
|
||||
// )
|
||||
// holder.bindGameCollectionList(testData, "首页内容列表")
|
||||
}
|
||||
|
||||
private fun bindAttachGame(holder: HomeGameItemViewHolder, position: Int) {
|
||||
val homeItemData = mDataList[position]
|
||||
val game = homeItemData.attachGame?.linkGame!!
|
||||
@ -460,5 +517,6 @@ class HomeFragmentAdapter(
|
||||
const val FOOTER_ITEM: Int = 110
|
||||
const val UNKNOWN_ITEM: Int = 111
|
||||
const val COMMON_ITEM: Int = 115
|
||||
const val GAME_COLLECTION_ITEM: Int = 116
|
||||
}
|
||||
}
|
||||
@ -1,10 +1,12 @@
|
||||
package com.gh.gamecenter.home
|
||||
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
|
||||
data class HomeItemData(var slides: List<HomeSlide>? = null,
|
||||
var recommends: List<HomeRecommend>? = null,
|
||||
var amway: List<AmwayCommentEntity>? = null,
|
||||
var gameCollection: List<GameCollectionListItemData>? = null,
|
||||
var attachGame: HomeContent? = null,
|
||||
var lineDivider: Float? = null,
|
||||
var unknownData: Any? = null) : LegacyHomeItemData()
|
||||
@ -13,6 +13,7 @@ import com.gh.download.DownloadManager
|
||||
import com.gh.gamecenter.BuildConfig
|
||||
import com.gh.gamecenter.baselist.LoadStatus
|
||||
import com.gh.gamecenter.entity.*
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
import com.gh.gamecenter.packagehelper.PackageRepository
|
||||
import com.gh.gamecenter.retrofit.BiResponse
|
||||
import com.gh.gamecenter.retrofit.Response
|
||||
@ -23,7 +24,10 @@ import com.lightgame.utils.Utils
|
||||
import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import retrofit2.HttpException
|
||||
import java.io.BufferedReader
|
||||
import java.io.InputStreamReader
|
||||
import java.util.*
|
||||
import java.util.stream.Collectors
|
||||
import kotlin.collections.ArrayList
|
||||
import kotlin.collections.set
|
||||
|
||||
@ -398,6 +402,25 @@ class HomeViewModel(application: Application) : AndroidViewModel(application) {
|
||||
commonCollectionItem.commonCollection = subjectEntity
|
||||
commonCollectionItem.blockPosition = i
|
||||
mSnapshotItemList.add(commonCollectionItem)
|
||||
} else if (linkType == "game_list_collection") {
|
||||
val head = HomeItemData()
|
||||
head.columnHead = SubjectEntity(type = linkType, name = "热门游戏单")
|
||||
mSnapshotItemList.add(LegacyHomeSubjectTransformer.getBlankSpacingItem(HomeItemData()) as HomeItemData)
|
||||
mSnapshotItemList.add(head)
|
||||
|
||||
val gameCollection = HomeItemData()
|
||||
gameCollection.blockPosition = i
|
||||
val itemDataList = arrayListOf<GameCollectionListItemData>().apply {
|
||||
if (!homeContent.linkGameCollection.isNullOrEmpty()) {
|
||||
var position = 0
|
||||
for (item in homeContent.linkGameCollection) {
|
||||
add(GameCollectionListItemData(gameCollectionItem = item, gameStartPosition = position))
|
||||
position += if (item.count?.game!! > 2) 3 else item.count?.game ?: 0
|
||||
}
|
||||
}
|
||||
}
|
||||
gameCollection.gameCollection = itemDataList
|
||||
mSnapshotItemList.add(gameCollection)
|
||||
} else {
|
||||
val unknown = HomeItemData()
|
||||
unknown.blockPosition = i + 1
|
||||
|
||||
@ -583,6 +583,8 @@ class LegacyHomeFragmentAdapterAssistant(
|
||||
)
|
||||
)
|
||||
NewLogUtils.logAccessToCommonCollectionDetail(column.id ?: "", column.name ?: "", "首页内容列表")
|
||||
} else if (column.type == "game_list_collection") {
|
||||
DirectUtils.directToGameCollectionSquare(mContext, "首页内容列表", column.name ?: "")
|
||||
} else {
|
||||
if (column.indexRightTopLink != null) {
|
||||
val link = column.indexRightTopLink!!
|
||||
|
||||
@ -0,0 +1,144 @@
|
||||
package com.gh.gamecenter.home.gamecollection
|
||||
|
||||
import android.view.View
|
||||
import com.gh.common.util.DisplayUtils
|
||||
import com.gh.common.view.stacklayoutmanager.StackAnimation
|
||||
import com.gh.common.view.stacklayoutmanager.StackLayoutManager
|
||||
import com.gh.gamecenter.R
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.utils.Utils
|
||||
import kotlin.math.pow
|
||||
|
||||
class GameCollectionStackAnimation(
|
||||
scrollOrientation: StackLayoutManager.ScrollOrientation,
|
||||
visibleCount: Int
|
||||
) : StackAnimation(scrollOrientation, visibleCount) {
|
||||
private var mScale = 0.9F
|
||||
private var mOutScale = 1.0F
|
||||
private var mOutRotation = 0
|
||||
|
||||
/**
|
||||
* 设置 item 缩放比例.
|
||||
* @param scale 缩放比例
|
||||
*/
|
||||
fun setItemScaleRate(scale: Float) {
|
||||
mScale = scale
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取item缩放比例.
|
||||
* @return item缩放比例
|
||||
*/
|
||||
fun getItemScaleRate(): Float {
|
||||
return mScale
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 itemView 离开屏幕时候的缩放比例.
|
||||
* @param scale 缩放比例
|
||||
*/
|
||||
fun setOutScale(scale: Float) {
|
||||
mOutScale = scale
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 itemView 离开屏幕时候的缩放比例.
|
||||
* @return 缩放比例
|
||||
*/
|
||||
fun getOutScale(): Float {
|
||||
return mOutScale
|
||||
}
|
||||
|
||||
/**
|
||||
* 设置 itemView 离开屏幕时候的旋转角度.
|
||||
* @param rotation 旋转角度
|
||||
*/
|
||||
fun setOutRotation(rotation: Int) {
|
||||
mOutRotation = rotation
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取 itemView 离开屏幕时候的旋转角度
|
||||
* @return 旋转角度
|
||||
*/
|
||||
fun getOutRotation(): Int {
|
||||
return mOutRotation
|
||||
}
|
||||
|
||||
override fun doAnimation(firstMovePercent: Float, itemView: View, position: Int) {
|
||||
val cover = itemView.findViewById<View>(R.id.cover)
|
||||
val content = itemView.findViewById<View>(R.id.content)
|
||||
val scale: Float
|
||||
var alpha = 1.0F
|
||||
val rotation: Float
|
||||
if (position == 0) {
|
||||
// 顶层item透明度变化
|
||||
cover.alpha = 0F
|
||||
content.alpha = 1F
|
||||
if (firstMovePercent > 0.5) alpha = 1F - 2 * (firstMovePercent - 0.5F)
|
||||
scale = 1 - ((1 - mOutScale) * firstMovePercent)
|
||||
rotation = mOutRotation * firstMovePercent
|
||||
} else {
|
||||
val minScale = (mScale.toDouble().pow(position.toDouble())).toFloat()
|
||||
val maxScale = (mScale.toDouble().pow((position - 1).toDouble())).toFloat()
|
||||
scale = minScale + (maxScale - minScale) * firstMovePercent
|
||||
when (position) {
|
||||
1 -> {
|
||||
cover.alpha = 0.3F - 0.3F * firstMovePercent
|
||||
content.alpha = firstMovePercent
|
||||
}
|
||||
2 -> cover.alpha = 0.6F - 0.3F * firstMovePercent
|
||||
3 -> {
|
||||
cover.alpha = 1F - 0.4F * firstMovePercent
|
||||
// 如果不改变会出现残留边框
|
||||
// alpha = if (firstMovePercent == 0F) 0F else 1F
|
||||
}
|
||||
}
|
||||
rotation = 0F
|
||||
}
|
||||
|
||||
setItemPivotXY(mScrollOrientation, itemView)
|
||||
rotationFirstVisibleItem(mScrollOrientation, itemView, rotation)
|
||||
itemView.scaleX = scale
|
||||
itemView.scaleY = scale
|
||||
itemView.alpha = alpha
|
||||
}
|
||||
|
||||
private fun setItemPivotXY(
|
||||
scrollOrientation: StackLayoutManager.ScrollOrientation,
|
||||
view: View
|
||||
) {
|
||||
when (scrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> {
|
||||
view.pivotX = view.measuredWidth.toFloat()
|
||||
view.pivotY = view.measuredHeight.toFloat() / 2
|
||||
}
|
||||
StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> {
|
||||
view.pivotX = 0F
|
||||
view.pivotY = view.measuredHeight.toFloat() / 2
|
||||
}
|
||||
StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> {
|
||||
view.pivotX = view.measuredWidth.toFloat() / 2
|
||||
view.pivotY = view.measuredHeight.toFloat()
|
||||
}
|
||||
StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> {
|
||||
view.pivotX = view.measuredWidth.toFloat() / 2
|
||||
view.pivotY = 0F
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun rotationFirstVisibleItem(
|
||||
scrollOrientation: StackLayoutManager.ScrollOrientation,
|
||||
view: View,
|
||||
rotation: Float
|
||||
) {
|
||||
when (scrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> view.rotationY = rotation
|
||||
StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> view.rotationY = -rotation
|
||||
StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> view.rotationX = -rotation
|
||||
StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> view.rotationX = rotation
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@ -0,0 +1,110 @@
|
||||
package com.gh.gamecenter.home.gamecollection
|
||||
|
||||
import android.view.View
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.view.stacklayoutmanager.StackLayout
|
||||
import com.gh.common.view.stacklayoutmanager.StackLayoutManager
|
||||
|
||||
class GameCollectionStackLayout(
|
||||
scrollOrientation: StackLayoutManager.ScrollOrientation,
|
||||
visibleCount: Int,
|
||||
perItemOffset: Int
|
||||
) : StackLayout(scrollOrientation, visibleCount, perItemOffset) {
|
||||
|
||||
private var mHasMeasureItemSize = false
|
||||
private var mWidthSpace = 0
|
||||
private var mHeightSpace = 0
|
||||
private var mStartMargin = 0
|
||||
|
||||
private var mWidth = 0
|
||||
private var mHeight = 0
|
||||
private var mScrollOffset = 0
|
||||
|
||||
override fun doLayout(
|
||||
stackLayoutManager: StackLayoutManager,
|
||||
scrollOffset: Int,
|
||||
firstMovePercent: Float,
|
||||
itemView: View,
|
||||
position: Int
|
||||
) {
|
||||
mWidth = stackLayoutManager.width
|
||||
mHeight = stackLayoutManager.height
|
||||
mScrollOffset = scrollOffset
|
||||
if (!mHasMeasureItemSize) {
|
||||
mWidthSpace =
|
||||
mWidth - stackLayoutManager.getDecoratedMeasuredWidth(itemView) - 18F.dip2px()
|
||||
mHeightSpace = mHeight - stackLayoutManager.getDecoratedMeasuredHeight(itemView)
|
||||
mStartMargin = 16F.dip2px()
|
||||
mHasMeasureItemSize = true
|
||||
}
|
||||
val left: Int
|
||||
val top: Int
|
||||
if (position == 0) {
|
||||
left = getFirstVisibleItemLeft()
|
||||
top = getFirstVisibleItemTop()
|
||||
} else {
|
||||
left = getAfterFirstVisibleItemLeft(position, firstMovePercent)
|
||||
top = getAfterFirstVisibleItemTop(position, firstMovePercent)
|
||||
}
|
||||
|
||||
val right = left + stackLayoutManager.getDecoratedMeasuredWidth(itemView)
|
||||
val bottom = top + stackLayoutManager.getDecoratedMeasuredHeight(itemView)
|
||||
|
||||
stackLayoutManager.layoutDecorated(itemView, left, top, right, bottom)
|
||||
}
|
||||
|
||||
override fun requestLayout() {
|
||||
mHasMeasureItemSize = false //表示尺寸可能发生了改变
|
||||
}
|
||||
|
||||
private fun getFirstVisibleItemLeft(): Int {
|
||||
return when (mScrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> mStartMargin - mScrollOffset % mWidth
|
||||
StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> {
|
||||
return if (mScrollOffset % mWidth == 0) {
|
||||
mStartMargin
|
||||
} else {
|
||||
mStartMargin + (mWidth - mScrollOffset % mWidth)
|
||||
}
|
||||
}
|
||||
else -> mWidthSpace / 2
|
||||
}
|
||||
}
|
||||
|
||||
private fun getFirstVisibleItemTop(): Int {
|
||||
return when (mScrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> mStartMargin - mScrollOffset % mHeight
|
||||
StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> {
|
||||
return if (mScrollOffset % mHeight == 0) {
|
||||
mStartMargin
|
||||
} else {
|
||||
mStartMargin + (mHeight - mScrollOffset % mHeight)
|
||||
}
|
||||
}
|
||||
else -> mHeightSpace / 2
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAfterFirstVisibleItemLeft(visiblePosition: Int, movePercent: Float): Int {
|
||||
return when (mScrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT -> (mStartMargin + mPerItemOffset * (visiblePosition - movePercent)).toInt()
|
||||
StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> (mStartMargin - mPerItemOffset * (visiblePosition - movePercent)).toInt()
|
||||
else -> mWidthSpace / 2
|
||||
}
|
||||
}
|
||||
|
||||
private fun getAfterFirstVisibleItemTop(visiblePosition: Int, movePercent: Float): Int {
|
||||
return when (mScrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.BOTTOM_TO_TOP -> (mStartMargin + mPerItemOffset * (visiblePosition - movePercent)).toInt()
|
||||
StackLayoutManager.ScrollOrientation.TOP_TO_BOTTOM -> (mStartMargin - mPerItemOffset * (visiblePosition - movePercent)).toInt()
|
||||
else -> mHeightSpace / 2
|
||||
}
|
||||
}
|
||||
|
||||
private fun getStartMargin(): Int {
|
||||
return when (mScrollOrientation) {
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT, StackLayoutManager.ScrollOrientation.LEFT_TO_RIGHT -> mWidthSpace / 2
|
||||
else -> mHeightSpace / 2
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,123 @@
|
||||
package com.gh.gamecenter.home.gamecollection
|
||||
|
||||
import android.content.Context
|
||||
import android.view.ViewGroup
|
||||
import androidx.core.graphics.drawable.toDrawable
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.gh.base.BaseActivity
|
||||
import com.gh.common.util.DirectUtils
|
||||
import com.gh.common.util.NewLogUtils
|
||||
import com.gh.common.util.safelyGetInRelease
|
||||
import com.gh.gamecenter.GameDetailActivity
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.HomeGameCollectionCardItemBinding
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class HomeGameCollectionAdapter(context: Context, private val entrance: String) :
|
||||
BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private var mGameCollectionItemDataList = listOf<GameCollectionListItemData>()
|
||||
|
||||
fun setGameCollectionList(gameCollectionItemDataList: List<GameCollectionListItemData>) {
|
||||
mGameCollectionItemDataList = gameCollectionItemDataList
|
||||
notifyDataSetChanged()
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) = HomeGameCollectionCardViewHolder(
|
||||
HomeGameCollectionCardItemBinding.bind(
|
||||
mLayoutInflater.inflate(
|
||||
R.layout.home_game_collection_card_item,
|
||||
parent,
|
||||
false
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
|
||||
if (holder is HomeGameCollectionCardViewHolder && mGameCollectionItemDataList.isNotEmpty()) {
|
||||
val realPosition: Int
|
||||
when (position) {
|
||||
0 -> {
|
||||
realPosition = getRealCount() - 2
|
||||
}
|
||||
1 -> {
|
||||
realPosition = getRealCount() - 1
|
||||
}
|
||||
itemCount - 2 -> {
|
||||
realPosition = 0
|
||||
}
|
||||
itemCount - 1 -> {
|
||||
realPosition = 1
|
||||
}
|
||||
else -> {
|
||||
realPosition = position - 2
|
||||
}
|
||||
}
|
||||
holder.bindGameListCard(mGameCollectionItemDataList[realPosition], entrance)
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount() = getRealCount() + 4
|
||||
|
||||
fun getRealCount() = mGameCollectionItemDataList.size
|
||||
|
||||
class HomeGameCollectionCardViewHolder(val binding: HomeGameCollectionCardItemBinding) :
|
||||
RecyclerView.ViewHolder(binding.root) {
|
||||
fun bindGameListCard(itemData: GameCollectionListItemData, entrance: String) {
|
||||
binding.run {
|
||||
val context = root.context
|
||||
val gamesCollectionEntity = itemData.gameCollectionItem
|
||||
if (gamesCollectionEntity != null) {
|
||||
entity = gamesCollectionEntity
|
||||
stampIv.setImageDrawable(if (gamesCollectionEntity.stamp == "official") R.drawable.ic_official.toDrawable() else R.drawable.ic_chosen.toDrawable())
|
||||
iconIvOne.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(0)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(entrance, "游戏单"), itemData.exposureEventList?.safelyGetInRelease(0))
|
||||
}
|
||||
}
|
||||
iconIvTwo.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(1)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(entrance, "游戏单"), itemData.exposureEventList?.safelyGetInRelease(1))
|
||||
}
|
||||
}
|
||||
iconIvThree.setOnClickListener {
|
||||
val game = gamesCollectionEntity.games?.get(2)
|
||||
NewLogUtils.logClickGameCollectionGameIcon(
|
||||
gamesCollectionEntity.title,
|
||||
gamesCollectionEntity.id,
|
||||
game?.name ?: "",
|
||||
game?.id ?: ""
|
||||
)
|
||||
game?.id?.let { id ->
|
||||
GameDetailActivity.startGameDetailActivity(context, id, BaseActivity.mergeEntranceAndPath(entrance, "游戏单"), itemData.exposureEventList?.safelyGetInRelease(2))
|
||||
}
|
||||
}
|
||||
userContainer.setOnClickListener {
|
||||
NewLogUtils.logClickGameCollectionAuthor(gamesCollectionEntity.title, gamesCollectionEntity.id)
|
||||
DirectUtils.directToHomeActivity(context, gamesCollectionEntity.user?.id, 0, entrance, "游戏单")
|
||||
}
|
||||
root.setOnClickListener {
|
||||
NewLogUtils.logEnterGameCollectionDetail(gamesCollectionEntity.title, gamesCollectionEntity.id)
|
||||
context.startActivity(GameCollectionDetailActivity.getIntent(context, gamesCollectionEntity.id, true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,46 @@
|
||||
package com.gh.gamecenter.home.gamecollection
|
||||
|
||||
import com.gh.base.BaseRecyclerViewHolder
|
||||
import com.gh.common.util.dip2px
|
||||
import com.gh.common.view.stacklayoutmanager.StackLayoutManager
|
||||
import com.gh.gamecenter.databinding.HomeGameCollectionItemBinding
|
||||
import com.gh.gamecenter.gamecollection.square.GameCollectionListItemData
|
||||
|
||||
class HomeGameCollectionViewHolder(val binding: HomeGameCollectionItemBinding) :
|
||||
BaseRecyclerViewHolder<Any>(binding.root) {
|
||||
fun bindGameCollectionList(gameCollectionItemDataList: List<GameCollectionListItemData>, entrance: String) {
|
||||
if (binding.recyclerView.adapter is HomeGameCollectionAdapter) {
|
||||
return
|
||||
}
|
||||
val adapter = HomeGameCollectionAdapter(binding.root.context, entrance)
|
||||
val manager = StackLayoutManager(
|
||||
StackLayoutManager.ScrollOrientation.RIGHT_TO_LEFT,
|
||||
3,
|
||||
GameCollectionStackAnimation::class.java,
|
||||
GameCollectionStackLayout::class.java
|
||||
)
|
||||
manager.setItemOffset(9F.dip2px())
|
||||
manager.setItemChangedListener(object : StackLayoutManager.ItemChangedListener {
|
||||
override fun onItemChanged(position: Int) {
|
||||
when (position) {
|
||||
0 -> binding.recyclerView.scrollToPosition(adapter.itemCount - 3)
|
||||
// 最后一个item跳转
|
||||
adapter.itemCount - 3 -> binding.recyclerView.scrollToPosition(1)
|
||||
}
|
||||
}
|
||||
})
|
||||
binding.recyclerView.layoutManager = manager
|
||||
binding.recyclerView.adapter = adapter
|
||||
binding.recyclerView.isNestedScrollingEnabled = false
|
||||
|
||||
adapter.setGameCollectionList(gameCollectionItemDataList)
|
||||
binding.recyclerView.post {
|
||||
// 定位到实际的第一个item
|
||||
manager.scrollToPosition(FIRST_ITEM_POSITION)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val FIRST_ITEM_POSITION = 2
|
||||
}
|
||||
}
|
||||
@ -26,6 +26,7 @@ import com.gh.gamecenter.entity.LinkEntity;
|
||||
import com.gh.gamecenter.entity.MessageEntity;
|
||||
import com.gh.gamecenter.entity.MessageFold;
|
||||
import com.gh.gamecenter.entity.UserEntity;
|
||||
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity;
|
||||
import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity;
|
||||
import com.gh.gamecenter.manager.UserManager;
|
||||
import com.gh.gamecenter.qa.answer.detail.SimpleAnswerDetailActivity;
|
||||
@ -413,6 +414,50 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder<MessageEntity>
|
||||
mBinding.messageArticleIcon.setVisibility(View.GONE);
|
||||
voteMoreUser(messageEntity);
|
||||
break;
|
||||
case "game_list_vote":
|
||||
mBinding.messageCommand.setText("赞了你的游戏单");
|
||||
mBinding.messageContent.setVisibility(View.GONE);
|
||||
mBinding.messageOriginalTitle.setText(messageEntity.getGameList().getTitle());
|
||||
targetUrl = messageEntity.getGameList().getCover();
|
||||
ImageUtils.displayIcon(mBinding.messageAskIcon, targetUrl);
|
||||
mBinding.messageAskIcon.setVisibility(View.VISIBLE);
|
||||
mBinding.messageArticleIcon.setVisibility(View.GONE);
|
||||
voteMoreUser(messageEntity);
|
||||
break;
|
||||
case "game_list_comment":
|
||||
mBinding.messageCommand.setText("评论了你");
|
||||
mBinding.messageContent.setVisibility(View.VISIBLE);
|
||||
mBinding.messageContent.setMaxLines(Integer.MAX_VALUE);
|
||||
mBinding.messageContent.setText(messageEntity.getComment().getContent());
|
||||
mBinding.messageOriginalTitle.setText(messageEntity.getGameList().getTitle());
|
||||
targetUrl = messageEntity.getGameList().getCover();
|
||||
ImageUtils.displayIcon(mBinding.messageAskIcon, targetUrl);
|
||||
mBinding.messageAskIcon.setVisibility(View.VISIBLE);
|
||||
mBinding.messageArticleIcon.setVisibility(View.GONE);
|
||||
voteMoreUser(messageEntity);
|
||||
break;
|
||||
case "game_list_comment_reply":
|
||||
mBinding.messageCommand.setText("回复了你");
|
||||
mBinding.messageContent.setVisibility(View.VISIBLE);
|
||||
mBinding.messageContent.setMaxLines(Integer.MAX_VALUE);
|
||||
mBinding.messageContent.setText(messageEntity.getComment().getContent());
|
||||
mBinding.messageOriginalTitle.setText(messageEntity.getGameList().getTitle());
|
||||
targetUrl = messageEntity.getGameList().getCover();
|
||||
ImageUtils.displayIcon(mBinding.messageAskIcon, targetUrl);
|
||||
mBinding.messageAskIcon.setVisibility(View.VISIBLE);
|
||||
mBinding.messageArticleIcon.setVisibility(View.GONE);
|
||||
voteMoreUser(messageEntity);
|
||||
break;
|
||||
case "game_list_comment_vote":
|
||||
mBinding.messageCommand.setText("赞了你的评论");
|
||||
mBinding.messageContent.setVisibility(View.GONE);
|
||||
mBinding.messageOriginalTitle.setText(messageEntity.getGameList().getTitle());
|
||||
targetUrl = messageEntity.getGameList().getCover();
|
||||
ImageUtils.displayIcon(mBinding.messageAskIcon, targetUrl);
|
||||
mBinding.messageAskIcon.setVisibility(View.VISIBLE);
|
||||
mBinding.messageArticleIcon.setVisibility(View.GONE);
|
||||
voteMoreUser(messageEntity);
|
||||
break;
|
||||
}
|
||||
switch (messageEntity.getType()) {
|
||||
case "video_comment":
|
||||
@ -661,6 +706,33 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder<MessageEntity>
|
||||
context.startActivity(WebActivity.getIntent(context, entity.getActivity().getUrlComment(), true));
|
||||
}
|
||||
break;
|
||||
|
||||
case "game_list_comment":
|
||||
context.startActivity(GameCollectionDetailActivity.getIntent(context, entity.getGameList().getId(), false, true));
|
||||
break;
|
||||
case "game_list_comment_reply":
|
||||
context.startActivity(CommentActivity.getGameCollectionCommentDetailIntent(
|
||||
context,
|
||||
entity.getComment().getTopId(),
|
||||
entity.getGameList().getId(),
|
||||
false,
|
||||
entrance,
|
||||
path
|
||||
));
|
||||
break;
|
||||
case "game_list_comment_vote":
|
||||
context.startActivity(CommentActivity.getGameCollectionCommentDetailIntent(
|
||||
context,
|
||||
entity.getComment().getId(),
|
||||
entity.getGameList().getId(),
|
||||
false,
|
||||
entrance,
|
||||
path
|
||||
));
|
||||
break;
|
||||
case "game_list_vote":
|
||||
context.startActivity(GameCollectionDetailActivity.getIntent(context, entity.getGameList().getId(), false));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -3,15 +3,26 @@ package com.gh.gamecenter.mygame
|
||||
import android.content.Context
|
||||
import android.content.Intent
|
||||
import android.os.Bundle
|
||||
import android.view.Gravity
|
||||
import android.view.MenuItem
|
||||
import android.widget.LinearLayout
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.gh.base.BaseActivity_TabLayout
|
||||
import com.gh.common.util.MtaHelper
|
||||
import com.gh.common.AppExecutor
|
||||
import com.gh.common.constant.Constants
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.view.BugFixedPopupWindow
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.databinding.PopupMyGameGuideBinding
|
||||
import com.gh.gamecenter.gamecollection.publish.GameCollectionEditActivity
|
||||
|
||||
class MyGameActivity : BaseActivity_TabLayout() {
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
setNavigationTitle("我的游戏")
|
||||
setToolbarMenu(R.menu.menu_my_game)
|
||||
showGuide()
|
||||
}
|
||||
|
||||
override fun initFragmentList(fragments: MutableList<Fragment>?) {
|
||||
@ -32,6 +43,33 @@ class MyGameActivity : BaseActivity_TabLayout() {
|
||||
MtaHelper.onEvent("我的光环_新", "我的游戏", "${mTabTitleList[position]}Tab")
|
||||
}
|
||||
|
||||
override fun onMenuItemClick(item: MenuItem?): Boolean {
|
||||
if (item?.itemId == R.id.menu_create_game_collection) {
|
||||
showRegulationTestDialogIfNeeded {
|
||||
startActivity(GameCollectionEditActivity.getIntent(this, mEntrance, "我的游戏"))
|
||||
}
|
||||
}
|
||||
return super.onMenuItemClick(item)
|
||||
}
|
||||
|
||||
private fun showGuide() {
|
||||
AppExecutor.uiExecutor.executeWithDelay({
|
||||
if (!SPUtils.getBoolean(Constants.SP_MY_GAME_GUIDE)) {
|
||||
val binding = PopupMyGameGuideBinding.inflate(layoutInflater, null, false)
|
||||
val popupWindow = BugFixedPopupWindow(binding.root, LinearLayout.LayoutParams.MATCH_PARENT, LinearLayout.LayoutParams.MATCH_PARENT)
|
||||
binding.root.setOnClickListener {
|
||||
SPUtils.setBoolean(Constants.SP_MY_GAME_GUIDE, true)
|
||||
popupWindow.dismiss()
|
||||
}
|
||||
popupWindow.run {
|
||||
isTouchable = true
|
||||
isFocusable = true
|
||||
showAtLocation(window.decorView, Gravity.TOP, 0, 0)
|
||||
}
|
||||
}
|
||||
}, 500)
|
||||
}
|
||||
|
||||
companion object {
|
||||
@JvmStatic
|
||||
fun getIntentWithConfig(context: Context, defaultFragmentPosition: Int): Intent {
|
||||
|
||||
@ -16,8 +16,8 @@ import io.reactivex.android.schedulers.AndroidSchedulers
|
||||
import io.reactivex.schedulers.Schedulers
|
||||
import okhttp3.ResponseBody
|
||||
|
||||
class PlayedGameViewModel(application: Application, var userId: String)
|
||||
: ListViewModel<GameEntity, GameEntity>(application) {
|
||||
class PlayedGameViewModel(application: Application, var userId: String, val isKeepTagStyle: Boolean = false) :
|
||||
ListViewModel<GameEntity, GameEntity>(application) {
|
||||
|
||||
override fun provideDataObservable(page: Int): Observable<MutableList<GameEntity>>? {
|
||||
return null
|
||||
@ -29,9 +29,11 @@ class PlayedGameViewModel(application: Application, var userId: String)
|
||||
|
||||
override fun mergeResultLiveData() {
|
||||
mResultLiveData.addSource(mListLiveData) {
|
||||
it.forEach { game ->
|
||||
game.hideSizeInsideDes = true
|
||||
game.tagStyle.clear()
|
||||
if (!isKeepTagStyle) {
|
||||
it.forEach { game ->
|
||||
game.hideSizeInsideDes = true
|
||||
game.tagStyle.clear()
|
||||
}
|
||||
}
|
||||
mResultLiveData.postValue(it)
|
||||
}
|
||||
@ -40,32 +42,32 @@ class PlayedGameViewModel(application: Application, var userId: String)
|
||||
@SuppressLint("CheckResult")
|
||||
fun deletePlayedGame(gameEntity: GameEntity) {
|
||||
RetrofitManager.getInstance(getApplication()).api
|
||||
.deletePlayedGame(userId, gameEntity.playedGameId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
mListLiveData.value?.let {
|
||||
for (game in it) {
|
||||
if (gameEntity.id == game.id) {
|
||||
it.remove(game)
|
||||
mListLiveData.postValue(it)
|
||||
break
|
||||
}
|
||||
.deletePlayedGame(userId, gameEntity.playedGameId)
|
||||
.subscribeOn(Schedulers.io())
|
||||
.observeOn(AndroidSchedulers.mainThread())
|
||||
.subscribe(object : BiResponse<ResponseBody>() {
|
||||
override fun onSuccess(data: ResponseBody) {
|
||||
mListLiveData.value?.let {
|
||||
for (game in it) {
|
||||
if (gameEntity.id == game.id) {
|
||||
it.remove(game)
|
||||
mListLiveData.postValue(it)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
Utils.toast(getApplication(), exception.localizedMessage)
|
||||
}
|
||||
})
|
||||
override fun onFailure(exception: Exception) {
|
||||
super.onFailure(exception)
|
||||
Utils.toast(getApplication(), exception.localizedMessage)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
class Factory(private val mUserId: String) : ViewModelProvider.NewInstanceFactory() {
|
||||
class Factory(private val mUserId: String, val isKeepTagStyle: Boolean = false) : ViewModelProvider.NewInstanceFactory() {
|
||||
override fun <T : ViewModel> create(modelClass: Class<T>): T {
|
||||
return PlayedGameViewModel(HaloApp.getInstance().application, mUserId) as T
|
||||
return PlayedGameViewModel(HaloApp.getInstance().application, mUserId, isKeepTagStyle) as T
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -18,6 +18,7 @@ import com.gh.gamecenter.entity.FunctionalGroupEntity
|
||||
import com.gh.gamecenter.entity.FunctionalLinkEntity
|
||||
import com.gh.gamecenter.entity.FunctionalMessageType
|
||||
import com.gh.gamecenter.game.upload.GameSubmissionActivity
|
||||
import com.gh.gamecenter.gamecollection.mine.MyGameCollectionActivity
|
||||
import com.gh.gamecenter.gamedetail.myrating.MyRatingActivity
|
||||
import com.gh.gamecenter.history.HistoryActivity
|
||||
import com.gh.gamecenter.manager.UserManager
|
||||
@ -32,8 +33,8 @@ import com.gh.gamecenter.video.videomanager.VideoManagerActivity
|
||||
import com.halo.assistant.HaloApp
|
||||
import com.lightgame.adapter.BaseRecyclerAdapter
|
||||
|
||||
class PersonalFunctionAdapter(val context: Context, val groupName: String, var mEntityList: ArrayList<FunctionalLinkEntity>)
|
||||
: BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
class PersonalFunctionAdapter(val context: Context, val groupName: String, var mEntityList: ArrayList<FunctionalLinkEntity>) :
|
||||
BaseRecyclerAdapter<RecyclerView.ViewHolder>(context) {
|
||||
|
||||
private var mDisplayUpdateHint = false
|
||||
private val gameTrendsDao = GameTrendsDao(HaloApp.getInstance().application)
|
||||
@ -287,6 +288,13 @@ class PersonalFunctionAdapter(val context: Context, val groupName: String, var m
|
||||
"青少年模式" -> {
|
||||
context.startActivity(TeenagerModeActivity.getIntent(context))
|
||||
}
|
||||
"我的游戏单" -> {
|
||||
if (UserManager.getInstance().isLoggedIn) {
|
||||
context.startActivity(MyGameCollectionActivity.getIntent(context))
|
||||
} else {
|
||||
CheckLoginUtils.checkLogin(context, "我的光环-我的游戏单") { }
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
DirectUtils.directToLinkPage(context, linkEntity, "", "我的光环")
|
||||
}
|
||||
|
||||
@ -44,7 +44,8 @@ class PersonalViewModel(application: Application) : AndroidViewModel(application
|
||||
Triple("浏览记录", R.drawable.personal_browsing_history, "浏览记录"),
|
||||
Triple("账号安全", R.drawable.personal_account_security, "账号安全"),
|
||||
Triple("模拟器游戏", R.drawable.personal_simulator_game, "模拟器游戏"),
|
||||
Triple("收货信息", R.drawable.personal_delivery_info, "收货信息")
|
||||
Triple("收货信息", R.drawable.personal_delivery_info, "收货信息"),
|
||||
Triple("我的游戏单", R.drawable.icon_game_collection, "我的游戏单")
|
||||
)
|
||||
private val contentCenterFuncs = arrayListOf(
|
||||
Triple("游戏动态", R.drawable.personal_game_dynamic, "游戏动态"),
|
||||
|
||||
@ -345,7 +345,7 @@ class UserHomeFragment : NormalFragment() {
|
||||
|
||||
val tag = "android:switcher:${mHomeBinding?.viewpager?.id}:"
|
||||
val gameFragment = childFragmentManager.findFragmentByTag("${tag}0")
|
||||
?: UserGameFragment.getInstance(mUserHomeViewModel.userId, count.gameComment)
|
||||
?: UserGameFragment.getInstance(mUserHomeViewModel.userId, count)
|
||||
val qaFragment = childFragmentManager.findFragmentByTag("${tag}1")
|
||||
?: UserHistoryFragment.getInstance(
|
||||
mUserHomeViewModel.userId,
|
||||
|
||||
@ -10,22 +10,24 @@ import android.widget.TextView
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.os.bundleOf
|
||||
import com.gh.common.util.*
|
||||
import com.gh.common.util.EntranceUtils.KEY_COMMENT_COUNT
|
||||
import com.gh.common.util.EntranceUtils.KEY_USER_ID
|
||||
import com.gh.common.util.EntranceUtils.*
|
||||
import com.gh.gamecenter.R
|
||||
import com.gh.gamecenter.collection.GamesCollectionFragment
|
||||
import com.gh.gamecenter.databinding.FragmentUserGameBinding
|
||||
import com.gh.gamecenter.entity.PersonalEntity
|
||||
import com.gh.gamecenter.normal.NormalFragment
|
||||
|
||||
class UserGameFragment: NormalFragment() {
|
||||
|
||||
private var mBinding: FragmentUserGameBinding? = null
|
||||
private var mViewModel: UserGameViewModel? = null
|
||||
private var mGamesCollectionFragment: GamesCollectionFragment? = null
|
||||
private var mPlayedGameFragment: UserPlayedGameFragment? = null
|
||||
private var mCommentFragment: UserCommentHistoryFragment? = null
|
||||
private var mUserId: String = ""
|
||||
private var mFilter: String = "全部"
|
||||
private var mCommentCount = 0
|
||||
private var mCurrentType = TYPE_PLAYED_GAME
|
||||
private var mCount = PersonalEntity.Count()
|
||||
private var mCurrentType = TYPE_GAME_COLLECTION
|
||||
|
||||
override fun getLayoutId() = 0
|
||||
|
||||
@ -34,9 +36,9 @@ class UserGameFragment: NormalFragment() {
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
mUserId = arguments?.getString(KEY_USER_ID, "") ?: ""
|
||||
mCommentCount = arguments?.getInt(KEY_COMMENT_COUNT, 0) ?: 0
|
||||
mCount = arguments?.getParcelable(COUNT) ?: PersonalEntity.Count()
|
||||
mViewModel = viewModelProvider(UserGameViewModel.Factory(mUserId))
|
||||
changeType(TYPE_PLAYED_GAME)
|
||||
changeType(TYPE_GAME_COLLECTION)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
@ -47,20 +49,23 @@ class UserGameFragment: NormalFragment() {
|
||||
}
|
||||
|
||||
mBinding?.run {
|
||||
commentType.text = "评论 $mCommentCount"
|
||||
gameCollectionType.text = "游戏单 ${mCount.gameList}"
|
||||
commentType.text = "评论 ${mCount.gameComment}"
|
||||
|
||||
gameType.setOnClickListener {
|
||||
if (mCurrentType == TYPE_PLAYED_GAME) return@setOnClickListener
|
||||
mCurrentType = TYPE_PLAYED_GAME
|
||||
updateTypeView(TYPE_PLAYED_GAME)
|
||||
changeType(TYPE_PLAYED_GAME)
|
||||
}
|
||||
val typePairList = arrayListOf(
|
||||
Pair(TYPE_GAME_COLLECTION, gameCollectionType),
|
||||
Pair(TYPE_PLAYED_GAME, gameType),
|
||||
Pair(TYPE_COMMENT, commentType)
|
||||
)
|
||||
|
||||
commentType.setOnClickListener {
|
||||
if (mCurrentType == TYPE_COMMENT) return@setOnClickListener
|
||||
mCurrentType = TYPE_COMMENT
|
||||
updateTypeView(TYPE_COMMENT)
|
||||
changeType(TYPE_COMMENT)
|
||||
typePairList.forEach { pair ->
|
||||
pair.second.setOnClickListener {
|
||||
if (mCurrentType != pair.first) {
|
||||
mCurrentType = pair.first
|
||||
updateTypeView(pair.first)
|
||||
changeType(pair.first)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
commentSubType.setOnClickListener {
|
||||
@ -71,35 +76,62 @@ class UserGameFragment: NormalFragment() {
|
||||
|
||||
private fun updateTypeView(type: Int) {
|
||||
mBinding?.run {
|
||||
if (type == TYPE_PLAYED_GAME) {
|
||||
commentSubType.visibility = View.GONE
|
||||
arrowIv.visibility = View.GONE
|
||||
gameType.setBackgroundResource(R.drawable.button_round_f0f8ff)
|
||||
gameType.setTextColor(R.color.theme_font.toColor())
|
||||
commentType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
commentType.setTextColor(R.color.text_333333.toColor())
|
||||
} else {
|
||||
commentSubType.visibility = View.VISIBLE
|
||||
arrowIv.visibility = View.VISIBLE
|
||||
gameType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
gameType.setTextColor(R.color.text_333333.toColor())
|
||||
commentType.setBackgroundResource(R.drawable.button_round_f0f8ff)
|
||||
commentType.setTextColor(R.color.theme_font.toColor())
|
||||
when (type) {
|
||||
TYPE_GAME_COLLECTION -> {
|
||||
commentSubType.visibility = View.GONE
|
||||
arrowIv.visibility = View.GONE
|
||||
gameCollectionType.setBackgroundResource(R.drawable.button_round_f0f8ff)
|
||||
gameCollectionType.setTextColor(R.color.theme_font.toColor())
|
||||
gameType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
gameType.setTextColor(R.color.text_333333.toColor())
|
||||
commentType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
commentType.setTextColor(R.color.text_333333.toColor())
|
||||
}
|
||||
TYPE_PLAYED_GAME -> {
|
||||
commentSubType.visibility = View.GONE
|
||||
arrowIv.visibility = View.GONE
|
||||
gameCollectionType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
gameCollectionType.setTextColor(R.color.text_333333.toColor())
|
||||
gameType.setBackgroundResource(R.drawable.button_round_f0f8ff)
|
||||
gameType.setTextColor(R.color.theme_font.toColor())
|
||||
commentType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
commentType.setTextColor(R.color.text_333333.toColor())
|
||||
}
|
||||
else -> {
|
||||
commentSubType.visibility = View.VISIBLE
|
||||
arrowIv.visibility = View.VISIBLE
|
||||
gameCollectionType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
gameCollectionType.setTextColor(R.color.text_333333.toColor())
|
||||
gameType.setBackgroundResource(R.drawable.button_round_fafafa)
|
||||
gameType.setTextColor(R.color.text_333333.toColor())
|
||||
commentType.setBackgroundResource(R.drawable.button_round_f0f8ff)
|
||||
commentType.setTextColor(R.color.theme_font.toColor())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun changeType(type: Int) {
|
||||
if (type == TYPE_PLAYED_GAME) {
|
||||
mPlayedGameFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? UserPlayedGameFragment
|
||||
when (type) {
|
||||
TYPE_GAME_COLLECTION -> {
|
||||
mGamesCollectionFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? GamesCollectionFragment
|
||||
?: GamesCollectionFragment()
|
||||
mGamesCollectionFragment?.arguments =
|
||||
bundleOf(KEY_USER_ID to mUserId, KEY_TYPE to GamesCollectionFragment.TYPE_USER)
|
||||
childFragmentManager.beginTransaction().replace(R.id.contentContainer, mGamesCollectionFragment!!, GamesCollectionFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
}
|
||||
TYPE_PLAYED_GAME -> {
|
||||
mPlayedGameFragment = childFragmentManager.findFragmentByTag(UserPlayedGameFragment::class.java.simpleName) as? UserPlayedGameFragment
|
||||
?: UserPlayedGameFragment()
|
||||
mPlayedGameFragment?.arguments = bundleOf(KEY_USER_ID to mUserId)
|
||||
childFragmentManager.beginTransaction().replace(R.id.contentContainer, mPlayedGameFragment!!, UserPlayedGameFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
} else {
|
||||
mCommentFragment = childFragmentManager.findFragmentByTag(UserCommentHistoryFragment::class.java.simpleName) as? UserCommentHistoryFragment
|
||||
mPlayedGameFragment?.arguments = bundleOf(KEY_USER_ID to mUserId)
|
||||
childFragmentManager.beginTransaction().replace(R.id.contentContainer, mPlayedGameFragment!!, UserPlayedGameFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
}
|
||||
else -> {
|
||||
mCommentFragment = childFragmentManager.findFragmentByTag(UserCommentHistoryFragment::class.java.simpleName) as? UserCommentHistoryFragment
|
||||
?: UserCommentHistoryFragment()
|
||||
mCommentFragment?.arguments = bundleOf(KEY_USER_ID to mUserId)
|
||||
childFragmentManager.beginTransaction().replace(R.id.contentContainer, mCommentFragment!!, UserCommentHistoryFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
mCommentFragment?.arguments = bundleOf(KEY_USER_ID to mUserId)
|
||||
childFragmentManager.beginTransaction().replace(R.id.contentContainer, mCommentFragment!!, UserCommentHistoryFragment::class.java.simpleName).commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,14 +186,16 @@ class UserGameFragment: NormalFragment() {
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val TYPE_PLAYED_GAME = 100
|
||||
private const val TYPE_COMMENT = 101
|
||||
private const val TYPE_GAME_COLLECTION = 100
|
||||
private const val TYPE_PLAYED_GAME = 101
|
||||
private const val TYPE_COMMENT = 102
|
||||
const val COUNT = "count"
|
||||
|
||||
fun getInstance(userId: String, commentCount: Int): UserGameFragment {
|
||||
fun getInstance(userId: String, count: PersonalEntity.Count): UserGameFragment {
|
||||
return UserGameFragment().apply {
|
||||
with(bundleOf(
|
||||
KEY_USER_ID to userId,
|
||||
KEY_COMMENT_COUNT to commentCount
|
||||
KEY_USER_ID to userId,
|
||||
COUNT to count
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user