Merge branch 'release' into 'dev'

Merge hotfix from release

See merge request halo/android/assistant-android!172
This commit is contained in:
刘贻荣
2022-01-05 11:49:50 +08:00
21 changed files with 319 additions and 234 deletions

View File

@ -59,9 +59,9 @@ android {
// 如果不添加 `arm64` 调用系统的 PackageManager 的方法读取安装包信息的时候会出现 native 层闪退,草
// 添加了 `arm64` 以后部分 5.0 的设备会报用错 so 的问题,
// couldn't find DSO to load: libimagepipeline.so caused by: dlopen failed: "/data/data/com.gh.gamecenter/lib-main/libimagepipeline.so" is 64-bit instead of 32-bit result: 0
// 以 OPPO R7PLUS 为例,明明设备是骁龙 615ARMv8-64 bit 的设备却不支持 arm64 的 abi
// 以 OPPO R7PLUS 为例,明明设备是骁龙 615ARMv8-64 bit 的设备却不支持 arm64 的 abi,限制了只使用 java 后还是报错,只有 5.05.1 设备无法复现 : (
// 惊了
abiFilters "armeabi-v7a", "arm64-v8a", "x86"
abiFilters "armeabi-v7a", "x86"
}
renderscriptTargetApi 18
@ -310,8 +310,7 @@ dependencies {
implementation "com.github.tbruyelle:rxpermissions:${rxPermissions}"
implementation "com.github.nichbar:skeleton:${skeleton}"
implementation "io.supercharge:shimmerlayout:${shimmerlayout}"
implementation "com.lg:skeleton:${skeleton}"
implementation "com.tencent.mm.opensdk:wechat-sdk-android-without-mta:${mta}"
implementation "com.github.nichbar:AndroidRomChecker:${romChecker}"
@ -325,7 +324,7 @@ dependencies {
implementation "net.lingala.zip4j:zip4j:${zip4j}"
implementation "io.sentry:sentry-android:${sentry}"
implementation "io.sentry:sentry-android:4.3.0"
implementation("com.github.piasy:BigImageViewer:${bigImageViewer}", {
exclude group: 'com.squareup.okhttp3'

View File

@ -82,7 +82,7 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
mViewPager.offscreenPageLimit = mFragmentsList.size
mViewPager.addOnPageChangeListener(this)
mViewPager.adapter = providePagerAdapter()
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
?: FragmentAdapter(childFragmentManager, mFragmentsList, mTabTitleList)
mViewPager.currentItem = mCheckedIndex
mTabLayout.setupWithViewPager(mViewPager)
mTabIndicatorView.setupWithTabLayout(mTabLayout)
@ -105,12 +105,15 @@ abstract class BaseLazyTabFragment : BaseLazyFragment(), ViewPager.OnPageChangeL
for (index in 0 until childCount) {
val fragment = childFragmentManager.findFragmentByTag("$tag$index")
if (fragment != null) {
restoreFragment(fragment)
fragments.add(fragment)
}
}
return fragments
}
open fun restoreFragment(fragment: Fragment) {}
override fun onPageScrolled(position: Int, positionOffset: Float, positionOffsetPixels: Int) {}
override fun onPageSelected(position: Int) {}

View File

@ -8,11 +8,12 @@ import com.gh.common.exposure.ExposureUtils
import com.gh.common.exposure.meta.MetaUtil
import com.gh.common.simulator.SimulatorDownloadManager
import com.gh.common.simulator.SimulatorGameManager
import com.gh.common.util.EnergyTaskHelper.postEnergyTask
import com.gh.common.xapk.XapkInstaller
import com.gh.download.DownloadDataHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.*
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.R
import com.gh.gamecenter.SuggestionActivity
import com.gh.gamecenter.entity.GameEntity
import com.gh.gamecenter.entity.SimpleGameEntity
import com.gh.gamecenter.entity.SimulatorEntity
@ -20,7 +21,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBShowDialog
import com.gh.gamecenter.retrofit.Response
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.setting.GameDownloadSettingFragment.Companion.AUTO_INSTALL_SP_KEY
import com.gh.gamecenter.setting.GameDownloadSettingFragment
import com.gh.gamecenter.suggest.SuggestType
import com.halo.assistant.HaloApp
import com.lightgame.download.DataWatcher
@ -40,6 +41,9 @@ import java.util.*
object DownloadObserver {
private val mApplication = HaloApp.getInstance().application
// 简单 debounce 因为内存更新 downloadEntity 对象造成的触发双重下载完成事件
// TODO 修复因为更改内存对象造成的双重下载完成事件问题,具体触发代码见 DownloadDao.updateSnapshotList
private var mDoneDebouncePair: Pair<String, Long>? = null
// 如果在WIFI状态下,下载自动暂停,则再重试一遍
@JvmStatic
@ -114,103 +118,13 @@ object DownloadObserver {
}
if (DownloadStatus.done == downloadEntity.status) {
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
statDoneEvent(downloadEntity)
MtaHelper.onEvent("软件更新", "下载完成")
// 会有 ActivityNotFoundException 异常catch 掉不管了
tryWithDefaultCatch {
if (Constants.SILENT_UPDATE != downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)) {
// TODO 在 Android 11 上没有授权安装未知应用的权限前第一次调用这个方法系统会杀掉我们的进程...
// 没能找到类似的解释,最接近的是这个 https://issuetracker.google.com/issues/154157387但也只是点授权杀进程而已
PackageInstaller.install(mApplication, downloadEntity);
DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据
}
}
if (mDoneDebouncePair?.first != downloadEntity.url) {
mDoneDebouncePair = Pair(downloadEntity.url, System.currentTimeMillis())
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
} else {
statDoneEvent(downloadEntity)
postEnergyTask("download_game", downloadEntity.gameId, downloadEntity.packageName)
val platform = PlatformUtils.getInstance(mApplication)
.getPlatformName(downloadEntity.platform)
if (platform != null) {
when {
// TODO 插件化传 path 从 apk 中获取 packageName 的形式有可能出现拿不到 packageName 的情况
downloadEntity.isPluggable -> // 弹出插件化提示框
EventBus.getDefault().post(EBShowDialog(BaseActivity.PLUGGABLE, downloadEntity.path))
downloadEntity.isPlugin -> Utils.toast(mApplication, downloadEntity.name + " - " + platform + " - 下载完成")
else -> Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
} else {
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
if (!downloadEntity.isPluggable) {
if (downloadEntity.isSimulatorGame()) {
val simulatorJson = downloadEntity.getMetaExtra(Constants.SIMULATOR)
val gameName = downloadEntity.getMetaExtra(Constants.GAME_NAME)
if (simulatorJson.isEmpty()) return
val simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
val isInstalled = PackageUtils.isInstalledFromAllPackage(HaloApp.getInstance().application, simulator.apk?.packageName)
if (!isInstalled) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator,
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null)
}
SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type)
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
} else {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
// 是否是自动安装
val isAutoInstall = PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(AUTO_INSTALL_SP_KEY, true)
if (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall) {
if (FileUtils.isEmptyFile(downloadEntity.path)) {
Utils.toast(mApplication, R.string.install_failure_hint)
downloadManager.cancel(downloadEntity.url)
} else {
if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) {
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
tryWithDefaultCatch {
PackageInstaller.install(mApplication, downloadEntity, false)
}
} else {
// 弹出卸载提示框
if (downloadEntity.isPlugin) {
EventBus.getDefault().post(EBShowDialog(BaseActivity.PLUGGABLE, downloadEntity.path))
} else {
EventBus.getDefault().post(EBShowDialog(BaseActivity.SIGNATURE_CONFLICT, downloadEntity.path))
}
}
}
}
}
}
// 统计下载完成
uploadData(gameId, downloadEntity.platform)
}
// 下载过程分析统计
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
// 这里为了让它能用就不判断是否解析包错误了
if (PackageUtils.isDeviceUnableToHandleBigApkFile(downloadEntity.path)) {
return
}
val pm = mApplication.packageManager
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
if (packageInfo == null) {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
if (downloadType == Constants.SIMULATOR_DOWNLOAD) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
DialogUtils.showSimulatorParseErrorDialog(currentActivity, downloadEntity.gameId, downloadEntity.name) {
val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity
?: return@showSimulatorParseErrorDialog
DownloadManager.getInstance(currentActivity).cancel(downloadEntity.url, true, true)
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator, SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME)
}
if (mDoneDebouncePair?.second == 0L
|| (mDoneDebouncePair?.second ?: 0) - System.currentTimeMillis() > 500) {
performDownloadCompleteAction(downloadEntity, gameId, downloadManager)
}
}
}
@ -235,6 +149,115 @@ object DownloadObserver {
}
private fun performDownloadCompleteAction(downloadEntity: DownloadEntity,
gameId: String,
downloadManager: DownloadManager) {
if (downloadEntity.name.contains(mApplication.getString(R.string.app_name))) {
statDoneEvent(downloadEntity)
MtaHelper.onEvent("软件更新", "下载完成")
// 会有 ActivityNotFoundException 异常catch 掉不管了
tryWithDefaultCatch {
if (Constants.SILENT_UPDATE != downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)) {
// TODO 在 Android 11 上没有授权安装未知应用的权限前第一次调用这个方法系统会杀掉我们的进程...
// 没能找到类似的解释,最接近的是这个 https://issuetracker.google.com/issues/154157387但也只是点授权杀进程而已
PackageInstaller.install(mApplication, downloadEntity);
DataLogUtils.uploadUpgradeLog(mApplication, "install") //上传更新安装数据
}
}
} else {
statDoneEvent(downloadEntity)
EnergyTaskHelper.postEnergyTask(
"download_game",
downloadEntity.gameId,
downloadEntity.packageName
)
val platform = PlatformUtils.getInstance(mApplication)
.getPlatformName(downloadEntity.platform)
if (platform != null) {
when {
// TODO 插件化传 path 从 apk 中获取 packageName 的形式有可能出现拿不到 packageName 的情况
downloadEntity.isPluggable -> // 弹出插件化提示框
EventBus.getDefault().post(EBShowDialog(BaseActivity.PLUGGABLE, downloadEntity.path))
downloadEntity.isPlugin -> Utils.toast(mApplication, downloadEntity.name + " - " + platform + " - 下载完成")
else -> Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
} else {
Utils.toast(mApplication, downloadEntity.name + " - 下载完成")
}
if (!downloadEntity.isPluggable) {
if (downloadEntity.isSimulatorGame()) {
val simulatorJson = downloadEntity.getMetaExtra(Constants.SIMULATOR)
val gameName = downloadEntity.getMetaExtra(Constants.GAME_NAME)
if (simulatorJson.isEmpty()) return
val simulator = GsonUtils.fromJson(simulatorJson, SimulatorEntity::class.java)
val isInstalled = PackageUtils.isInstalledFromAllPackage(HaloApp.getInstance().application, simulator.apk?.packageName)
if (!isInstalled) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator,
SimulatorDownloadManager.SimulatorLocation.LAUNCH, downloadEntity.gameId, gameName, null)
}
SimulatorGameManager.recordDownloadSimulatorGame(downloadEntity.gameId, simulator.type)
SimulatorGameManager.postPlayedGame(downloadEntity.gameId, downloadEntity.packageName)
} else {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
// 是否是自动安装
val isAutoInstall = PreferenceManager.getDefaultSharedPreferences(mApplication).getBoolean(
GameDownloadSettingFragment.AUTO_INSTALL_SP_KEY, true)
if (downloadType == Constants.SIMULATOR_DOWNLOAD || isAutoInstall) {
if (FileUtils.isEmptyFile(downloadEntity.path)) {
Utils.toast(mApplication, R.string.install_failure_hint)
downloadManager.cancel(downloadEntity.url)
} else {
if (PackageUtils.isCanLaunchSetup(mApplication, downloadEntity.path)) {
downloadEntity.meta[Constants.MARK_ALREADY_TRIGGERED_INSTALLATION] = "YES"
tryWithDefaultCatch {
PackageInstaller.install(mApplication, downloadEntity, false)
}
} else {
// 弹出卸载提示框
if (downloadEntity.isPlugin) {
EventBus.getDefault().post(EBShowDialog(BaseActivity.PLUGGABLE, downloadEntity.path))
} else {
EventBus.getDefault().post(EBShowDialog(BaseActivity.SIGNATURE_CONFLICT, downloadEntity.path))
}
}
}
}
}
}
// 统计下载完成
uploadData(gameId, downloadEntity.platform)
}
// 下载过程分析统计
// 部分设备 (已知 vivo 5.1.1) 在调用 packageManager.getPackageArchiveInfo 获取比较大的 APK 文件时会出现 ANR
// 这里为了让它能用就不判断是否解析包错误了
if (PackageUtils.isDeviceUnableToHandleBigApkFile(downloadEntity.path)) {
return
}
val pm = mApplication.packageManager
val packageInfo = pm.getPackageArchiveInfo(downloadEntity.path, 0)
if (packageInfo == null) {
val downloadType = downloadEntity.getMetaExtra(Constants.EXTRA_DOWNLOAD_TYPE)
if (downloadType == Constants.SIMULATOR_DOWNLOAD) {
val currentActivity = AppManager.getInstance().currentActivity()
?: return
DialogUtils.showSimulatorParseErrorDialog(currentActivity, downloadEntity.gameId, downloadEntity.name) {
val simulator = HaloApp.get(downloadEntity.name, true) as? SimulatorEntity
?: return@showSimulatorParseErrorDialog
DownloadManager.getInstance(currentActivity).cancel(downloadEntity.url, true, true)
SimulatorDownloadManager.getInstance().showDownloadDialog(currentActivity, simulator, SimulatorDownloadManager.SimulatorLocation.SIMULATOR_GAME)
}
}
}
}
// 统计下载完成事件
private fun statDoneEvent(downloadEntity: DownloadEntity) {
var type: ExposureUtils.DownloadType

View File

@ -20,6 +20,13 @@ public class FixLinearLayoutManager extends LinearLayoutManager {
super(context, attrs, defStyleAttr, defStyleRes);
}
// 尝试避免 IndexOutOfBoundException by GapWorker
// https://stackoverflow.com/questions/43752449/recyclerview-indexoutofboundsexception-inconsistency-detected-invalid-item-pos
@Override
public boolean supportsPredictiveItemAnimations() {
return false;
}
@Override
public void onLayoutChildren(RecyclerView.Recycler recycler, RecyclerView.State state) {
try {

View File

@ -136,7 +136,7 @@ class ImageViewerActivity : BaseActivity(), OnPageChangeListener {
mAnswerEntity = it.getParcelable(AnswerEntity::class.java.name)
}
if (mUrlList?.isEmpty() == true) {
if (mUrlList == null || mUrlList?.isEmpty() == true) {
ToastUtils.toast("无法查看大图")
finish()
}

View File

@ -63,7 +63,7 @@ class NewInstalledGameFragment : NormalFragment() {
if (locationList != null && locationList.size != 0) {
var gameEntity: GameEntity?
for (location in locationList) {
if (location < mInstallGameViewModel.gameListLiveData.value!!.size) {
if (location < mInstallGameViewModel.gameListLiveData.value?.size?:0) {
gameEntity = mInstallGameViewModel.gameListLiveData.value?.get(location)
if (gameEntity != null) {
DownloadItemUtils.processDate(
@ -122,7 +122,7 @@ class NewInstalledGameFragment : NormalFragment() {
})
mInstallGameViewModel.gameListLiveData.observe(this, {
mSkeleton?.hide()
if (it.isNotEmpty()) {
if (!it.isNullOrEmpty()) {
mAdapter?.submitList(it)
} else {
mBinding.fmInstallRvShow.visibility = View.GONE

View File

@ -64,8 +64,10 @@ class NewInstalledGameFragmentAdapter(context: Context, private var mViewModel:
binding.root.context,
gameEntity.getApk()[0].packageName
)
if (drawable.intrinsicWidth >= 300 || drawable.intrinsicHeight >= 300){
drawable = drawable.toBitmap(200, 200).toDrawable(mContext.resources)
if (drawable != null) {
if (drawable.intrinsicWidth >= 300 || drawable.intrinsicHeight >= 300){
drawable = drawable.toBitmap(200, 200).toDrawable(mContext.resources)
}
}
binding.gameIconView.getIconIv().hierarchy.setPlaceholderImage(drawable)
binding.gameIconView.getIconDecoratorIv().visibility = View.GONE

View File

@ -11,8 +11,8 @@ import com.gh.common.constant.Constants
import com.gh.common.util.*
import com.gh.common.view.divider.HorizontalDividerItemDecoration
import com.gh.gamecenter.R
import com.gh.gamecenter.baselist.LazyListFragment
import com.gh.gamecenter.baselist.ListAdapter
import com.gh.gamecenter.baselist.ListFragment
import com.gh.gamecenter.eventbus.EBDeleteDetail
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.eventbus.EBUserFollow
@ -26,7 +26,7 @@ import org.greenrobot.eventbus.EventBus
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskListViewModel>() {
class ForumArticleAskListFragment : LazyListFragment<AnswerEntity, ForumArticleAskListViewModel>() {
private lateinit var mAdapter: ForumArticleAskListAdapter
private var mViewModel: ForumArticleAskListViewModel? = null
@ -36,7 +36,7 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
private var bbsId: String = ""
private var mListReachTop = true
override fun getLayoutId(): Int = R.layout.fragment_list_base_skeleton
override fun getRealLayoutId(): Int = R.layout.fragment_list_base_skeleton
override fun provideListViewModel(): ForumArticleAskListViewModel {
val factory = ForumArticleAskListViewModel.Factory(bbsId, mPath)
@ -62,16 +62,17 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
bbsId = arguments?.getString(EntranceUtils.KEY_BBS_ID, "") ?: ""
mPath = arguments?.getString(EntranceUtils.KEY_PATH) ?: ""
super.onCreate(savedInstanceState)
}
override fun onFragmentFirstVisible() {
super.onFragmentFirstVisible()
mListRv.overScrollMode = View.OVER_SCROLL_NEVER
mListRefresh?.isEnabled = false
mListRv.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white))
mScrollCalculatorHelper = ForumScrollCalculatorHelper(R.id.horizontalVideoView, R.id.verticalVideoView, 0)
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
requireView().setBackgroundColor(Color.WHITE)
mSkeletonScreen = Skeleton.bind(mListSkeleton)
.shimmer(true)
.angle(Constants.SHIMMER_ANGLE)
@ -81,7 +82,9 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
.gradientCenterColorWidth(Constants.GRADIENT_CENTER_COLOR_WIDTH)
.load(R.layout.forum_article_ask_list_skeleton)
.show()
onLoadRefresh()
mListRv.addOnScrollListener(object : RecyclerView.OnScrollListener() {
override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) {
@ -110,13 +113,13 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
})
}
override fun onResume() {
override fun onFragmentResume() {
resumeVideo()
super.onResume()
super.onFragmentResume()
}
override fun onPause() {
super.onPause()
override fun onFragmentPause() {
super.onFragmentPause()
pauseVideo()
}
@ -124,6 +127,7 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
super.onDestroy()
mScrollCalculatorHelper?.currentPlayer?.release()
}
override fun onLoadRefresh() {
super.onLoadRefresh()
requireView().setBackgroundColor(Color.WHITE)
@ -201,8 +205,8 @@ class ForumArticleAskListFragment : ListFragment<AnswerEntity, ForumArticleAskLi
}
private fun scroll() {
val firstVisibleItem = mLayoutManager.findFirstVisibleItemPosition()
val lastVisibleItem = mLayoutManager.findLastVisibleItemPosition()
val firstVisibleItem = mLayoutManager?.findFirstVisibleItemPosition() ?: -1
val lastVisibleItem = mLayoutManager?.findLastVisibleItemPosition() ?: -1
mScrollCalculatorHelper?.onScroll(mViewModel?.videoList, firstVisibleItem, lastVisibleItem)
}

View File

@ -108,6 +108,23 @@ class ForumDetailFragment : BaseLazyTabFragment() {
fragments.add(mVideoForumArticleAskListFragment!!)
}
override fun restoreFragment(fragment: Fragment) {
when (fragment.arguments?.getString(EntranceUtils.KEY_PATH)) {
"全部" -> {
mAllForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
"精华" -> {
mEssenceForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
"问答" -> {
mAskForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
"视频" -> {
mVideoForumArticleAskListFragment = fragment as ForumArticleAskListFragment
}
}
}
override fun initTabTitleList(tabTitleList: MutableList<String>) {
tabTitleList.add(TAB_TITLE_ALL)
tabTitleList.add(TAB_TITLE_ESSENCE)
@ -120,22 +137,6 @@ class ForumDetailFragment : BaseLazyTabFragment() {
return mBinding.root
}
override fun providePagerAdapter(): PagerAdapter? {
return object : FragmentStatePagerAdapter(childFragmentManager, BEHAVIOR_RESUME_ONLY_CURRENT_FRAGMENT) {
override fun getItem(position: Int) = mFragmentsList[position]
override fun getCount() = mFragmentsList.size
override fun getItemPosition(`object`: Any) = PagerAdapter.POSITION_NONE
override fun getPageTitle(position: Int): CharSequence? {
return if (mTabTitleList.size > position) {
mTabTitleList[position]
} else super.getPageTitle(position)
}
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
bbsId = arguments?.getString(EntranceUtils.KEY_BBS_ID, "") ?: ""

View File

@ -3,6 +3,7 @@ package com.gh.gamecenter.forum.home
import android.app.Activity
import android.content.Intent
import android.graphics.Typeface
import android.os.Bundle
import android.view.Gravity
import android.view.LayoutInflater
import android.view.View
@ -13,7 +14,7 @@ import androidx.core.graphics.ColorUtils
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import com.gh.base.adapter.FragmentAdapter
import com.gh.base.fragment.LazyFragment
import com.gh.base.fragment.BaseLazyFragment
import com.gh.common.dialog.TrackableDialog
import com.gh.common.util.*
import com.gh.gamecenter.R
@ -35,33 +36,30 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import kotlin.math.abs
class CommunityHomeFragment : LazyFragment() {
class CommunityHomeFragment : BaseLazyFragment() {
private var mBinding: FragmentCommunityHomeBinding? = null
private var mViewModel: CommunityHomeViewModel? = null
private var mFragmentList = arrayListOf<Fragment>()
private var mTitleList = arrayListOf("推荐", "论坛", "活动")
private var mTabList = arrayListOf<TextView>()
private var mDefaultSelectedTab = -1
override fun getRealLayoutId(): Int {
return R.layout.fragment_community_home
}
override fun getLayoutId() = R.layout.fragment_community_home
override fun onRealLayoutInflated(inflatedView: View) {
mBinding = FragmentCommunityHomeBinding.bind(inflatedView)
}
override fun getInflatedLayout() =
FragmentCommunityHomeBinding.inflate(layoutInflater).apply { mBinding = this }.root
override fun onFragmentFirstVisible() {
mViewModel = viewModelProvider()
super.onFragmentFirstVisible()
initObserver()
initViewPager()
}
override fun initRealView() {
super.initRealView()
private fun initObserver() {
mViewModel?.articleLiveData?.observeNonNull(viewLifecycleOwner) {
insertDataToRecommendTab(it)
}
@ -80,6 +78,16 @@ class CommunityHomeFragment : LazyFragment() {
}
}
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
savedInstanceState?.let { mDefaultSelectedTab = it.getInt(LAST_SELECTED_POSITION) }
}
override fun onSaveInstanceState(outState: Bundle) {
mBinding?.viewPager?.let { outState.putInt(LAST_SELECTED_POSITION, it.currentItem) }
super.onSaveInstanceState(outState)
}
override fun onFragmentResume() {
super.onFragmentResume()
@ -92,7 +100,9 @@ class CommunityHomeFragment : LazyFragment() {
}
private fun initViewPager() {
val defaultSelectedPosition = arguments?.getInt(EntranceUtils.KEY_SUB_POSITION) ?: 0
val defaultSelectedPosition =
if (mDefaultSelectedTab != -1) mDefaultSelectedTab
else arguments?.getInt(EntranceUtils.KEY_SUB_POSITION) ?: 0
mBinding?.run {
mFragmentList.clear()
@ -361,5 +371,6 @@ class CommunityHomeFragment : LazyFragment() {
const val ARTICLE_REQUEST_CODE = 200
const val QUESTION_REQUEST_CODE = 201
const val VIDEO_REQUEST_CODE = 202
const val LAST_SELECTED_POSITION = "last_selected_position"
}
}

View File

@ -167,6 +167,8 @@ public class MainWrapperFragment extends BaseFragment_ViewPager_Checkable implem
mGameWrapperFragment = (SearchToolWrapperFragment) fragment;
} else if (fragment instanceof HomeVideoFragment) {
homeVideoFragment = (HomeVideoFragment) fragment;
} else if (fragment instanceof CommunityHomeFragment) {
mCommunityHomeFragment = (CommunityHomeFragment) fragment;
}
}
return restoreFragments;

View File

@ -12,6 +12,7 @@ import com.gh.common.runOnUiThread
import com.gh.common.util.DirectUtils
import com.gh.common.util.ImageUtils
import com.gh.common.util.dip2px
import com.gh.common.util.safelyGetInRelease
import com.gh.common.view.AsyncCell
import com.gh.gamecenter.R
import com.gh.gamecenter.databinding.RankCollectionItemBinding
@ -195,7 +196,7 @@ class RankCollectionAdapter(
val position = positionAndPackageMap[key]
if (position != null && position < dataList.size) {
dataList[position].getEntryMap()[download.platform] = download
gameItemList[position].run {
gameItemList.safelyGetInRelease(position)?.run {
updateGameItem(
this.rankItemUi,
dataList[position],
@ -217,7 +218,7 @@ class RankCollectionAdapter(
val position = positionAndPackageMap[key]
if (position != null && position < dataList.size) {
dataList[position].getEntryMap().remove(status.platform)
gameItemList[position].run {
gameItemList.safelyGetInRelease(position)?.run {
updateGameItem(
this.rankItemUi,
dataList[position],
@ -239,7 +240,7 @@ class RankCollectionAdapter(
mRankSubjectEntity?.data?.forEach { dataIds += it.id }
for ((index, game) in mRankSubjectEntity?.data!!.withIndex()) {
gameItemList[index].run {
gameItemList.safelyGetInRelease(index)?.run {
updateGameItem(
this.rankItemUi,
game,

View File

@ -33,6 +33,7 @@ import com.lightgame.utils.Utils
import kotterknife.bindView
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import kotlin.math.abs
class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel>(), KeyboardHeightObserver {
@ -44,6 +45,7 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
private val mScrollView by bindView<ScrollView>(R.id.scrollView)
private val mShadowView by bindView<View>(R.id.shadowView)
private val mCommentLine by bindView<View>(R.id.comment_line)
private val mPlaceholderView by bindView<View>(R.id.placeholderView)
private var mAdapter: RatingReplyAdapter? = null
@ -58,7 +60,8 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
private val dataWatcher = object : DataWatcher() {
override fun onDataChanged(downloadEntity: DownloadEntity) {
if (downloadEntity.gameId == mListViewModel?.game?.id
&& mLastDownloadStatus != downloadEntity.status) {
&& mLastDownloadStatus != downloadEntity.status
) {
mLastDownloadStatus = downloadEntity.status
mAdapter?.notifyItemChanged(0)
}
@ -158,11 +161,12 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
override fun provideListViewModel(): RatingReplyViewModel {
val factory = RatingReplyViewModel.Factory(
intent.getStringExtra(EntranceUtils.KEY_GAMEID),
intent.getParcelableExtra(GameEntity::class.java.simpleName),
intent.getStringExtra(EntranceUtils.KEY_COMMENTID),
intent.getParcelableExtra(RatingComment::class.java.simpleName),
intent.getBooleanExtra(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED, false))
intent.getStringExtra(EntranceUtils.KEY_GAMEID),
intent.getParcelableExtra(GameEntity::class.java.simpleName),
intent.getStringExtra(EntranceUtils.KEY_COMMENTID),
intent.getParcelableExtra(RatingComment::class.java.simpleName),
intent.getBooleanExtra(EntranceUtils.KEY_SHOW_KEYBOARD_IF_NEEDED, false)
)
return ViewModelProviders.of(this, factory).get(RatingReplyViewModel::class.java)
}
@ -258,18 +262,21 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
mAnswerContent.background = ContextCompat.getDrawable(this, R.drawable.bg_shape_white_radius_10_top_only)
} else {
mAnswerContent.setBackgroundColor(ContextCompat.getColor(this, R.color.white))
mOffset = Math.abs(height)
mOffset = abs(height)
}
DisplayUtils.setLightStatusBar(this, !isPopup)
mPlaceholderView.goneIf(!isPopup)
val mScrollViewParams = mScrollView.layoutParams as LinearLayout.LayoutParams
mScrollViewParams.width = if (isPopup) LinearLayout.LayoutParams.MATCH_PARENT else 0
mScrollViewParams.height = if (isPopup) DisplayUtils.dip2px(64f) else DisplayUtils.dip2px(28f)
mScrollViewParams.height = if (isPopup) 76f.dip2px() else 28f.dip2px()
mScrollViewParams.topMargin = if (isPopup) 8f.dip2px() else 0
mScrollView.layoutParams = mScrollViewParams
val mLayoutParams = mReplyEditorContainer.layoutParams as RelativeLayout.LayoutParams
mLayoutParams.height = if (isPopup) DisplayUtils.dip2px(130f) else LinearLayout.LayoutParams.WRAP_CONTENT
mLayoutParams.bottomMargin = if (isPopup) height + mOffset - DisplayUtils.dip2px(12f) else 0
mLayoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT
mLayoutParams.bottomMargin = if (isPopup) height + mOffset else 0
mReplyEditorContainer.layoutParams = mLayoutParams
}
@ -295,12 +302,14 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
}
@JvmStatic
fun getIntent(context: Context,
gameId: String,
comment: RatingComment,
showKeyboardIfReplyListIsEmpty: Boolean,
entrance: String,
path: String): Intent {
fun getIntent(
context: Context,
gameId: String,
comment: RatingComment,
showKeyboardIfReplyListIsEmpty: Boolean,
entrance: String,
path: String
): Intent {
val intent = Intent(context, RatingReplyActivity::class.java)
intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path))
intent.putExtra(EntranceUtils.KEY_GAMEID, gameId)
@ -315,12 +324,14 @@ class RatingReplyActivity : ListActivity<RatingReplyEntity, RatingReplyViewModel
}
@JvmStatic
fun getIntent(context: Context,
gameId: String,
commentId: String,
showKeyboardIfReplyListIsEmpty: Boolean,
entrance: String,
path: String): Intent {
fun getIntent(
context: Context,
gameId: String,
commentId: String,
showKeyboardIfReplyListIsEmpty: Boolean,
entrance: String,
path: String
): Intent {
val intent = Intent(context, RatingReplyActivity::class.java)
intent.putExtra(EntranceUtils.KEY_ENTRANCE, mergeEntranceAndPath(entrance, path))
intent.putExtra(EntranceUtils.KEY_GAMEID, gameId)

View File

@ -38,7 +38,7 @@ public class DataCollectionManager {
}
public static void onEvent(Context context, String type, Map<String, Object> map, boolean isUpload) {
AppExecutor.getLightWeightIoExecutor().execute(() -> {
AppExecutor.getLogExecutor().execute(() -> {
map.put("createdOn", Utils.getTime(context));
if (isUpload) {
DataCollectionManager.getInstance(context).realTimeUpload(type, map);
@ -95,7 +95,7 @@ public class DataCollectionManager {
}
// 添加事件
public static void onEvent(Context context, DataCollectionInfo entity, boolean isUpload) {
private static void onEvent(Context context, DataCollectionInfo entity, boolean isUpload) {
DataCollectionManager.getInstance(context).add(entity, isUpload);
}
@ -159,7 +159,7 @@ public class DataCollectionManager {
DataCollectionManager.getInstance(context).upsert(entity, type);
}
public void upsert(DataCollectionInfo entity, String type) {
private void upsert(DataCollectionInfo entity, String type) {
List<DataCollectionInfo> list = dao.findByType(type);
if (list == null || list.isEmpty()) {
dao.add(entity);
@ -193,34 +193,38 @@ public class DataCollectionManager {
* 统计点击数据
*/
public void statClickData() {
List<DataCollectionInfo> list = dao.getClickData();
if (list != null && !list.isEmpty()) {
List<String> ids = new ArrayList<>();
List<Object> data = new ArrayList<>();
try {
DataCollectionInfo dataCollectionEntity;
for (int i = 0, size = list.size(); i < size; i++) {
dataCollectionEntity = list.get(i);
ids.add(dataCollectionEntity.getId());
data.add(new JSONObject(dataCollectionEntity.getData()));
AppExecutor.getLogExecutor().execute(() -> {
List<DataCollectionInfo> list = dao.getClickData();
if (list != null && !list.isEmpty()) {
List<String> ids = new ArrayList<>();
List<Object> data = new ArrayList<>();
try {
DataCollectionInfo dataCollectionEntity;
for (int i = 0, size = list.size(); i < size; i++) {
dataCollectionEntity = list.get(i);
ids.add(dataCollectionEntity.getId());
data.add(new JSONObject(dataCollectionEntity.getData()));
}
} catch (JSONException e) {
e.printStackTrace();
}
} catch (JSONException e) {
e.printStackTrace();
Map<String, Object> map = new HashMap<>();
map.put("data", data);
onEvent(mContext, "click", map);
dao.delete(ids);
}
Map<String, Object> map = new HashMap<>();
map.put("data", data);
onEvent(mContext, "click", map);
dao.delete(ids);
}
});
}
public static void onEvent(Context context, String type, Map<String, Object> map) {
map.put("createdOn", Utils.getTime(context));
if ("news".equals(type) || "download".equals(type) || "search".equals(type)) {
DataCollectionManager.getInstance(context).realTimeUpload(type, map);
return;
}
onEvent(context, type, new JSONObject(map).toString(), true);
AppExecutor.getLogExecutor().execute(() -> {
map.put("createdOn", Utils.getTime(context));
if ("news".equals(type) || "download".equals(type) || "search".equals(type)) {
DataCollectionManager.getInstance(context).realTimeUpload(type, map);
return;
}
onEvent(context, type, new JSONObject(map).toString(), true);
});
}
}

View File

@ -21,7 +21,6 @@ import java.util.HashMap;
import java.util.List;
import java.util.Map;
import io.reactivex.android.schedulers.AndroidSchedulers;
import io.reactivex.schedulers.Schedulers;
public class FilterManager {
@ -67,7 +66,11 @@ public class FilterManager {
String line;
while ((line = reader.readLine()) != null) {
String[] values = line.split("=");
list.add(new PackageInfo(values[0], Long.parseLong(values[1])));
try {
list.add(new PackageInfo(values[0], Long.parseLong(values[1])));
} catch (NumberFormatException e) {
// ignore format error
}
}
reader.close();
addAllFilter(list);

View File

@ -96,6 +96,8 @@ public class MessageDetailFragment extends NormalFragment implements OnCommentCa
ScrollView mScrollView;
@BindView(R.id.answer_comment_content_container)
View mReplyEditorContainer;
@BindView(R.id.placeholderView)
View mPlaceholderView;
private LinearLayoutManager mLayoutManager;
@ -519,14 +521,18 @@ public class MessageDetailFragment extends NormalFragment implements OnCommentCa
mOffset = Math.abs(height);
}
DisplayUtils.setLightStatusBar(requireActivity(), !isPopup);
mPlaceholderView.setVisibility(isPopup ? View.VISIBLE : View.GONE);
LinearLayout.LayoutParams mScrollViewParams = (LinearLayout.LayoutParams) mScrollView.getLayoutParams();
mScrollViewParams.width = isPopup ? LinearLayout.LayoutParams.MATCH_PARENT : 0;
mScrollViewParams.height = isPopup ? DisplayUtils.dip2px(64f) : DisplayUtils.dip2px(28f);
mScrollViewParams.height = isPopup ? DisplayUtils.dip2px(76f) : DisplayUtils.dip2px(28f);
mScrollViewParams.topMargin = isPopup ? DisplayUtils.dip2px(8f) : 0;
mScrollView.setLayoutParams(mScrollViewParams);
RelativeLayout.LayoutParams mLayoutParams = (RelativeLayout.LayoutParams) mReplyEditorContainer.getLayoutParams();
mLayoutParams.height = isPopup ? DisplayUtils.dip2px(130f) : LinearLayout.LayoutParams.WRAP_CONTENT;
mLayoutParams.bottomMargin = isPopup ? height + mOffset - DisplayUtils.dip2px(12f) : 0;
mLayoutParams.height = LinearLayout.LayoutParams.WRAP_CONTENT;
mLayoutParams.bottomMargin = isPopup ? height + mOffset : 0;
mReplyEditorContainer.setLayoutParams(mLayoutParams);
}

View File

@ -711,19 +711,10 @@ public class MessageItemViewHolder extends BaseRecyclerViewHolder<MessageEntity>
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.getComment().getTopId(),
entity.getGameList().getId(),
false,
entrance,

View File

@ -359,18 +359,21 @@ public class HaloApp extends MultiDexApplication {
// 会出现找不到 arm64 so 的情况,具体可见
// https://sentry.ghzs.com/organizations/lightgame/issues/53107/
// 所以这里尝试在 5.0 & 5.1 设备上关闭 fresco 的 native 解码,应该会让 5.0 & 5.1 的设备 OOM 概率提高,但先试试效果
ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig.newBuilder(this);
// 效果不好,还是有闪退,麻了,见 https://sentry.ghzs.com/organizations/lightgame/issues/86665/
// 难道非得换成不用 SO 的 Glide ?
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
|| Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
configBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
.setImageTranscoderType(ImageTranscoderType.JAVA_TRANSCODER)
.experiment()
.setNativeCodeDisabled(true);
}
// ImagePipelineConfig.Builder configBuilder = ImagePipelineConfig.newBuilder(this);
// if (Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP
// || Build.VERSION.SDK_INT == Build.VERSION_CODES.LOLLIPOP_MR1) {
// configBuilder.setMemoryChunkType(MemoryChunkType.BUFFER_MEMORY)
// .setImageTranscoderType(ImageTranscoderType.JAVA_TRANSCODER)
// .experiment()
// .setNativeCodeDisabled(true);
// }
try {
BigImageViewer.initialize(FrescoImageLoader.with(this, configBuilder.build()));
BigImageViewer.initialize(FrescoImageLoader.with(this));
} catch (Throwable e) {
e.printStackTrace();
}

View File

@ -1,5 +1,6 @@
package com.halo.assistant.fragment.user
import android.content.ActivityNotFoundException
import android.content.Intent
import android.net.Uri
import android.os.Bundle
@ -130,8 +131,22 @@ class ManuallyRealNameFragment : NormalFragment() {
}
private fun selectImage() {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, REQUEST_IMAGE)
try {
val intent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
startActivityForResult(intent, REQUEST_IMAGE)
} catch (e: ActivityNotFoundException) {
// https://stackoverflow.com/questions/45707678
val getIntent = Intent(Intent.ACTION_GET_CONTENT)
getIntent.type = "image/*"
val pickIntent = Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI)
pickIntent.type = "image/*"
val chooserIntent = Intent.createChooser(getIntent, "Select Image")
chooserIntent.putExtra(Intent.EXTRA_INITIAL_INTENTS, arrayOf(pickIntent))
startActivityForResult(chooserIntent, REQUEST_IMAGE)
}
}
private fun removeUploadedImage() {

View File

@ -268,7 +268,7 @@ class RealNameInfoFragment : NormalFragment() {
* 屏幕高度小于等于 1080 (16:9) 的设备看作无法显示完整内容
*/
private fun isScreenUnableToShowAllTheContent(): Boolean {
return DisplayUtils.getScreenHeight() <= 1080
return DisplayUtils.getScreenHeight() <= 1920
}
private fun changeToCompatView() {
@ -346,7 +346,7 @@ class RealNameInfoFragment : NormalFragment() {
ConstraintSet.TOP,
R.id.idCardEt,
ConstraintSet.BOTTOM,
4F.dip2px()
24F.dip2px()
)
}.applyTo(mBinding.infoContainer)
}

View File

@ -7,8 +7,8 @@ ext {
targetSdkVersion = 26
// application info (每个大版本之间的 versionCode 增加 20)
versionCode = 450
versionName = "5.5.0"
versionCode = 453
versionName = "5.5.3"
applicationId = "com.gh.gamecenter"
// AndroidX
@ -97,8 +97,7 @@ ext {
flexbox = "1.1.0"
pickerView = "4.1.8"
verifier = "1.0.6"
skeleton = "1.1.3"
shimmerlayout = "2.1.0"
skeleton = "1.1.4"
mta = "6.7.9"
romChecker = "1.0.3"
oss = "2.9.2"