diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index e1e996d8c3..08591511b7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -764,6 +764,10 @@
android:name=".qa.editor.InsertGameCollectionWrapperActivity"
android:screenOrientation="portrait" />
+
+
directToVideoTab(context)
- "toolkit" -> context.startActivity(ToolBoxActivity.getIntent(context, entrance))
+ "toolkit" -> context.startActivity(ToolBoxBlockActivity.getIntent(context, entrance))
"column_test" -> context.startActivity(
GameServerTestActivity.getIntent(
diff --git a/app/src/main/java/com/gh/common/util/EnvHelper.kt b/app/src/main/java/com/gh/common/util/EnvHelper.kt
index d097d187a9..53200a4c73 100644
--- a/app/src/main/java/com/gh/common/util/EnvHelper.kt
+++ b/app/src/main/java/com/gh/common/util/EnvHelper.kt
@@ -24,6 +24,19 @@ object EnvHelper {
}
}
+ @JvmStatic
+ fun getNewHost(): String {
+ return if (!isTestEnv()) {
+ Constants.NEW_API_HOST
+ } else {
+ if (isDevEnv) {
+ Constants.NEW_DEV_API_HOST
+ } else {
+ Constants.NEW_API_HOST
+ }
+ }
+ }
+
// 包体是否为测试包
@JvmStatic
fun isTestEnv(): Boolean {
diff --git a/app/src/main/java/com/gh/gamecenter/entity/ToolBoxBlockEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ToolBoxBlockEntity.kt
new file mode 100644
index 0000000000..7ea04d3c83
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/entity/ToolBoxBlockEntity.kt
@@ -0,0 +1,16 @@
+package com.gh.gamecenter.entity
+
+import android.os.Parcelable
+import com.google.gson.annotations.SerializedName
+import kotlinx.android.parcel.Parcelize
+
+@Parcelize
+data class ToolBoxBlockEntity(
+ @SerializedName("_id")
+ val categoryId: String = "",
+ @SerializedName("name")
+ val categoryName: String = "",
+ val total: Int = 0,
+ @SerializedName("data")
+ val toolboxList: List = ArrayList()
+) : Parcelable
diff --git a/app/src/main/java/com/gh/gamecenter/entity/ToolBoxEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/ToolBoxEntity.kt
index 72bce0d2b8..f878e1dd29 100644
--- a/app/src/main/java/com/gh/gamecenter/entity/ToolBoxEntity.kt
+++ b/app/src/main/java/com/gh/gamecenter/entity/ToolBoxEntity.kt
@@ -23,6 +23,9 @@ class ToolBoxEntity : Parcelable {
var time: Long = 0
+ // 用户上次打开使用时间(仅用于历史记录)
+ var lastOpenTime: Long = 0
+
@SerializedName("me")
var me: MeEntity? = null
@@ -41,6 +44,34 @@ class ToolBoxEntity : Parcelable {
dest.writeParcelable(this.me, flags)
}
+ override fun equals(other: Any?): Boolean {
+ if (this === other) return true
+ if (javaClass != other?.javaClass) return false
+
+ other as ToolBoxEntity
+
+ if (id != other.id) return false
+ if (icon != other.icon) return false
+ if (name != other.name) return false
+ if (des != other.des) return false
+ if (url != other.url) return false
+ if (time != other.time) return false
+ if (me != other.me) return false
+
+ return true
+ }
+
+ override fun hashCode(): Int {
+ var result = id?.hashCode() ?: 0
+ result = 31 * result + (icon?.hashCode() ?: 0)
+ result = 31 * result + (name?.hashCode() ?: 0)
+ result = 31 * result + (des?.hashCode() ?: 0)
+ result = 31 * result + (url?.hashCode() ?: 0)
+ result = 31 * result + time.hashCode()
+ result = 31 * result + (me?.hashCode() ?: 0)
+ return result
+ }
+
constructor()
protected constructor(`in`: Parcel) {
diff --git a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt
index adff595420..7e309808f2 100644
--- a/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/forum/home/WelfaresAdapter.kt
@@ -7,6 +7,7 @@ import com.gh.base.BaseRecyclerViewHolder
import com.gh.common.util.*
import com.gh.gamecenter.*
import com.gh.gamecenter.databinding.ForumWelfareItemBinding
+import com.gh.gamecenter.toolbox.ToolBoxBlockActivity
import com.lightgame.adapter.BaseRecyclerAdapter
class WelfaresAdapter(context: Context,
@@ -31,7 +32,7 @@ class WelfaresAdapter(context: Context,
when (entity.second) {
"工具箱" -> {
NewLogUtils.logForumPageEvent("click_forum_toolbox")
- mContext.startActivity(ToolBoxActivity.getIntent(mContext, "(社区-论坛:工具箱)"))
+ mContext.startActivity(ToolBoxBlockActivity.getIntent(mContext, "(社区-论坛:工具箱)"))
}
"礼包中心" -> {
diff --git a/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt b/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt
index 1853a5c43e..23ebc78a4c 100644
--- a/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt
+++ b/app/src/main/java/com/gh/gamecenter/personal/PersonalFunctionAdapter.kt
@@ -28,6 +28,7 @@ import com.gh.gamecenter.qa.myqa.MyAskActivity
import com.gh.gamecenter.security.SecurityActivity
import com.gh.gamecenter.simulatorgame.SimulatorGameActivity
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
+import com.gh.gamecenter.toolbox.ToolBoxBlockActivity
import com.gh.gamecenter.user.UserViewModel
import com.gh.gamecenter.video.videomanager.VideoManagerActivity
import com.halo.assistant.HaloApp
@@ -248,7 +249,7 @@ class PersonalFunctionAdapter(val context: Context, val groupName: String, var m
"工具箱" -> {
DataCollectionUtils.uploadClick(context, "工具箱", "发现")
- context.startActivity(ToolBoxActivity.getIntent(context, "(发现:工具箱)"))
+ context.startActivity(ToolBoxBlockActivity.getIntent(context, "(发现:工具箱)"))
}
"安装包清理" -> {
DataCollectionUtils.uploadClick(context, "安装包清理", "发现")
diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java
index 9c24c9ca8d..b6865dd421 100644
--- a/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java
+++ b/app/src/main/java/com/gh/gamecenter/retrofit/RetrofitManager.java
@@ -31,6 +31,7 @@ public class RetrofitManager {
private static final int UPLOAD_CALL_TIME_OUT = 20; // 图片上传超时时间
private static final byte[] LOCK = new byte[0];
private final ApiService mApiService;
+ private final ApiService mNewApiService;
private final ApiService mUploadApiService;
public static T provideService(OkHttpClient client, String url, Class serviceCls) {
@@ -45,6 +46,7 @@ public class RetrofitManager {
Context context = HaloApp.getInstance().getApplicationContext();
OkHttpClient okHttpNormalConfig = getOkHttpConfig(context, 0, 2);
mApiService = provideService(okHttpNormalConfig, Config.API_HOST, ApiService.class);
+ mNewApiService = provideService(okHttpNormalConfig, Config.NEW_API_HOST, ApiService.class);
mUploadApiService = provideService(getOkHttpConfig(context, UPLOAD_CALL_TIME_OUT, 1), Config.API_HOST, ApiService.class);
}
@@ -72,6 +74,10 @@ public class RetrofitManager {
return mApiService;
}
+ public ApiService getNewApi() {
+ return mNewApiService;
+ }
+
public ApiService getUploadApi() {
return mUploadApiService;
}
diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
index 8fc6bb8c5d..82e662810b 100644
--- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
+++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java
@@ -356,6 +356,12 @@ public interface ApiService {
@POST("games/platform_requests/{platform_id}:vote")
Observable postVersionVote(@Path("platform_id") String platformId);
+ /**
+ * 获取工具箱分类数据
+ */
+ @GET("toolkit")
+ Observable> getCategoryToolKits();
+
/**
* 根据game_id获取工具箱数据
*/
diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt
new file mode 100644
index 0000000000..42dec2b571
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockActivity.kt
@@ -0,0 +1,246 @@
+package com.gh.gamecenter.toolbox
+
+import android.content.Context
+import android.content.Intent
+import android.os.Bundle
+import android.view.KeyEvent
+import android.view.MotionEvent
+import android.view.View
+import android.view.View.OnFocusChangeListener
+import android.view.inputmethod.EditorInfo
+import android.widget.EditText
+import android.widget.TextView
+import androidx.activity.viewModels
+import androidx.recyclerview.widget.LinearLayoutManager
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.base.ToolBarActivity
+import com.gh.common.util.*
+import com.gh.common.view.CustomLinkMovementMethod
+import com.gh.gamecenter.R
+import com.gh.gamecenter.SuggestionActivity
+import com.gh.gamecenter.baselist.LoadStatus
+import com.gh.gamecenter.databinding.ActivityToolboxBlockBinding
+import com.gh.gamecenter.suggest.SuggestType
+import com.google.android.material.appbar.AppBarLayout
+import com.lightgame.utils.Util_System_Keyboard
+import com.lightgame.utils.Utils
+
+
+class ToolBoxBlockActivity : ToolBarActivity() {
+
+ private val mViewModel: ToolBoxViewModel by viewModels()
+ private lateinit var mBinding: ActivityToolboxBlockBinding
+ private lateinit var mAdapter: ToolBoxBlockAdapter
+ private lateinit var mSearchAdapter: ToolBoxItemAdapter
+ private lateinit var mLayoutManager: LinearLayoutManager
+
+ private var mIsSearch = false // 记录页面状态 搜索页面/普通页面
+
+ override fun getLayoutId() = R.layout.activity_toolbox_block
+
+ override fun onCreate(savedInstanceState: Bundle?) {
+ super.onCreate(savedInstanceState)
+ mBinding = ActivityToolboxBlockBinding.bind(mContentView)
+ mAdapter = ToolBoxBlockAdapter(this)
+ mSearchAdapter = ToolBoxItemAdapter(this, false, mViewModel)
+ mLayoutManager = LinearLayoutManager(this@ToolBoxBlockActivity)
+ setNavigationTitle("光环工具箱")
+ mBinding.run {
+ toolboxRefresh.setColorSchemeResources(R.color.theme)
+ toolboxRefresh.setOnRefreshListener {
+ refresh()
+ }
+ feedbackTv.text = SpanBuilder("需要其他工具,点击反馈").click(7, 11, R.color.theme, false) {
+ SuggestionActivity.startSuggestionActivity(
+ this@ToolBoxBlockActivity,
+ SuggestType.normal,
+ null,
+ null
+ )
+ }.build()
+ feedbackTv.movementMethod = CustomLinkMovementMethod.getInstance()
+ closeIv.setOnClickListener { mBinding.feedbackCv.visibility = View.GONE }
+ toolboxRv.adapter = mAdapter
+ toolboxRv.layoutManager = mLayoutManager
+ toolboxAppbar.addOnOffsetChangedListener(AppBarLayout.OnOffsetChangedListener { _, verticalOffset ->
+ toolboxRefresh.isEnabled = verticalOffset == 0
+ })
+ toolboxRv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
+ override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
+ super.onScrollStateChanged(recyclerView, newState)
+ if (newState == RecyclerView.SCROLL_STATE_IDLE && mLayoutManager.findLastVisibleItemPosition() == mSearchAdapter.itemCount - 1 && mIsSearch && mViewModel.searchStatus.value == LoadStatus.LIST_LOADED) {
+ mViewModel.getSearchResultList()
+ }
+ }
+ })
+ }
+ mViewModel.toolBoxBlockList.observeNonNull(this, { list ->
+ mAdapter.setDataList(list)
+ })
+ mViewModel.searchResultList.observeNonNull(this, { list ->
+ mSearchAdapter.setDataList(list)
+ })
+ mViewModel.loadStatus.observeNonNull(this, {
+ when (it) {
+ LoadStatus.INIT_LOADED -> loadDone()
+ LoadStatus.INIT_EMPTY -> loadEmpty()
+ LoadStatus.INIT_FAILED -> loadError()
+ }
+ })
+ mViewModel.searchStatus.observeNonNull(this, {
+ when (it) {
+ LoadStatus.INIT_LOADED -> loadDone()
+ LoadStatus.INIT_EMPTY -> loadEmpty()
+ LoadStatus.INIT_FAILED -> loadError()
+ LoadStatus.INIT_OVER,
+ LoadStatus.LIST_LOADED,
+ LoadStatus.LIST_FAILED,
+ LoadStatus.LIST_OVER -> {
+ loadDone()
+ mSearchAdapter.notifyItemChanged(mSearchAdapter.itemCount - 1)
+ }
+ }
+ })
+
+ initSearch()
+ }
+
+ override fun onResume() {
+ super.onResume()
+ if (!mIsSearch) mViewModel.getToolBoxList()
+ }
+
+ private fun loadDone() {
+ mBinding.toolboxRv.visibility = View.VISIBLE
+ mBinding.toolboxRefresh.isRefreshing = false
+ mBinding.reuseNoneData.root.visibility = View.GONE
+ mBinding.reuseNoConnection.root.visibility = View.GONE
+ mBinding.reuseLoading.root.visibility = View.GONE
+ }
+
+ private fun loadError() {
+ mBinding.toolboxRv.visibility = View.GONE
+ mBinding.toolboxRefresh.isRefreshing = false
+ mBinding.reuseNoneData.root.visibility = View.GONE
+ mBinding.reuseNoConnection.root.visibility = View.VISIBLE
+ mBinding.reuseLoading.root.visibility = View.GONE
+ mBinding.reuseNoConnection.root.setOnClickListener {
+ refresh()
+ mBinding.reuseNoConnection.root.visibility = View.GONE
+ mBinding.reuseLoading.root.visibility = View.VISIBLE
+ }
+ }
+
+ private fun loadEmpty() {
+ mBinding.toolboxRv.visibility = View.GONE
+ mBinding.toolboxRefresh.isRefreshing = false
+ mBinding.reuseNoneData.root.visibility = View.VISIBLE
+ mBinding.reuseNoConnection.root.visibility = View.GONE
+ mBinding.reuseLoading.root.visibility = View.GONE
+ mBinding.reuseNoneData.reuseTvNoneData.text =
+ if (mIsSearch) "未找到结果,点我反馈" else resources.getString(R.string.game_empty)
+ mBinding.reuseNoneData.root.setOnClickListener {
+ if (mIsSearch) SuggestionActivity.startSuggestionActivity(
+ this,
+ SuggestType.functionSuggest,
+ null,
+ null
+ )
+ }
+ }
+
+ private fun refresh() {
+ if (mIsSearch) {
+ mViewModel.getSearchResultList(true)
+ } else {
+ mViewModel.getToolBoxList()
+ }
+ }
+
+ private fun search(isSearch: Boolean, searchKey: String) {
+ if (mBinding.reuseNoneData.root.visibility == View.VISIBLE) {
+ mBinding.reuseNoneData.root.visibility = View.GONE
+ }
+ mIsSearch = isSearch
+ mViewModel.mSearchKey = searchKey
+ changeAdapter(isSearch)
+ if (isSearch) {
+ mBinding.reuseLoading.root.visibility = View.VISIBLE
+ mBinding.toolboxRv.visibility = View.GONE
+ mViewModel.getSearchResultList(true)
+ }
+ }
+
+ private fun initSearch() {
+ val backTv = mBinding.reuseSearchBar.tvBack
+ val searchTv = mBinding.reuseSearchBar.tvSearch
+ val searchEt = mBinding.reuseSearchBar.etSearch
+ mBinding.reuseSearchBar.root.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px())
+ backTv.setOnClickListener {
+ search(false, "")
+ searchEt.text.clear()
+ }
+ TextHelper.limitTheLengthOfEditText(searchEt, 20,
+ object : TextHelper.ExceedTextLengthLimitCallback {
+ override fun onExceed() {
+ toast("最多输入20字")
+ }
+ })
+ searchTv.setOnClickListener {
+ if (searchEt.text.toString().isEmpty()) {
+ Utils.toast(this, R.string.search_hint)
+ return@setOnClickListener
+ }
+ Util_System_Keyboard.hideSoftKeyboard(this, searchEt)
+ search(true, searchEt.text.toString())
+ }
+ searchEt.onFocusChangeListener = OnFocusChangeListener { _: View?, hasFocus: Boolean ->
+ if (!hasFocus) {
+ Util_System_Keyboard.hideSoftKeyboard(this, searchEt)
+ }
+ }
+ searchEt.setOnEditorActionListener { _: TextView?, actionId: Int, _: KeyEvent? ->
+ if (actionId == EditorInfo.IME_ACTION_SEARCH) {
+ searchTv.performClick()
+ }
+ false
+ }
+ }
+
+ private fun changeAdapter(isSearch: Boolean) {
+ mBinding.toolboxRv.adapter = if (isSearch) mSearchAdapter else mAdapter
+ mBinding.reuseSearchBar.tvBack.visibility = if (mIsSearch) View.VISIBLE else View.GONE
+ }
+
+ override fun dispatchTouchEvent(ev: MotionEvent?): Boolean {
+ if (ev?.action == MotionEvent.ACTION_DOWN) {
+ val v = currentFocus
+ if (isShouldHideKeyboard(v, ev)) {
+ Util_System_Keyboard.hideSoftKeyboard(this)
+ }
+ }
+ return super.dispatchTouchEvent(ev)
+ }
+
+ private fun isShouldHideKeyboard(v: View?, event: MotionEvent): Boolean {
+ if (v is EditText) {
+ val l = intArrayOf(0, 0)
+ v.getLocationInWindow(l)
+ val left = l[0]
+ val top = l[1]
+ val bottom = top + v.getHeight()
+ val right = left + v.getWidth()
+ return (event.x <= left || event.x >= right || event.y <= top || event.y >= bottom)
+ }
+ return false
+ }
+
+ companion object {
+ @JvmStatic
+ fun getIntent(context: Context, entrance: String?): Intent {
+ val intent = Intent(context, ToolBoxBlockActivity::class.java)
+ intent.putExtra(EntranceUtils.KEY_ENTRANCE, entrance)
+ return intent
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockAdapter.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockAdapter.kt
new file mode 100644
index 0000000000..eaef153009
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxBlockAdapter.kt
@@ -0,0 +1,97 @@
+package com.gh.gamecenter.toolbox
+
+import android.content.Context
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.GridLayoutManager
+import com.gh.base.BaseRecyclerViewHolder
+import com.gh.common.util.goneIf
+import com.gh.common.util.safelyGetInRelease
+import com.gh.gamecenter.baselist.ListExecutor
+import com.gh.gamecenter.databinding.ToolboxBlockItemBinding
+import com.gh.gamecenter.entity.ToolBoxBlockEntity
+import com.lightgame.adapter.BaseRecyclerAdapter
+import java.util.*
+
+class ToolBoxBlockAdapter(context: Context) : BaseRecyclerAdapter(context) {
+
+ private var mEntityList = listOf()
+
+ fun setDataList(dataList: List) {
+ if (dataList.isEmpty() || mEntityList.size > dataList.size) {
+ mEntityList = dataList
+ notifyDataSetChanged()
+ return
+ }
+ ListExecutor.workerExecutor.execute {
+ val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun getOldListSize(): Int {
+ return mEntityList.size
+ }
+
+ override fun getNewListSize(): Int {
+ return dataList.size
+ }
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ val oldItem = mEntityList.safelyGetInRelease(oldItemPosition)
+ val newItem = dataList.safelyGetInRelease(newItemPosition)
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(
+ oldItemPosition: Int,
+ newItemPosition: Int
+ ): Boolean {
+ val oldItem = mEntityList.safelyGetInRelease(oldItemPosition)
+ val newItem = dataList.safelyGetInRelease(newItemPosition)
+ return oldItem == newItem
+ }
+ })
+ ListExecutor.uiExecutor.execute {
+ mEntityList = ArrayList(dataList)
+ diffResult.dispatchUpdatesTo(this)
+ }
+ }
+ }
+
+ override fun onBindViewHolder(holder: ToolBoxBlockViewHolder, position: Int) {
+ holder.bindToolBoxBlock(mEntityList[position])
+ holder.binding.divider.goneIf(position == itemCount - 1 || mEntityList[position].categoryName == "最近使用")
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+ ToolBoxBlockViewHolder(ToolboxBlockItemBinding.inflate(mLayoutInflater, parent, false))
+
+ override fun getItemCount() = mEntityList.size
+
+ class ToolBoxBlockViewHolder(val binding: ToolboxBlockItemBinding) :
+ BaseRecyclerViewHolder(binding.root) {
+
+ private var mIsExpand = false
+ private var mAdapter: ToolBoxItemAdapter? = null
+
+ fun bindToolBoxBlock(toolBoxBlockEntity: ToolBoxBlockEntity) {
+ val context = binding.root.context
+ mAdapter = ToolBoxItemAdapter(context, true)
+ mAdapter?.setDataList(toolBoxBlockEntity.toolboxList.take(4))
+ binding.titleTv.text = toolBoxBlockEntity.categoryName
+ binding.toolboxRv.layoutManager = GridLayoutManager(context, 2)
+ binding.toolboxRv.adapter = mAdapter
+ binding.expandContainer.goneIf(toolBoxBlockEntity.toolboxList.size < 5)
+ binding.expandContainer.setOnClickListener {
+ mIsExpand = !mIsExpand
+ if (mIsExpand)
+ mAdapter?.setDataList(toolBoxBlockEntity.toolboxList)
+ else
+ mAdapter?.setDataList(toolBoxBlockEntity.toolboxList.take(4))
+ binding.expandTv.text = if (mIsExpand) "收起" else "展开全部"
+ binding.expandIv.run {
+ pivotX = width / 2F
+ pivotY = height / 2F
+ rotation = if (mIsExpand) 180F else 0F
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt
new file mode 100644
index 0000000000..0422e0e19e
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxItemAdapter.kt
@@ -0,0 +1,174 @@
+package com.gh.gamecenter.toolbox
+
+import android.content.Context
+import android.view.View
+import android.view.ViewGroup
+import androidx.recyclerview.widget.DiffUtil
+import androidx.recyclerview.widget.RecyclerView
+import com.gh.base.BaseRecyclerViewHolder
+import com.gh.common.constant.Config
+import com.gh.common.constant.Constants
+import com.gh.common.constant.ItemViewType
+import com.gh.common.util.*
+import com.gh.common.util.ImageUtils.display
+import com.gh.gamecenter.NewsDetailActivity
+import com.gh.gamecenter.R
+import com.gh.gamecenter.WebActivity.Companion.getWebByCollectionTools
+import com.gh.gamecenter.adapter.viewholder.FooterViewHolder
+import com.gh.gamecenter.baselist.ListExecutor
+import com.gh.gamecenter.baselist.LoadStatus
+import com.gh.gamecenter.databinding.ItemToolboxBinding
+import com.gh.gamecenter.entity.ToolBoxEntity
+import com.google.gson.Gson
+import com.lightgame.adapter.BaseRecyclerAdapter
+import java.util.*
+
+class ToolBoxItemAdapter(context: Context, val isBlockInside: Boolean = false, val viewModel: ToolBoxViewModel? = null) :
+ BaseRecyclerAdapter(context) {
+
+ private var mEntityList: List = arrayListOf()
+
+ fun setDataList(dataList: List) {
+ if (dataList.isEmpty() || mEntityList.size > dataList.size || viewModel?.mReset == true) {
+ mEntityList = dataList
+ notifyDataSetChanged()
+ return
+ }
+ ListExecutor.workerExecutor.execute {
+ val diffResult = DiffUtil.calculateDiff(object : DiffUtil.Callback() {
+ override fun getOldListSize(): Int {
+ return mEntityList.size
+ }
+
+ override fun getNewListSize(): Int {
+ return dataList.size
+ }
+
+ override fun areItemsTheSame(oldItemPosition: Int, newItemPosition: Int): Boolean {
+ val oldItem = mEntityList.safelyGetInRelease(oldItemPosition)
+ val newItem = dataList.safelyGetInRelease(newItemPosition)
+ return oldItem == newItem
+ }
+
+ override fun areContentsTheSame(
+ oldItemPosition: Int,
+ newItemPosition: Int
+ ): Boolean {
+ val oldItem = mEntityList.safelyGetInRelease(oldItemPosition)
+ val newItem = dataList.safelyGetInRelease(newItemPosition)
+ return oldItem == newItem
+ }
+ })
+ ListExecutor.uiExecutor.execute {
+ mEntityList = ArrayList(dataList)
+ diffResult.dispatchUpdatesTo(this)
+ }
+ }
+ }
+
+ override fun getItemViewType(position: Int): Int {
+ return if (!isBlockInside && position == itemCount - 1 && itemCount != 0) {
+ ItemViewType.ITEM_FOOTER
+ } else {
+ ItemViewType.ITEM_BODY
+ }
+ }
+
+ override fun onCreateViewHolder(parent: ViewGroup, viewType: Int) =
+ if (viewType == ItemViewType.ITEM_FOOTER) {
+ FooterViewHolder(
+ mLayoutInflater.inflate(
+ R.layout.refresh_footerview,
+ parent,
+ false
+ )
+ )
+ } else {
+ ToolBoxViewHolder(
+ ItemToolboxBinding.bind(
+ mLayoutInflater.inflate(
+ R.layout.item_toolbox,
+ parent,
+ false
+ )
+ )
+ )
+ }
+
+ override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) {
+ if (holder is ToolBoxViewHolder) {
+ val toolBoxEntity = mEntityList[position]
+ initToolBoxViewHolder(holder, toolBoxEntity)
+ } else if (holder is FooterViewHolder) {
+ initFooterViewHolder(holder)
+ }
+ }
+
+ private fun initFooterViewHolder(viewHolder: FooterViewHolder) {
+ viewHolder.initItemPadding()
+ viewModel?.let {
+ when (viewModel.searchStatus.value) {
+ LoadStatus.LIST_FAILED -> {
+ viewHolder.loading.visibility = View.GONE
+ viewHolder.hint.setText(R.string.loading_failed_retry)
+ viewHolder.itemView.isClickable = true
+ viewHolder.itemView.setOnClickListener {
+ viewModel.getSearchResultList()
+ viewModel.searchStatus.postValue(LoadStatus.LIST_LOADING)
+ }
+ }
+ LoadStatus.INIT_OVER,
+ LoadStatus.LIST_OVER -> {
+ viewHolder.loading.visibility = View.GONE
+ viewHolder.hint.setText(R.string.load_over_hint)
+ viewHolder.itemView.isClickable = false
+ }
+ LoadStatus.LIST_LOADING -> {
+ viewHolder.loading.visibility = View.VISIBLE
+ viewHolder.hint.setText(R.string.loading)
+ viewHolder.itemView.isClickable = false
+ }
+ }
+ }
+ }
+
+ override fun getItemCount() = if (isBlockInside) mEntityList.size else if (mEntityList.isEmpty()) 0 else mEntityList.size + 1
+
+ private fun initToolBoxViewHolder(viewHolder: ToolBoxViewHolder, toolBoxEntity: ToolBoxEntity) {
+ viewHolder.binding.toolboxItemDes.text = toolBoxEntity.des
+ viewHolder.binding.toolboxItemTitle.text = toolBoxEntity.name
+ display(viewHolder.binding.toolboxItemGameThumb, toolBoxEntity.icon)
+ viewHolder.binding.divider.goneIf(isBlockInside)
+ viewHolder.binding.root.setOnClickListener {
+ if (SPUtils.getString(Constants.TOOLBOX_HISTORY).isEmpty()) {
+ SPUtils.setString(Constants.TOOLBOX_HISTORY, arrayListOf(toolBoxEntity.apply { lastOpenTime = System.currentTimeMillis() }).toJson())
+ } else {
+ val list = arrayListOf(*Gson().fromJson(SPUtils.getString(Constants.TOOLBOX_HISTORY), Array::class.java)).apply {
+ if (contains(toolBoxEntity)) {
+ remove(toolBoxEntity)
+ }
+ add(0, toolBoxEntity.apply { lastOpenTime = System.currentTimeMillis() })
+ }
+ SPUtils.setString(Constants.TOOLBOX_HISTORY, list.take(4).toJson())
+ }
+
+ val url = toolBoxEntity.url
+ if (url != null && url.contains(Config.URL_ARTICLE)) {
+ val newsId = url.substring(url.lastIndexOf("/") + 1, url.length - 5) // 5: ".html"
+ val intent = NewsDetailActivity.getIntentById(mContext, newsId, "工具箱列表")
+ mContext.startActivity(intent)
+ } else {
+ mContext.startActivity(
+ getWebByCollectionTools(
+ mContext,
+ toolBoxEntity,
+ false
+ )
+ )
+ }
+ }
+ }
+
+ class ToolBoxViewHolder(val binding: ItemToolboxBinding) :
+ BaseRecyclerViewHolder(binding.root)
+}
\ No newline at end of file
diff --git a/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxViewModel.kt b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxViewModel.kt
new file mode 100644
index 0000000000..afd9623f7a
--- /dev/null
+++ b/app/src/main/java/com/gh/gamecenter/toolbox/ToolBoxViewModel.kt
@@ -0,0 +1,116 @@
+package com.gh.gamecenter.toolbox
+
+import android.app.Application
+import androidx.lifecycle.AndroidViewModel
+import androidx.lifecycle.MutableLiveData
+import com.gh.common.constant.Constants
+import com.gh.common.util.*
+import com.gh.gamecenter.baselist.LoadStatus
+import com.gh.gamecenter.entity.ToolBoxBlockEntity
+import com.gh.gamecenter.entity.ToolBoxEntity
+import com.gh.gamecenter.retrofit.Response
+import com.gh.gamecenter.retrofit.RetrofitManager
+import com.google.gson.Gson
+import io.reactivex.android.schedulers.AndroidSchedulers
+import io.reactivex.schedulers.Schedulers
+import retrofit2.HttpException
+
+class ToolBoxViewModel(application: Application) : AndroidViewModel(application) {
+
+ private var mPage = 1
+ var mSearchKey = "" //搜索关键字
+ var mReset = false
+
+ val toolBoxBlockList = MutableLiveData>()
+ val searchResultList = MutableLiveData>()
+ val loadStatus = MutableLiveData()
+ val searchStatus = MutableLiveData()
+
+ private val resultList = mutableListOf()
+
+ fun getToolBoxList() {
+ RetrofitManager.getInstance().newApi.categoryToolKits
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Response>() {
+ override fun onResponse(response: List?) {
+ super.onResponse(response)
+ response?.let {
+ if (SPUtils.getString(Constants.TOOLBOX_HISTORY).isNotEmpty()) {
+ toolBoxBlockList.postValue(getNewList(it))
+ } else {
+ toolBoxBlockList.postValue(it)
+ }
+ }
+ loadStatus.postValue(if (response.isNullOrEmpty()) LoadStatus.INIT_EMPTY else LoadStatus.INIT_LOADED)
+ }
+
+ override fun onFailure(e: HttpException?) {
+ super.onFailure(e)
+ loadStatus.postValue(LoadStatus.INIT_FAILED)
+ }
+ })
+ }
+
+ // 添加最近使用列表
+ fun getNewList(list: List): List {
+ val historyList = listOf(*Gson().fromJson(SPUtils.getString(Constants.TOOLBOX_HISTORY), Array::class.java))
+ val validList = mutableListOf().apply {
+ // 最多展示30天以内的打开记录
+ for (item in historyList) {
+ val offsetDay = TimeUtils.getBeforeDays(item.lastOpenTime / 1000)
+ if (offsetDay <= 30) {
+ add(item)
+ }
+ }
+ }
+ SPUtils.setString(Constants.TOOLBOX_HISTORY, validList.take(4).toJson())
+ return if (validList.isNotEmpty()) arrayListOf(ToolBoxBlockEntity(categoryName = "最近使用", toolboxList = validList)).apply {
+ addAll(list)
+ } else list
+ }
+
+ fun getSearchResultList(reset: Boolean = false) {
+ mReset = reset
+ if (reset) {
+ mPage = 1
+ resultList.clear()
+ }
+ RetrofitManager.getInstance().api.getToolKitData(mPage, UrlFilterUtils.getFilterQuery("keyword", mSearchKey))
+ .subscribeOn(Schedulers.io())
+ .observeOn(AndroidSchedulers.mainThread())
+ .subscribe(object : Response>() {
+ override fun onResponse(response: List?) {
+ super.onResponse(response)
+ response?.let {
+ if (mPage != 1 && (response.isEmpty() || response.size < PAGE_SIZE)) {
+ searchStatus.postValue(LoadStatus.LIST_OVER)
+ resultList.addAll(response)
+ } else if (mPage == 1 && response.isEmpty()) {
+ searchStatus.postValue(LoadStatus.INIT_EMPTY)
+ resultList.clear()
+ } else if (mPage == 1 && response.size < PAGE_SIZE) {
+ searchStatus.postValue(LoadStatus.INIT_OVER)
+ resultList.clear()
+ resultList.addAll(response)
+ } else {
+ mPage++
+ searchStatus.postValue(if (mPage == 1) LoadStatus.INIT_LOADED else LoadStatus.LIST_LOADED)
+ if (mPage == 1) resultList.clear()
+ resultList.addAll(response)
+ }
+ searchResultList.postValue(resultList)
+ }
+ }
+
+ override fun onFailure(e: HttpException?) {
+ super.onFailure(e)
+ searchStatus.postValue(if (mPage == 1) LoadStatus.INIT_FAILED else LoadStatus.LIST_FAILED)
+ }
+ })
+ }
+
+ companion object {
+ const val PAGE_SIZE = 20
+ }
+}
\ No newline at end of file
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toolbox_cancel.webp b/app/src/main/res/drawable-xxxhdpi/ic_toolbox_cancel.webp
new file mode 100644
index 0000000000..f4bc16296f
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_toolbox_cancel.webp differ
diff --git a/app/src/main/res/drawable-xxxhdpi/ic_toolbox_down_arrow.webp b/app/src/main/res/drawable-xxxhdpi/ic_toolbox_down_arrow.webp
new file mode 100644
index 0000000000..f6bd0ad26a
Binary files /dev/null and b/app/src/main/res/drawable-xxxhdpi/ic_toolbox_down_arrow.webp differ
diff --git a/app/src/main/res/layout/activity_toolbox_block.xml b/app/src/main/res/layout/activity_toolbox_block.xml
new file mode 100644
index 0000000000..da78de6705
--- /dev/null
+++ b/app/src/main/res/layout/activity_toolbox_block.xml
@@ -0,0 +1,104 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/item_toolbox.xml b/app/src/main/res/layout/item_toolbox.xml
new file mode 100644
index 0000000000..d8a9cfcaae
--- /dev/null
+++ b/app/src/main/res/layout/item_toolbox.xml
@@ -0,0 +1,69 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/toolbox_block_item.xml b/app/src/main/res/layout/toolbox_block_item.xml
new file mode 100644
index 0000000000..09c750ce40
--- /dev/null
+++ b/app/src/main/res/layout/toolbox_block_item.xml
@@ -0,0 +1,57 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file