diff --git a/app/src/main/java/com/gh/common/util/DownloadItemUtils.java b/app/src/main/java/com/gh/common/util/DownloadItemUtils.java index b371fbfd54..925378de7c 100644 --- a/app/src/main/java/com/gh/common/util/DownloadItemUtils.java +++ b/app/src/main/java/com/gh/common/util/DownloadItemUtils.java @@ -413,7 +413,9 @@ public class DownloadItemUtils { } return; } else { - holder.recommendContainer.setVisibility(View.GONE); + if (holder.recommendContainer != null) { + holder.recommendContainer.setVisibility(View.GONE); + } } if (briefStyle != null && briefStyle.contains("star")) { diff --git a/app/src/main/java/com/gh/common/util/Extensions.kt b/app/src/main/java/com/gh/common/util/Extensions.kt index 4c731bf9fc..bb39df39db 100644 --- a/app/src/main/java/com/gh/common/util/Extensions.kt +++ b/app/src/main/java/com/gh/common/util/Extensions.kt @@ -15,7 +15,9 @@ import android.text.style.ImageSpan import android.text.style.URLSpan import android.util.TypedValue import android.view.Gravity +import android.view.LayoutInflater import android.view.View +import android.view.ViewGroup import android.view.inputmethod.InputMethodManager import android.widget.EditText import android.widget.PopupWindow @@ -29,6 +31,7 @@ import androidx.fragment.app.FragmentActivity import androidx.lifecycle.* import androidx.lifecycle.Observer import androidx.recyclerview.widget.RecyclerView +import androidx.viewbinding.ViewBinding import androidx.viewpager.widget.ViewPager import com.airbnb.lottie.LottieAnimationView import com.facebook.drawee.view.SimpleDraweeView @@ -54,6 +57,7 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import okhttp3.MediaType import okhttp3.RequestBody +import org.json.JSONObject import java.net.URI import java.util.* import java.util.concurrent.TimeUnit @@ -176,6 +180,18 @@ fun RecyclerView.doOnScrolledSpecificDistance(distanceX: Int = 0, distanceY: Int addOnScrollListener(listener) } +/** + * ViewBinding related Extensions + */ +inline fun ViewGroup.toBinding(): T { + return T::class.java.getMethod( + "inflate", + LayoutInflater::class.java, + ViewGroup::class.java, + Boolean::class.java + ).invoke(null, layoutInflater, this, false) as T +} + /** * View Extensions */ @@ -216,6 +232,9 @@ fun View.setDebouncedClickListener(action: () -> Unit) { setOnClickListener { debounceActionWithInterval(interval = 300L) { action.invoke() } } } +val View.layoutInflater: LayoutInflater + get() = LayoutInflater.from(this.context) + fun isPublishEnv(): Boolean { return BuildConfig.FLAVOR != "internal" } @@ -463,6 +482,10 @@ fun Any.toRequestBody(): RequestBody { return RequestBody.create(MediaType.parse("application/json"), json) } +fun JSONObject.toRequestBody(): RequestBody { + return RequestBody.create(MediaType.parse("application/json"), this.toString()) +} + // 对在浏览器(WebView)显示的路径进行转码 fun String.decodeURI(): String { return URI(null, null, this, null).rawPath diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java index 44c4649c35..76da4e2be7 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.java @@ -103,6 +103,7 @@ public class SplashScreenActivity extends BaseActivity { protected void onCreate(Bundle savedInstanceState) { mSharedPreferences = PreferenceManager.getDefaultSharedPreferences(this); mIsNewForThisVersion = mSharedPreferences.getBoolean("isNewFirstLaunchV" + PackageUtils.getVersionName(), true); + HaloApp.getInstance().isNewForThisVersion = mIsNewForThisVersion; super.onCreate(savedInstanceState); diff --git a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameViewHolder.java b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameViewHolder.java index 1a4b8cb666..b07df42731 100644 --- a/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameViewHolder.java +++ b/app/src/main/java/com/gh/gamecenter/adapter/viewholder/GameViewHolder.java @@ -25,6 +25,7 @@ public class GameViewHolder extends BaseRecyclerViewHolder { public LinearLayout gameLabelList; public LinearLayout gameInfo; public ProgressBar gameProgressbar; + @Nullable public View recommendContainer; public TextView recommendTv; public ImageView recommendIv; diff --git a/app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt b/app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt index b6af05b5c7..1400e53827 100644 --- a/app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/catalog/CatalogFragment.kt @@ -33,6 +33,9 @@ class CatalogFragment : LazyFragment() { mViewModel = viewModelProviderFromParent(CatalogViewModel.Factory(mCatalogId, mCatalogTitle), mCatalogId) mViewModel?.validEntranceName = if (mEntrance.contains("首页")) "首页" else "板块" + if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) { + mViewModel?.validEntranceName = "首页Tab栏" + } mViewModel?.logAppearance() super.onFragmentFirstVisible() diff --git a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt index 0568222554..a7d072f65e 100644 --- a/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt +++ b/app/src/main/java/com/gh/gamecenter/category2/CategoryV2Fragment.kt @@ -39,14 +39,15 @@ class CategoryV2Fragment : LazyFragment() { mCategoryTitle = arguments?.getString(EntranceUtils.KEY_CATEGORY_TITLE) ?: "" mViewModel = viewModelProviderFromParent(CategoryV2ViewModel.Factory(mCategoryId, mCategoryTitle), mCategoryId) + // 除了这里以外,下面还有一个判断是否为首页 tab 栏的赋值 mViewModel?.entrance = if (mEntrance.contains("首页")) "首页" else "板块" + if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) { + mHomeViewModel = viewModelProviderFromParent() + mViewModel?.entrance = "首页Tab栏" + } mViewModel?.logAppearance() super.onFragmentFirstVisible() - - if (arguments?.getBoolean(EntranceUtils.KEY_IS_HOME) == true) { - mHomeViewModel = viewModelProviderFromParent() - } } override fun onRealLayoutInflated(inflatedView: View) { diff --git a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt index 779c25e7f1..6ba8364183 100644 --- a/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/fragment/MainWrapperViewModel.kt @@ -88,7 +88,7 @@ class MainWrapperViewModel(application: Application) : AndroidViewModel(applicat fun requestOpeningData() { val lastId = PreferenceManager.getDefaultSharedPreferences(getApplication()).getString(Constants.SP_LAST_OPENING_ID, "") val lastTime = PreferenceManager.getDefaultSharedPreferences(getApplication()).getLong(Constants.SP_LAST_OPENING_TIME, 0) - val openType = if (HaloApp.getInstance().isBrandNewInstall) "first" else "not_first_time" + val openType = if (HaloApp.getInstance().isNewForThisVersion) "first" else "not_first_time" mSensitiveApi.getOpeningDialog(HaloApp.getInstance().channel, lastId, lastTime, openType) .subscribeOn(Schedulers.io()) diff --git a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyViewModel.kt b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyViewModel.kt index eacf011b81..0257e3f069 100644 --- a/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/gamedetail/rating/RatingReplyViewModel.kt @@ -6,7 +6,10 @@ import androidx.lifecycle.MediatorLiveData import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModelProvider import com.gh.base.fragment.WaitingDialogFragment +import com.gh.common.exposure.meta.MetaUtil +import com.gh.common.json.json import com.gh.common.util.* +import com.gh.gamecenter.BuildConfig import com.gh.gamecenter.R import com.gh.gamecenter.baselist.ListViewModel import com.gh.gamecenter.baselist.LoadStatus @@ -163,9 +166,19 @@ class RatingReplyViewModel(application: Application, fun replyComment(replyId: String?, content: String, successCallback: () -> Unit) { processDialog.postValue(WaitingDialogFragment.WaitingDialogData("提交中...", true)) - val requestMap = HashMap() - requestMap["content"] = content - val body = requestMap.createRequestBody() + val json = json { + "content" to content + "device" to json { + "os" to "Android" + "rom" to MetaUtil.getMeta().rom + "model" to MetaUtil.getMeta().model + "manufacturer" to MetaUtil.getMeta().manufacturer + "android_sdk" to MetaUtil.getMeta().android_sdk + "android_version" to MetaUtil.getMeta().android_version + "gh_version" to BuildConfig.VERSION_NAME + } + } + val body = json.toRequestBody() val observable = if (replyId.isNullOrEmpty()) { mApi.postCommentReply(game?.id, comment?.id, body) diff --git a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestAdapter.kt b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestAdapter.kt index 68ed720b1c..4c993fe153 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestAdapter.kt @@ -9,16 +9,15 @@ import com.gh.common.constant.ItemViewType import com.gh.common.exposure.ExposureEvent import com.gh.common.exposure.ExposureEvent.Companion.createEvent import com.gh.common.exposure.ExposureSource -import com.gh.common.exposure.ExposureType import com.gh.common.exposure.IExposable import com.gh.common.util.DownloadItemUtils import com.gh.common.util.StringUtils import com.gh.common.util.dip2px +import com.gh.common.util.toBinding import com.gh.gamecenter.GameDetailActivity import com.gh.gamecenter.R import com.gh.gamecenter.adapter.viewholder.FooterViewHolder import com.gh.gamecenter.adapter.viewholder.GameViewHolder -import com.gh.gamecenter.databinding.GameItemBinding import com.gh.gamecenter.databinding.ItemTestServerTextBinding import com.gh.gamecenter.entity.GameEntity import com.gh.gamecenter.game.GameItemViewHolder @@ -43,12 +42,12 @@ class GameServersTestAdapter(context: Context, if (mDataList.size == position) { return GameServersContentAdapter.VIEW_TYPE_FOOTER } - return if (mDataList[position].time != null) { - GameServersContentAdapter.VIEW_TYPE_TOP - } else if (mDataList[position].text != null) { - ItemViewType.ITEM_EMPTY - } else { - GameServersContentAdapter.VIEW_TYPE_ITEM + return when { + mDataList[position].time != null -> GameServersContentAdapter.VIEW_TYPE_TOP + + mDataList[position].text != null -> ItemViewType.ITEM_EMPTY + + else -> GameServersContentAdapter.VIEW_TYPE_ITEM } } @@ -61,10 +60,10 @@ class GameServersTestAdapter(context: Context, GameServerTimeViewHolder(mLayoutInflater.inflate(R.layout.kaifu_item_time, parent, false)) } ItemViewType.ITEM_EMPTY -> { - TextViewHolder(ItemTestServerTextBinding.inflate(mLayoutInflater, parent, false)) + TextViewHolder(parent.toBinding()) } else -> { - GameItemViewHolder(GameItemBinding.bind(mLayoutInflater.inflate(R.layout.game_item, parent, false))) + GameItemViewHolder(parent.toBinding()) } } } @@ -72,36 +71,43 @@ class GameServersTestAdapter(context: Context, override fun getItemCount() = if (mDataList.size == 0) 0 else mDataList.size + 1 override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int) { - if (holder is GameItemViewHolder) { - holder.itemView.setPadding(16F.dip2px(), 8F.dip2px(), 16F.dip2px(), 8F.dip2px()) + when (holder) { + is GameItemViewHolder -> { + val gameEntity = mDataList[position].game!! + val exposureSources = ArrayList() + val isTheLastOfTheLatestConsecutiveGame = mDataList[position].isTheLastOfTheLatestConsecutiveGame - val gameEntity = mDataList[position].game!! - val exposureSources = ArrayList() + holder.itemView.setPadding( + 16F.dip2px(), + 8F.dip2px(), + 16F.dip2px(), + if (isTheLastOfTheLatestConsecutiveGame) 16F.dip2px() else 8F.dip2px()) - holder.binding.game = gameEntity - holder.binding.gameName.layoutParams = holder.binding.gameName.layoutParams.apply { - width = ViewGroup.LayoutParams.WRAP_CONTENT + holder.binding.game = gameEntity + holder.binding.gameName.layoutParams = holder.binding.gameName.layoutParams.apply { + width = ViewGroup.LayoutParams.WRAP_CONTENT + } + + mBasicExposureSource?.let { exposureSources.addAll(it) } + + exposureSources.add(ExposureSource( + "开测表详情", + getExposureSourceValue(gameEntity.testTime ?: ""))) + + mExposureEventArray.put(position, createEvent(gameEntity, exposureSources)) + initViewHolder(gameEntity, holder) + } + is GameServerTimeViewHolder -> { + holder.container.setPadding(0, 8F.dip2px(), 0, 4F.dip2px()) + holder.header.setImageResource(R.drawable.kaifu_time_icon) + holder.time.text = mDataList[position].time + } + is FooterViewHolder -> { + initFooterViewHolder(holder) + } + is TextViewHolder -> { + holder.binding.contentTv.text = mDataList[position].text } - - mBasicExposureSource?.let { exposureSources.addAll(it) } - - exposureSources.add(ExposureSource("开测表详情", getExposureSourceValue(gameEntity.testTime - ?: ""))) - - val exposureEvent = createEvent(gameEntity, - exposureSources, - null, - ExposureType.EXPOSURE) - mExposureEventArray.put(position, exposureEvent) - initViewHolder(gameEntity, holder) - } else if (holder is GameServerTimeViewHolder) { - holder.container.setPadding(0, if (position != 0) 16F.dip2px() else 8F.dip2px(), 0, 4F.dip2px()) - holder.header.setImageResource(R.drawable.kaifu_time_icon) - holder.time.text = mDataList[position].time - } else if (holder is FooterViewHolder) { - initFooterViewHolder(holder) - } else if (holder is TextViewHolder) { - holder.binding.contentTv.text = mDataList[position].text } } @@ -142,38 +148,13 @@ class GameServersTestAdapter(context: Context, return sb.toString() } -// private fun getExposureSourceValue(): String { -// val sb = StringBuilder() -// val time = mViewModel.filterTime -// -// var timeFilter = "" -// val typeFilter = mViewModel.filterLiveData.value ?: "全部" -// -// if (time == 0) { -// timeFilter = "今天" -// } else if (time == -1) { -// timeFilter = "昨天" -// } else if (time == 1) { -// timeFilter = "明天" -// } else if (time > 1) { -// timeFilter = "后续" -// } else if (time < 7) { -// // TODO 根据接口返回来确定是最近多少天 -// timeFilter = "最近14天" -// } -// sb.append(timeFilter) -// sb.append("+") -// sb.append(typeFilter) -// return sb.toString() -// } - @Nullable override fun getEventByPosition(pos: Int): ExposureEvent? { return mExposureEventArray[pos] } @Nullable - override fun getEventListByPosition(pos: Int) = null + override fun getEventListByPosition(pos: Int): List? = null class TextViewHolder(var binding: ItemTestServerTextBinding) : RecyclerView.ViewHolder(binding.root) diff --git a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestFragment.kt b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestFragment.kt index 7ea8765f16..b9f246aa16 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestFragment.kt @@ -5,6 +5,7 @@ import android.view.View import android.widget.CheckedTextView import android.widget.LinearLayout import android.widget.PopupWindow +import android.widget.RelativeLayout import androidx.lifecycle.Observer import androidx.recyclerview.widget.DefaultItemAnimator import androidx.recyclerview.widget.LinearLayoutManager @@ -150,7 +151,6 @@ class GameServersTestFragment : LazyFragment() { mSubBinding?.recyclerView?.layoutManager = LinearLayoutManager(requireContext()) mSubBinding?.recyclerView?.addOnScrollListener(mExposureListener!!) mSubBinding?.recyclerView?.adapter = mAdapter - mSubBinding?.recyclerView?.setPadding(0, 8F.dip2px(), 0, 0) mSubBinding?.recyclerView?.clipToPadding = false mSubBinding?.recyclerView?.addOnScrollListener(object : RecyclerView.OnScrollListener() { @@ -168,6 +168,25 @@ class GameServersTestFragment : LazyFragment() { mBinding?.serverTime?.text = filterDay mBinding?.serverTime?.setTextColor(R.color.theme_font.toColor()) } + + // 悬挂的文案 + mSubBinding?.kaifuTimeContainer?.kaifuItemTime?.goneIf(firstVisiblePosition == 0) + mSubBinding?.kaifuTimeContainer?.kaifuItemTimeTv?.text = mViewModel?.getDayTextByPosition(firstVisiblePosition) + + // 悬挂界面移动 + val lp = mSubBinding?.kaifuTimeContainer?.kaifuItemTime?.layoutParams as RelativeLayout.LayoutParams + if (firstVisiblePosition != 0 + && mViewModel?.getItemAtPosition(firstVisiblePosition + 1)?.time != null) { + val bottom = layoutManager.findViewByPosition(firstVisiblePosition)?.bottom ?: 0 + if (bottom <= mSubBinding?.kaifuTimeContainer?.kaifuItemTime?.height ?: 0) { + lp.topMargin = bottom - (mSubBinding?.kaifuTimeContainer?.kaifuItemTime?.height ?: 0) + } else { + lp.topMargin = 0 + } + } else { + lp.topMargin = 0 + } + mSubBinding?.kaifuTimeContainer?.kaifuItemTime?.layoutParams = lp } } @@ -249,6 +268,9 @@ class GameServersTestFragment : LazyFragment() { } } + /** + * 滚动到具体日期 + */ private fun scrollToDay(day: String) { val positionToScroll = mViewModel!!.getDayPositionByDayString(day) if (positionToScroll >= 0) { diff --git a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestViewModel.kt b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestViewModel.kt index 133b5ed977..96f3e70ab1 100644 --- a/app/src/main/java/com/gh/gamecenter/servers/GameServersTestViewModel.kt +++ b/app/src/main/java/com/gh/gamecenter/servers/GameServersTestViewModel.kt @@ -108,8 +108,8 @@ class GameServersTestViewModel(application: Application, private val mColumnId: return -1 } - fun getDayRangeByPosition(firstVisiblePosition: Int): String { - return when (listLiveData.value?.safelyGetInRelease(firstVisiblePosition)?.readableDaysOffset) { + fun getDayRangeByPosition(position: Int): String { + return when (listLiveData.value?.safelyGetInRelease(position)?.readableDaysOffset) { Day.PAST_DAY.value -> return dayList[0] Day.YESTERDAY.value -> return dayList[1] Day.TODAY.value -> return dayList[2] @@ -119,6 +119,27 @@ class GameServersTestViewModel(application: Application, private val mColumnId: } } + fun getDayTextByPosition(position: Int): String { + var dayText = "" + val dataList = listLiveData.value + + dataList?.let { + for ((index, value) in dataList.withIndex()) { + if (value.time != null) { + dayText = value.time ?: "" + } + if (index == position) { + return dayText + } + } + } + return dayText + } + + fun getItemAtPosition(position: Int): ItemData? { + return listLiveData.value?.safelyGetInRelease(position) + } + private fun initLocationMap(itemList: ArrayList) { locationMap.clear() @@ -190,7 +211,10 @@ class GameServersTestViewModel(application: Application, private val mColumnId: game.sequence = index game.testTime = testTimeInHumanReadableFormat - subList.add(ItemData(game = game, readableDaysOffset = readableDaysOffset)) + subList.add(ItemData( + game = game, + readableDaysOffset = readableDaysOffset, + isTheLastOfTheLatestConsecutiveGame = index == slice.gameList.lastIndex)) isEmptyIndeed = false } @@ -236,10 +260,13 @@ class GameServersTestViewModel(application: Application, private val mColumnId: UPCOMING_DAY(2) } - class ItemData(var time: String? = null, - var game: GameEntity? = null, - var text: String? = null, - var readableDaysOffset: Int = 0) + class ItemData( + var time: String? = null, + var game: GameEntity? = null, + var text: String? = null, + var readableDaysOffset: Int = 0, + var isTheLastOfTheLatestConsecutiveGame: Boolean = false + ) class Factory(private val mApplication: Application, private val mColumnId: String) : ViewModelProvider.NewInstanceFactory() { override fun create(modelClass: Class): T { diff --git a/app/src/main/java/com/halo/assistant/HaloApp.java b/app/src/main/java/com/halo/assistant/HaloApp.java index 404bb75ddf..a1ab41525e 100644 --- a/app/src/main/java/com/halo/assistant/HaloApp.java +++ b/app/src/main/java/com/halo/assistant/HaloApp.java @@ -72,6 +72,7 @@ public class HaloApp extends MultiDexApplication { public long deviceRamSize = 0; public boolean isBrandNewInstall = false; // 当前用户是否是安装光环后第一次打开 + public boolean isNewForThisVersion = false; // 当前用户是否是安装当前版本后第一次打开 (包括全新和更新) public boolean isRunningForeground = false; // 标记当前 APP 是否处于前台运行中 public int mCacheKeyboardHeight = 0;