Compare commits

..

5 Commits

526 changed files with 4960 additions and 16739 deletions

View File

@ -71,8 +71,7 @@ android_build:
exit_codes: 137
only:
- dev
- release
- feature-GHZS-5816
- feat/GHZSCY-5250
# 代码检查
sonarqube_analysis:
@ -153,5 +152,4 @@ oss-upload&send-email:
- /usr/local/bin/python /ci-android-mail-jira-comment.py
only:
- dev
- release
- feature-GHZS-5816
- feat/GHZSCY-5250

View File

@ -126,10 +126,6 @@ android {
}
}
packagingOptions {
exclude 'META-INF/gradle/incremental.annotation.processors'
}
buildTypes {
debug {
debuggable true
@ -352,7 +348,7 @@ dependencies {
implementation "com.llew.huawei:verifier:${verifier}"
teaImplementation "com.bytedance.applog:RangersAppLog-Lite-cn:${bytedanceApplog}"
teaImplementation "com.bytedance.ads:AppConvert:${bytedanceAppConvert}"
teaImplementation "com.bytedance.applog:RangersAppLog-All-convert:${bytedanceApplog}"
implementation "net.lingala.zip4j:zip4j:${zip4j}"
@ -361,9 +357,7 @@ dependencies {
implementation "com.lg:easyfloat:${easyFloat}"
implementation ("com.lg:apksig:${apksig}") {
exclude group: 'com.google.protobuf'
}
implementation "com.lg:apksig:${apksig}"
implementation "com.lg:gid:${gid}"
@ -403,6 +397,7 @@ dependencies {
implementation(project(':module_message')) {
exclude group: 'androidx.swiperefreshlayout'
}
// implementation(project(':feature:vpn'))
implementation(project(':feature:pkg'))
implementation(project(':feature:oaid'))
implementation(project(':feature:floating-window'))
@ -414,9 +409,8 @@ dependencies {
}
internalImplementation(project(':module_internal_test'))
def pushProperty = findProperty('BUILD_PUSH_TYPE')
// 根据BUILD_PUSH_TYPE决定使用哪个推送SDK目前默认使用极光推送
def pushProject = (pushProperty == null || pushProperty == 'jg')
// 根据BUILD_PUSH_TYPE决定使用哪个推送SDK目前默认使用阿里云推送
def pushProject = findProperty('BUILD_PUSH_TYPE') == 'jg'
? project(':feature:jg_push') : project(':feature:acloud_push')
implementation(pushProject) {
exclude group: 'androidx.swiperefreshlayout'
@ -574,16 +568,7 @@ andResGuard {
"R.id.cardMask",
"R.id.cardGradientMask",
"R.id.gameIconIv",
"R.id.titleContainer",
"R.id.v_login_background",
"R.id.tv_login_tips",
"R.id.tv_login",
"R.id.v_code_background",
"R.id.tv_code_tips",
"R.id.tv_code",
"R.id.tv_copy",
"R.id.v_indicator_background",
"R.id.v_indicator"
"R.id.titleContainer"
]
compressFilePattern = [
"*.png",

View File

@ -28,9 +28,6 @@ object GdtHelper {
} else {
GDTAction.init(application, USER_ACTION_SET_ID, APP_SECRET_ID, channel)
}
GDTAction.start()
Utils.log("init GdtHelper")
}

Binary file not shown.

Binary file not shown.

View File

@ -111,14 +111,6 @@
android:name="android.permission.ACCESS_COARSE_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_BACKGROUND_LOCATION"
tools:node="remove" />
<uses-permission
android:name="android.permission.ACCESS_FINE_LOCATION"
tools:node="remove" />
<supports-screens
android:anyDensity="true"
android:largeScreens="true"
@ -755,11 +747,11 @@
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.minigame.qq.QGameHomeWrapperActivity"
android:name="com.gh.gamecenter.qgame.QGameHomeWrapperActivity"
android:screenOrientation="portrait" />
<activity
android:name="com.gh.gamecenter.minigame.MiniGameSearchActivity"
android:name="com.gh.gamecenter.qgame.QGameSearchActivity"
android:screenOrientation="portrait" />
<activity
@ -778,19 +770,8 @@
android:name="com.gh.gamecenter.wrapper.ToolbarWrapperActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.home.CommunityActivity"
android:screenOrientation="portrait" />
<activity
android:name=".forum.home.follow.FollowDynamicActivity"
android:screenOrientation="portrait"
android:theme="@style/Theme.Transparent" />
<activity
android:name=".forum.home.follow.AllFollowedActivity"
android:screenOrientation="portrait"
android:theme="@style/AppCompatTheme.APP" />
<activity android:name=".forum.home.CommunityActivity"
android:screenOrientation="portrait"/>
<!-- <activity-->
<!-- android:name="${applicationId}.douyinapi.DouYinEntryActivity"-->

View File

@ -191,11 +191,10 @@ object AdDelegateHelper {
}
/**
* 热启动是否需要显示开屏广告(目前只展示第三方广告)
* 热启动是否需要显示开屏广告
*/
private fun shouldShowStartUpAdWhenHotLaunch() = (mCsjAdImpl != null || mBeiziAdImpl != null)
&& mSplashAd?.displayRule?.hotStartSplashAd?.type == AD_TYPE_SDK
&& mSplashAd?.hotStartThirdPartyAd != null
private fun shouldShowStartUpAdWhenHotLaunch() =
mSplashAd?.displayRule?.hotStartSplashAd?.type == AD_TYPE_SDK && mSplashAd?.hotStartThirdPartyAd != null
/**
* 是否需要显示下载管理广告

View File

@ -45,14 +45,6 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
}
isFromBackgroundToForeground = false
}
if (activityCount == 1) {
// 清除桌面角标
if (activity !is SplashScreenActivity && activity !is AuthorizationActivity) {
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
pushProvider?.cleanBadgeNumber(activity.applicationContext)
}
}
}
override fun onActivityResumed(activity: Activity) {
@ -92,6 +84,10 @@ class GlobalActivityLifecycleObserver : Application.ActivityLifecycleCallbacks {
}
XapkInstaller.updateCurrentInstallStatus()
// 清除桌面角标
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
pushProvider?.cleanBadgeNumber(activity.applicationContext)
}
override fun onActivityPaused(activity: Activity) {

View File

@ -32,13 +32,9 @@ import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.common.view.dsbridge.CompletionHandler
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.provider.IPushProvider
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.core.utils.*
import com.gh.gamecenter.entity.SensorsEvent
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.eventbus.EBPackage
@ -73,7 +69,7 @@ class DefaultJsApi(
private var mFragment: Fragment? = null,
private var mBbsId: String? = "",
private var mOriginUrl: String? = "",
private val mForumName: String? = "",
private val mForumName: String? = ""
) {
companion object {
@ -93,11 +89,6 @@ class DefaultJsApi(
}
}
@JavascriptInterface
fun isEnableForceDark(msg: Any): Boolean {
return DarkModeUtils.isWebViewForceDarkEnabled
}
@JavascriptInterface
fun isGhzs(msg: Any): String {
return "true"
@ -342,12 +333,6 @@ class DefaultJsApi(
return HaloApp.getInstance().oaid
}
@JavascriptInterface
fun getPushId(): String {
val pushProvider = ARouter.getInstance().build(RouteConsts.provider.push).navigation() as? IPushProvider
return pushProvider?.getRegistrationId(HaloApp.getInstance()) ?: "unknown"
}
@JavascriptInterface
fun showIncompatibleVersionDialog(msg: Any) {
DialogHelper.showUpgradeDialog(context)

View File

@ -1,7 +1,6 @@
package com.gh.common.constant;
import android.annotation.SuppressLint;
import android.content.IntentFilter;
import android.content.SharedPreferences;
import android.os.Build;
import android.preference.PreferenceManager;
@ -15,7 +14,6 @@ import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.CommonConsts;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.common.utils.DarkModeUtils;
@ -30,7 +28,6 @@ import com.gh.gamecenter.entity.VSetting;
import com.gh.gamecenter.feature.entity.SettingsEntity;
import com.gh.gamecenter.feature.entity.SimulatorEntity;
import com.gh.gamecenter.feature.utils.ContentBlockedHelper;
import com.gh.gamecenter.receiver.PackageChangeBroadcastReceiver;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.gh.vspace.VHelper;
import com.halo.assistant.HaloApp;
@ -80,11 +77,24 @@ public class Config {
return !SPUtils.getBoolean(Constants.SP_TEENAGER_MODE);
}
/**
* VPN 开关选项是否开启
*/
public static boolean isVpnOptionEnabled() {
if (mNewApiSettingsEntity == null
|| mNewApiSettingsEntity.getInstall() == null
|| mNewApiSettingsEntity.getInstall().getVpnRequired() == null) {
return false;
}
return mNewApiSettingsEntity.getInstall().getVpnRequired().getShouldShowVpnOption();
}
public static void setSettings(SettingsEntity settingsEntity) {
getPreferences().edit().putString(SETTINGS_KEY, GsonUtils.toJson(settingsEntity)).apply();
mSettingsEntity = settingsEntity;
PackageHelper.refreshPackageNameList();
PackageHelper.refreshList();
}
@Nullable
@ -299,9 +309,7 @@ public class Config {
String filterString = UrlFilterUtils.getFilterQuery(
"manufacturer", Build.MANUFACTURER,
"model", Build.MODEL,
"android_sdk_version", String.valueOf(Build.VERSION.SDK_INT),
"rom", MetaUtil.INSTANCE.getRom().name() + " " + MetaUtil.INSTANCE.getRom().getVersionName()
);
"android_sdk_version", String.valueOf(Build.VERSION.SDK_INT));
RetrofitManager.getInstance()
.getNewApi().getNewSettings(PackageUtils.getGhVersionName(), channel, filterString)
@ -323,31 +331,8 @@ public class Config {
if (mNewApiSettingsEntity.getGameShieldContents() != null) {
ContentBlockedHelper.INSTANCE.init(mNewApiSettingsEntity.getGameShieldContents());
}
// 更新安装列表是否开启的配置
if (mNewApiSettingsEntity.getInstalledComplianceSwitch() != null) {
PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(mNewApiSettingsEntity.getInstalledComplianceSwitch());
} else {
PackageHelper.INSTANCE.updateIsGetInstalledPackagesApiAgreedRequired(false);
}
// 更新包名监听是否开启
if (mNewApiSettingsEntity.isPackageObserveEnable()) {
observePackageChange(mNewApiSettingsEntity.getPackageObserveActions());
}
}
});
}
}
public static void observePackageChange(NewApiSettingsEntity.PackageObserveActions packageObserveActions) {
PackageChangeBroadcastReceiver receiver = new PackageChangeBroadcastReceiver(packageObserveActions);
IntentFilter intentFilter = new IntentFilter();
intentFilter.addAction(packageObserveActions.getAdd());
intentFilter.addAction(packageObserveActions.getRem());
intentFilter.addAction(packageObserveActions.getRep());
intentFilter.addDataScheme("package");
HaloApp.getInstance().registerReceiver(receiver, intentFilter);
}
}

View File

@ -1,13 +1,13 @@
package com.gh.common.exposure
import com.aliyun.sls.android.producer.Log
import com.gh.gamecenter.BuildConfig
import com.gh.gamecenter.common.loghub.TLogHubHelper
import com.gh.gamecenter.common.loghub.LoghubHelper
import com.gh.gamecenter.common.utils.FixedSizeLinkedHashSet
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.lightgame.utils.Utils
import com.volcengine.model.tls.LogItem
/**
* A handful tool for committing logs to aliyun loghub.
@ -78,20 +78,19 @@ object ExposureManager {
private fun uploadExposures(eventSet: HashSet<ExposureEvent>, forced: Boolean) {
eventSet.forEach {
TLogHubHelper.sendLog(buildLog(it), LOG_STORE)
// LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced)
LoghubHelper.uploadLog(buildLog(it), LOG_STORE, forced)
// it.recycle()
}
}
private fun buildLog(event: ExposureEvent) = LogItem(System.currentTimeMillis()).apply {
addContent("__id", event.id)
addContent("payload", event.payload.toJson())
addContent("event", event.event.toString())
addContent("source", eliminateMultipleBrackets(event.source.toJson()))
addContent("meta", event.meta.toJson())
addContent("real_millisecond", event.timeInMillisecond.toString())
addContent(
private fun buildLog(event: ExposureEvent) = Log().apply {
putContent("id", event.id)
putContent("payload", event.payload.toJson())
putContent("event", event.event.toString())
putContent("source", eliminateMultipleBrackets(event.source.toJson()))
putContent("meta", event.meta.toJson())
putContent("real_millisecond", event.timeInMillisecond.toString())
putContent(
"e-traces", if (event.eTrace != null) {
eliminateMultipleBrackets(event.eTrace?.toJson() ?: "")
} else ""

View File

@ -9,12 +9,10 @@ import androidx.fragment.app.FragmentActivity
import com.gh.common.iinterface.ISuperiorChain
import com.gh.common.util.CheckLoginUtils
import com.gh.common.util.PackageUtils
import com.gh.gamecenter.SplashAdActivity
import com.gh.gamecenter.SplashScreenActivity
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.entity.SimpleGameEntity
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.DialogEntity
import com.gh.gamecenter.feature.entity.WelcomeDialogEntity
@ -44,13 +42,12 @@ object GlobalPriorityChainHelper : ISuperiorChain {
return activity is FragmentActivity
&& !activity.isFinishing
&& activity !is SplashScreenActivity
&& activity !is SplashAdActivity
}
/**
* 预启动所有的优先级弹窗管理链
*/
fun preStart(withSpecialDelay: Boolean) {
fun preStart() {
val launchRedirectHandler = LaunchRedirectHandler(-101)
val updateDialogHandler = UpdateDialogHandler(-100)
val privacyPolicyDialogHandler = PrivacyPolicyDialogHandler(-99)
@ -67,13 +64,8 @@ object GlobalPriorityChainHelper : ISuperiorChain {
launchRedirectHandler.doPreProcess()
updateDialogHandler.doPreProcess()
// 首次启动延迟 300ms保证请求首次启动时已经获取到了 GID 、 OAID 等标记
val requestDelay = if (withSpecialDelay) 300L else 0L
AppExecutor.uiExecutor.executeWithDelay({
requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler)
requestReserveDialogData(reserveDialogHandler)
}, requestDelay)
requestOpeningDialogData(welcomeDialogHandler, privacyPolicyDialogHandler)
requestReserveDialogData(reserveDialogHandler)
}
/**
@ -131,17 +123,6 @@ object GlobalPriorityChainHelper : ISuperiorChain {
mainChain.resume()
}
/**
* 添加新的 handler 到优先级弹窗管理链 (插队!)
*/
fun queueNewHandler(handler: PriorityChainHandler) {
if (mainChain.isHandlerQueueEmpty()) {
observeLifecycle()
}
mainChain.addHandler(handler)
}
/**
* 请求首页启动弹窗相关的数据并执行相关 handler 的 preProcess
*/

View File

@ -1,28 +0,0 @@
package com.gh.common.prioritychain
import androidx.fragment.app.FragmentActivity
import com.gh.common.util.PackageHelper
import com.gh.gamecenter.common.base.GlobalActivityManager
class RequestInstalledListPermissionHandler : PriorityChainHandler(-1000) {
init {
updateStatus(STATUS_VALID)
}
override fun onProcess(): Boolean {
val currentActivity = GlobalActivityManager.currentActivity ?: return false
if (currentActivity !is FragmentActivity) return false
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity = currentActivity,
ignorePermanentlyDenied = true
) {
processNext()
}
return true
}
}

View File

@ -32,6 +32,5 @@ class BuildConfigImpl : IBuildConfigProvider {
override fun getVApiHost(): String = BuildConfig.VAPI_HOST
override fun getVDevApiHost(): String = BuildConfig.DEV_VAPI_HOST
override fun getLogProducerProject(): String = BuildConfig.LOG_HUB_PROJECT
}

View File

@ -1,103 +0,0 @@
package com.gh.common.provider
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.LinearLayout
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.facade.annotation.Route
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.util.ConcernContentUtils
import com.gh.common.view.ImageContainerView
import com.gh.gamecenter.ImageViewerActivity.Companion.getIntent
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.toArrayList
import com.gh.gamecenter.core.provider.IConcernArticleUtilsProvider
import com.gh.gamecenter.databinding.RecyclerGameArticleBinding
import com.gh.gamecenter.message.R
@Route(path = RouteConsts.provider.concernContentUtils, name = "ConcernContentUtils暴露服务")
class ConcernArticleUtilsProviderImpl : IConcernArticleUtilsProvider {
override fun addContentPic(
context: Context,
linearLayout: LinearLayout,
list: List<String>,
entrance: String,
width: Int
) {
ConcernContentUtils.addContentPic(context, linearLayout, list, entrance, width)
}
override fun createArticleBinding(parent: ViewGroup): ViewBinding {
val inflater = LayoutInflater.from(parent.context)
return RecyclerGameArticleBinding.inflate(inflater, parent, false)
}
override fun initArticleStyle(binding: ViewBinding) {
if (binding is RecyclerGameArticleBinding) {
val context = binding.root.context
binding.root
.setBackground(ContextCompat.getDrawable(context, R.drawable.reuse_listview_item_style))
binding.tvGameName.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
binding.tvTime.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
binding.tvTitle.setTextColor(ContextCompat.getColor(context, R.color.text_primary))
binding.tvDescription.setTextColor(ContextCompat.getColor(context, R.color.text_secondary))
binding.tvComment.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
binding.tvShare.setTextColor(ContextCompat.getColor(context, R.color.text_tertiary))
}
}
override fun getIvIcon(binding: ViewBinding): View =
(binding as RecyclerGameArticleBinding).ivIcon
override fun getTvGameName(binding: ViewBinding): TextView =
(binding as RecyclerGameArticleBinding).tvGameName
override fun getTvTime(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvTime
override fun getTvTitle(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvTitle
override fun getTvDescription(binding: ViewBinding): TextView =
(binding as RecyclerGameArticleBinding).tvDescription
override fun bindImgs(binding: ViewBinding, img: List<String>) {
if (binding is RecyclerGameArticleBinding) {
val images = img.map {
ImageContainerView.ImageContainerData.ImageInfo(it, 3, 2)
}
val imageContainerData =
ImageContainerView.ImageContainerData("", false, images, show = images.isNotEmpty())
binding.ivImagesContainer.bindData(imageContainerData,
object : ImageContainerView.OnImageContainerEventListener {
override fun onImageClick(
images: List<String>,
position: Int,
imageViewList: ArrayList<SimpleDraweeView>
) {
val checkIntent = getIntent(
binding.root.context,
images.toArrayList(),
position,
imageViewList,
""
)
binding.root.context.startActivity(checkIntent)
}
override fun onVideoCLick(videoId: String) = Unit
})
}
}
override fun getTvComment(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvComment
override fun getTvShare(binding: ViewBinding): TextView = (binding as RecyclerGameArticleBinding).tvShare
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -0,0 +1,26 @@
package com.gh.common.provider
import android.content.Context
import android.widget.LinearLayout
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.common.util.ConcernContentUtils
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IConcernContentUtilsProvider
@Route(path = RouteConsts.provider.concernContentUtils, name = "ConcernContentUtils暴露服务")
class ConcernContentUtilsProviderImpl : IConcernContentUtilsProvider {
override fun addContentPic(
context: Context,
linearLayout: LinearLayout,
list: List<String>,
entrance: String,
width: Int
) {
ConcernContentUtils.addContentPic(context, linearLayout, list, entrance, width)
}
override fun init(context: Context?) {
// Do nothing
}
}

View File

@ -1,55 +0,0 @@
package com.gh.common.provider
import android.content.Context
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.viewbinding.ViewBinding
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.toColor
import com.gh.gamecenter.core.provider.IConcernGiftPackUtilsProvider
import com.gh.gamecenter.databinding.RecyclerGiftPackBinding
@Route(path = RouteConsts.provider.concernGiftPackUtils, name = "ConcernGiftPackUtils暴露服务")
class ConcernGiftPackUtilsProviderImpl : IConcernGiftPackUtilsProvider {
override fun createBinding(parent: ViewGroup): ViewBinding {
val inflater = LayoutInflater.from(parent.context)
return RecyclerGiftPackBinding.inflate(inflater, parent, false)
.also {
it.gCode.goneIf(true)
}
}
override fun initStyle(viewBinding: ViewBinding) {
with(viewBinding as RecyclerGiftPackBinding) {
val context = root.context
tvGameName.setTextColor(R.color.text_primary.toColor(context))
tvTime.setTextColor(R.color.text_tertiary.toColor(context))
tvGiftPackName.setTextColor(R.color.text_primary.toColor(context))
tvGiftPackContent.setTextColor(R.color.text_secondary.toColor(context))
}
}
override fun getIvGameIcon(viewBinding: ViewBinding) =
(viewBinding as RecyclerGiftPackBinding).ivIcon
override fun getTvGameName(viewBinding: ViewBinding) =
(viewBinding as RecyclerGiftPackBinding).tvGameName
override fun getTvTime(viewBinding: ViewBinding) =
(viewBinding as RecyclerGiftPackBinding).tvTime
override fun getTvGiftPackName(viewBinding: ViewBinding) =
(viewBinding as RecyclerGiftPackBinding).tvGiftPackName
override fun getTvGiftPackContent(viewBinding: ViewBinding) =
(viewBinding as RecyclerGiftPackBinding).tvGiftPackContent
override fun init(context: Context?) {
// Do Nothing
}
}

View File

@ -1,19 +0,0 @@
package com.gh.common.provider
import android.content.Context
import androidx.appcompat.app.AppCompatActivity
import com.alibaba.android.arouter.facade.annotation.Route
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IConcernShareNewsProvider
import com.gh.gamecenter.newsdetail.NewsShareDialog
@Route(path = RouteConsts.provider.concernShareNews, name = "ConcernShareNews暴露服务")
class ConcernShareNewsProviderImpl : IConcernShareNewsProvider {
override fun share(activity: AppCompatActivity, shortId: String?, id: String?, gameIcon: String?, title: String?) {
NewsShareDialog.show(activity, shortId, id, gameIcon, title)
}
override fun init(context: Context?) {
// Do Nothing
}
}

View File

@ -1,6 +1,5 @@
package com.gh.common.provider
import android.app.Activity
import android.content.Context
import android.os.Bundle
import com.alibaba.android.arouter.facade.annotation.Route
@ -70,18 +69,10 @@ class DirectProviderImpl : IDirectProvider {
DirectUtils.directToAmway(context, fixedTopAmwayCommentId, entrance, path)
}
override fun directToQQGameHome(context: Context) {
override fun directToQGame(context: Context) {
return DirectUtils.directToQGameHome(context)
}
override fun directToQQGameById(activity: Activity, qqAppId: String) {
DirectUtils.directToQQGameById(activity, qqAppId)
}
override fun directToWechatGameById(activity: Activity, qqAppId: String) {
DirectUtils.directToWechatGameById(activity, qqAppId)
}
override fun directToExternalBrowser(context: Context, url: String) {
DirectUtils.directToExternalBrowser(context, url)
}

View File

@ -7,8 +7,8 @@ import android.widget.LinearLayout;
import com.facebook.drawee.drawable.ScalingUtils;
import com.facebook.drawee.view.SimpleDraweeView;
import com.gh.gamecenter.ImageViewerActivity;
import com.gh.gamecenter.common.utils.ImageUtils;
import com.gh.gamecenter.core.utils.DisplayUtils;
import com.gh.gamecenter.common.utils.ImageUtils;
import java.util.ArrayList;
import java.util.List;
@ -121,4 +121,5 @@ public class ConcernContentUtils {
}
return imageView;
}
}

View File

@ -1,14 +1,17 @@
package com.gh.common.util;
import android.content.Context;
import android.os.Build;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.utils.NetworkUtils;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.entity.NewsDetailEntity;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.gamecenter.manager.DataCollectionManager;
import com.gh.gamecenter.manager.PackagesManager;
import com.lightgame.download.DownloadEntity;
import java.util.HashMap;
@ -29,7 +32,7 @@ public class DataCollectionUtils {
map.put("type", android.os.Build.MODEL);
map.put("system", android.os.Build.VERSION.SDK_INT + "=" + android.os.Build.VERSION.RELEASE);
// WIFI实时
DataCollectionManager.onEvent(context, "error", map, true);
DataCollectionManager.onEvent(context, "error", map, NetworkUtils.isWifiConnected(context));
}
// 上传下载数据(开始、完成)

View File

@ -14,10 +14,12 @@ import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.eventbus.EBReuse;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.BiResponse;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.MtaHelper;
import com.gh.gamecenter.core.utils.SPUtils;
import com.gh.gamecenter.core.utils.SentryHelper;
import com.gh.gamecenter.login.entity.IdCardEntity;
@ -30,6 +32,8 @@ import com.halo.assistant.HaloApp;
import com.lightgame.config.CommonDebug;
import com.lightgame.utils.Utils;
import org.greenrobot.eventbus.EventBus;
import io.reactivex.schedulers.Schedulers;
import io.sentry.Sentry;
import io.sentry.android.core.SentryAndroid;
@ -104,51 +108,45 @@ public class DataUtils {
}
public static void getGid() {
// 默认用 APP 级已存储的 GID 来使用,不使用外部 GID
String savedGid = SPUtils.getString(Constants.GID);
if (!TextUtils.isEmpty(savedGid)) {
HaloApp.getInstance().setGid(savedGid);
onGidReceived(savedGid);
} else {
GidHelper.getInstance().registerDevice(HaloApp.getInstance().getApplication(), new GidCallback() {
@Override
public void onSuccess(String gid) {
Utils.log("Gid", gid);
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, gid).apply();
GidHelper.getInstance().registerDevice(HaloApp.getInstance().getApplication(), new GidCallback() {
@Override
public void onSuccess(String gid) {
Utils.log("Gid", gid);
PreferenceManager.getDefaultSharedPreferences(HaloApp.getInstance().getApplication()).edit().putString(Constants.DEVICE_KEY, gid).apply();
// 默认用 APP 级已存储的 GID 来使用,不使用外部 GID
String savedGid = SPUtils.getString(Constants.GID);
if (!TextUtils.isEmpty(savedGid)) {
gid = savedGid;
} else {
SPUtils.setString(Constants.GID, gid);
onGidReceived(gid);
}
@Override
public void onFailure(String s) {
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
HaloApp.getInstance().setGid(gid);
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
getDeviceCertification(gid);
// 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid
MetaUtil.INSTANCE.refreshMeta();
ContentValues values = new ContentValues();
values.put(GhContentProvider.KEY_GID, gid);
values.put(GhContentProvider.KEY_ANDROID_ID, MetaUtil.getBase64EncodedAndroidId());
try {
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/device"), values);
} catch (Exception exception) {
SentryHelper.INSTANCE.onEvent("DEVICE_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
exception.printStackTrace();
}
});
}
}
}
private static void onGidReceived(String gid) {
HaloApp.getInstance().setGid(gid);
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
getDeviceCertification(gid);
// 避免初始化顺序问题导致 MetaUtil 一直持有空的 gid
MetaUtil.INSTANCE.refreshMeta();
AppExecutor.getIoExecutor().execute(() -> {
ContentValues values = new ContentValues();
values.put(GhContentProvider.KEY_GID, gid);
values.put(GhContentProvider.KEY_ANDROID_ID, MetaUtil.getBase64EncodedAndroidId());
try {
HaloApp.getInstance().getContentResolver().insert(Uri.parse("content://com.gh.gamecenter.provider/device"), values);
} catch (Exception exception) {
SentryHelper.INSTANCE.onEvent("DEVICE_INSERT_ERROR", "exception_digest", exception.getLocalizedMessage());
exception.printStackTrace();
@Override
public void onFailure(String s) {
// 更新广告配置
AdDelegateHelper.INSTANCE.requestAdConfig(false, "", null);
}
});
}

View File

@ -633,19 +633,7 @@ public class DialogUtils {
binding.gameIcon.displayGameIcon(gameEntity);
binding.gameNameTv.setText(context.getString(R.string.dialog_land_page_address_hint, gameEntity.getName()));
AtomicBoolean dismissByButton = new AtomicBoolean(false);
binding.closeIv.setOnClickListener(v -> {
dismissByButton.set(true);
SensorsBridge.INSTANCE.trackJumpLandPageAddressDialogClick(
"取消",
gameEntity.getId(),
gameEntity.getName() != null ? gameEntity.getName() : "",
gameEntity.getCategoryChinese()
);
dialog.dismiss();
});
binding.closeIv.setOnClickListener(v -> dialog.dismiss());
binding.urlTv.setText(gameEntity.getLandPageAddressDialog().getLink());
@ -655,28 +643,10 @@ public class DialogUtils {
binding.downloadBtn.setText(downloadText);
binding.downloadBtn.setOnClickListener(v -> {
dismissByButton.set(true);
SensorsBridge.INSTANCE.trackJumpLandPageAddressDialogClick(
"前往第三方网站下载游戏",
gameEntity.getId(),
gameEntity.getName() != null ? gameEntity.getName() : "",
gameEntity.getCategoryChinese()
);
listener.onConfirm();
dialog.dismiss();
});
dialog.setOnDismissListener(dialog1 -> {
if (!dismissByButton.get()) {
SensorsBridge.INSTANCE.trackJumpLandPageAddressDialogClick(
"关闭弹窗",
gameEntity.getId(),
gameEntity.getName() != null ? gameEntity.getName() : "",
gameEntity.getCategoryChinese()
);
}
});
Window window = dialog.getWindow();
if (window != null) {
window.setBackgroundDrawable(new ColorDrawable(Color.TRANSPARENT));
@ -685,12 +655,6 @@ public class DialogUtils {
dialog.requestWindowFeature(Window.FEATURE_NO_TITLE);
dialog.setContentView(contentView);
dialog.show();
SensorsBridge.INSTANCE.trackJumpLandPageAddressDialogShow(
gameEntity.getId(),
gameEntity.getName() != null ? gameEntity.getName() : "",
gameEntity.getCategoryChinese()
);
}
public static void showGameH5DownloadDialog(Context context, GameEntity gameEntity, RegionSetting.GameH5Download gameH5Download) {

View File

@ -43,13 +43,11 @@ import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent
import com.gh.gamecenter.feature.exposure.ExposureType
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.feature.provider.IConcernInfoProvider
import com.gh.gamecenter.forum.detail.ForumDetailActivity
import com.gh.gamecenter.forum.home.CommunityActivity
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailActivity
import com.gh.gamecenter.game.columncollection.detail.ColumnCollectionDetailFragment
import com.gh.gamecenter.game.commoncollection.detail.CustomCommonCollectionDetailActivity
import com.gh.gamecenter.game.upload.GameSubmissionActivity
import com.gh.gamecenter.gamecollection.detail.GameCollectionDetailActivity
@ -65,9 +63,6 @@ import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
import com.gh.gamecenter.help.HelpAndFeedbackBridge
import com.gh.gamecenter.libao.LibaoDetailActivity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.minigame.MiniGameRecentlyPlayUseCase
import com.gh.gamecenter.minigame.MiniGameSearchActivity
import com.gh.gamecenter.minigame.qq.QGameHomeWrapperActivity
import com.gh.gamecenter.newsdetail.NewsDetailActivity
import com.gh.gamecenter.personalhome.UserHomeActivity
import com.gh.gamecenter.personalhome.background.PersonalityBackgroundActivity
@ -78,6 +73,9 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.subject.CommunitySubjectActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.gh.gamecenter.qgame.QGameHomeWrapperActivity
import com.gh.gamecenter.qgame.QGameSearchActivity
import com.gh.gamecenter.qgame.QGameViewModel
import com.gh.gamecenter.retrofit.RetrofitManager
import com.gh.gamecenter.servers.GameServerTestActivity
import com.gh.gamecenter.servers.GameServersActivity
@ -97,8 +95,6 @@ import com.gh.vspace.VDownloadManagerActivity
import com.halo.assistant.HaloApp
import com.halo.assistant.fragment.WebFragment
import com.lightgame.utils.Utils
import com.tencent.mm.opensdk.modelbiz.WXLaunchMiniProgram
import com.tencent.mm.opensdk.openapi.WXAPIFactory
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import retrofit2.HttpException
@ -112,14 +108,8 @@ import kotlin.math.roundToInt
object DirectUtils {
@JvmStatic
fun directToLinkPage(
context: Context,
linkEntity: LinkEntity,
entrance: String,
path: String,
sourceEntrance: String = ""
) {
directToLinkPage(context, linkEntity, entrance, path, null, sourceEntrance)
fun directToLinkPage(context: Context, linkEntity: LinkEntity, entrance: String, path: String, sourceEntrance: String = "") {
directToLinkPage(context, linkEntity, entrance, path, null, sourceEntrance)
}
@JvmStatic
@ -253,7 +243,7 @@ object DirectUtils {
path
)
HOST_WEB, HOST_WEB_INURL, HOST_WEB_AL, HOST_QA_CONTENT -> {
HOST_WEB, HOST_WEB_INURL, HOST_WEB_AL -> {
when {
linkEntity.link!!.contains("v.douyin") && PackageHelper.localPackageNameSet.contains("com.ss.android.ugc.aweme") -> {
directDouyin(context, "1402577827140941")
@ -353,7 +343,7 @@ object DirectUtils {
"feedback" -> directToFeedback(context, linkEntity.name, false, "", false, false, entrance)
"qa", "Q&A" -> directToQa(context, linkEntity.text ?: "", linkEntity.link ?: "")
"qa", "qa_content", "Q&A" -> directToQa(context, linkEntity.text ?: "", linkEntity.link ?: "")
"qa_list" -> directToHelpAndFeedback(context)
@ -485,22 +475,17 @@ object DirectUtils {
)
)
ColumnCollectionDetailFragment.TYPE_QQ_MINI_GAME_COLUMN -> directToQGameHome(context)
"qq_mini_game_column" -> directToQGameHome(context)
// QQ游戏专题详情页
ViewPagerFragmentHelper.TYPE_QQ_MINI_GAME_COLUMN, ViewPagerFragmentHelper.TYPE_WECHAT_GAME_COLUMN -> {
val subjectType = if (linkEntity.type == ViewPagerFragmentHelper.TYPE_QQ_MINI_GAME_COLUMN) {
SubjectData.SubjectType.QQ_GAME
} else {
SubjectData.SubjectType.WECHAT_GAME
}
"qq_mini_game_column_detail" -> {
directToSubject(
context = context,
id = linkEntity.link ?: "",
subjectName = linkEntity.text,
entrance = BaseActivity.mergeEntranceAndPath(entrance, path),
exposureEvent = exposureEvent,
subjectType = subjectType
isQQMiniGame = true
)
}
@ -551,10 +536,6 @@ object DirectUtils {
}
}
"wechat_game" -> linkEntity.link?.let {
MiniGameItemHelper.launchMiniGame(it, Constants.WECHAT_MINI_GAME)
}
"" -> {
// do nothing
}
@ -793,12 +774,7 @@ object DirectUtils {
}
}
if (traceEvent != null) {
val clickEvent = createEvent(
GameEntity(id = id, name = name),
traceEvent.source,
appendTrace(traceEvent),
ExposureType.CLICK
)
val clickEvent = createEvent(GameEntity(id = id, name = name), traceEvent.source, appendTrace(traceEvent), ExposureType.CLICK)
log(clickEvent)
bundle.putParcelable(KEY_TRACE_EVENT, clickEvent)
}
@ -861,12 +837,12 @@ object DirectUtils {
subjectName: String? = "",
entrance: String? = null,
exposureEvent: ExposureEvent? = null,
subjectType: SubjectData.SubjectType = SubjectData.SubjectType.NORMAL
isQQMiniGame: Boolean = false,
) {
if (id.isEmpty()) return
val bundle = Bundle()
val subjectData =
SubjectData(subjectId = id, subjectName = subjectName, isOrder = false, subjectType = subjectType)
SubjectData(subjectId = id, subjectName = subjectName, isOrder = false, isQQMiniGame = isQQMiniGame)
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
bundle.putString(KEY_TO, SubjectActivity::class.java.name)
bundle.putParcelable(EntranceConsts.KEY_SUBJECT_DATA, subjectData)
@ -1015,13 +991,7 @@ object DirectUtils {
}
@JvmStatic
fun directToQuestionDetail(
context: Context,
id: String,
entrance: String? = null,
path: String? = null,
sourceEntrance: String = ""
) {
fun directToQuestionDetail(context: Context, id: String, entrance: String? = null, path: String? = null, sourceEntrance: String = "") {
if (id.isEmpty()) return
val bundle = Bundle()
bundle.putString(KEY_ENTRANCE, entrance ?: ENTRANCE_BROWSER)
@ -1965,7 +1935,7 @@ object DirectUtils {
}
@JvmStatic
fun directToMiniGameSearch(
fun directToQGameSearch(
context: Context,
hint: String,
sourceEntrance: String,
@ -1977,7 +1947,7 @@ object DirectUtils {
searchBoxPattern: String = ""
) {
context.startActivity(
MiniGameSearchActivity.getIntent(
QGameSearchActivity.getIntent(
context,
hint,
sourceEntrance,
@ -1993,10 +1963,11 @@ object DirectUtils {
@SuppressLint("CheckResult")
@JvmStatic
fun directToQQGameById(
fun directToQGameById(
activity: Activity,
qqAppId: String
qqGameId: String
) {
if (activity !is AppCompatActivity || activity.supportFragmentManager.isDestroyed) {
ToastUtils.toast("启动QQ小游戏失败请稍后再试")
return
@ -2017,40 +1988,25 @@ object DirectUtils {
val qGameProvider = ARouter
.getInstance()
.build(RouteConsts.provider.qGame)
.navigation() as IQGameProvider
.navigation() as IQGameProvider<*>
qGameProvider.setLoginInfo(activity, userId, userName, userToken)
qGameProvider.launchGame(activity, qqAppId) { _, _ ->
MiniGameRecentlyPlayUseCase.submitRecentPlayedQGame(qqAppId, userId)
qGameProvider.launchGame(activity, qqGameId) { _, _ ->
RetrofitManager
.getInstance()
.newApi
.postQGamePlay(qqGameId, userId)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
QGameViewModel.notifyQGameSubjectUpdate() // 通知QQ小游戏首页列表刷新
},
{}
) // 秒玩记录上报
}
}
}
@SuppressLint("CheckResult")
fun directToWechatGameById(
activity: Activity,
wechatAppId: String,
) {
val wxApiProxy = WXAPIFactory.createWXAPI(
activity,
Config.WECHAT_APPID
)
if (!wxApiProxy.isWXAppInstalled) {
ToastUtils.toast("请安装微信客户端")
return
}
wxApiProxy.sendReq(
WXLaunchMiniProgram.Req().apply {
userName = wechatAppId
path = Constants.WECHAT_MINI_GAME_PCS
miniprogramType = WXLaunchMiniProgram.Req.MINIPTOGRAM_TYPE_RELEASE;
}
)
MiniGameRecentlyPlayUseCase.submitRecentPlayedWGame(wechatAppId, HaloApp.getInstance().gid)
}
@JvmStatic
fun directToMessageCenter(defaultTabIndex: Int) {
ARouter.getInstance().build(RouteConsts.activity.messageWrapperActivity)
@ -2126,7 +2082,7 @@ object DirectUtils {
)
)
BottomTab.SearchStyle.TYPE_MINI_GAME -> directToMiniGameSearch(
BottomTab.SearchStyle.TYPE_QQ_MINI_GAME -> directToQGameSearch(
context,
"请输入小游戏关键词",
sourceEntrance,

View File

@ -27,6 +27,7 @@ import com.gh.download.server.BrowserInstallHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.WebActivity
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.callback.CancelListener
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.constant.EntranceConsts
@ -40,7 +41,6 @@ import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.view.DownloadButton
import com.gh.gamecenter.gamedetail.dialog.GamePermissionDialogFragment
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.gh.gamecenter.teenagermode.TeenagerModeActivity
import com.gh.vspace.VHelper
import com.lightgame.download.DownloadConfig
@ -247,8 +247,9 @@ object DownloadItemUtils {
}
return
}
if (gameEntity.isMiniGame()) {
if (gameEntity.isMiniGameOffShelve()) {
if (gameEntity.isQQMiniGame()) {
val isQQMiniGameOffShelve = gameEntity.qqMiniGameAppStatus == 1 // QQ小游戏是否下架
if (isQQMiniGameOffShelve) {
downloadBtn.apply {
isClickable = false
text = context.getString(R.string.off_shelve)
@ -843,11 +844,12 @@ object DownloadItemUtils {
}
return
}
if (gameEntity.isMiniGame()) {
if (gameEntity.isQQMiniGame()) {
downloadBtn.setOnClickListener {
MiniGameItemHelper.launchMiniGame(gameEntity.miniGameAppId, gameEntity.miniGameType)
clickCallback?.onCallback()
allStateClickCallback?.onCallback()
NewFlatLogUtils.logQGameClick(gameEntity.qqMiniGameAppId, gameEntity.name)
GlobalActivityManager.currentActivity?.let { activity ->
DirectUtils.directToQGameById(activity, gameEntity.qqMiniGameAppId)
}
}
return
}

View File

@ -2007,6 +2007,29 @@ object NewFlatLogUtils {
log(json, "event", false)
}
// vpn 弹窗显示
fun logVpnHintDialogShow(gameId: String, gameName: String) {
val json = json {
KEY_EVENT to "vpn_pre_access_dialog_show"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
// vpn 弹窗点击
fun logVpnHintDialogClick(gameId: String, gameName: String, button: String) {
val json = json {
KEY_EVENT to "vpn_pre_access_dialog_click"
KEY_GAME_ID to gameId
KEY_GAME_NAME to gameName
"button" to button
parseAndPutMeta().invoke(this)
}
log(json, "event", false)
}
// 谷歌框架弹窗显示
fun logGAppsDialogShowed(gameId: String, gameName: String) {
val json = json {
@ -2577,6 +2600,17 @@ object NewFlatLogUtils {
log(json)
}
@JvmStatic
fun logQGameClick(qqGameId: String, qqGameName: String?) {
val json = json {
KEY_EVENT to "qq_game_click"
"qq_game_id" to qqGameId
"qq_game_name" to qqGameName ?: ""
parseAndPutMeta().invoke(this)
}
log(json)
}
// 点击个人主页的认证文案事件
@JvmStatic
fun logClickAuthText(linkType: String, linkId: String, linkText: String, userId: String, text: String) {

View File

@ -1,186 +0,0 @@
package com.gh.common.util
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gh.download.DownloadManager
import com.gh.download.PackageObserver
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.lightgame.utils.Utils
import org.greenrobot.eventbus.EventBus
object PackageChangeHelper : DefaultLifecycleObserver {
private const val TAG = "PackageChangeHelper"
private const val INSTALL_PENDING = 1
private const val UNINSTALL_PENDING = 2
private const val UPDATE_PENDING = 3
// <包名pending 类型,应用版本> Triple
private var pendingPackagePair: Triple<String, Int, String>? = null
private var pendingGhId: String ? = null
/**
* 添加一个等待中,待确定是否已成功安装的应用
*/
fun addInstallPendingPackage(packageName: String) {
val installData = PackagesManager.getInstalledData(packageName)
if (installData == null) {
Utils.log(TAG, "添加了: $packageName 包名等待安装成功")
pendingPackagePair = Triple(packageName, INSTALL_PENDING, "")
} else {
Utils.log(TAG, "添加了: $packageName 包名等待安装更新成功")
val ghId = PackageUtils.getGhId(packageName)
// 记录光环插件相关信息,用于安装成功后的处理
if (ghId != null) {
pendingGhId = ghId.toString()
}
pendingPackagePair = Triple(packageName, UPDATE_PENDING, installData.version)
}
}
/**
* 添加一个等待中,待确定是否已成功卸载的应用
*/
fun addUninstallPendingPackage(packageName: String) {
Utils.log(TAG, "添加了: $packageName 包名等待卸载成功")
pendingPackagePair = Triple(packageName, UNINSTALL_PENDING, "")
}
override fun onResume(owner: LifecycleOwner) {
super.onResume(owner)
if (pendingPackagePair != null) {
val packageName = pendingPackagePair?.first ?: return
val isInstallPending = pendingPackagePair?.second == INSTALL_PENDING
val isUninstallPending = pendingPackagePair?.second == UNINSTALL_PENDING
val isUpdatePending = pendingPackagePair?.second == UPDATE_PENDING
val pendingVersion = pendingPackagePair?.third ?: ""
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
val isInstalled = installedVersionName != null
if (isInstallPending && isInstalled) {
pendingPackagePair = null
pendingGhId = null
PackageRepository.addInstalledGame(packageName)
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
performInstallSuccessAction(packageName)
} else if (isUninstallPending && !isInstalled) {
pendingPackagePair = null
pendingGhId = null
// 从额外的包名白名单移除,下次启动时不再查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = false)
performUninstallSuccessAction(packageName)
} else if (isUpdatePending) {
val isUpdateValid = if (installedVersionName != pendingVersion) {
true
} else {
!pendingGhId.isNullOrEmpty() && pendingGhId != PackageUtils.getGhId(packageName).toString()
}
pendingPackagePair = null
pendingGhId = null
if (isUpdateValid) {
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName)
}
// 添加到额外的包名白名单,下次启动同时查询此包的安装情况
PackageHelper.updateAdditionalWhiteListPackageName(packageName = packageName, isAdd = true)
}
}
}
/**
* 对外暴露的方法,用于添加一个安装成功的应用
* @param packageName 包名
* @param cachedGameEntity 缓存的 GameEntity用于快速更新状态
*/
fun addInstall(packageName: String, cachedGameEntity: GameEntity? = null) {
performInstallSuccessAction(packageName, cachedGameEntity)
}
/**
* 对外暴露的方法,用于添加一个卸载成功的应用
* @param packageName 包名
* @param cachedGameEntity 缓存的 GameEntity用于快速更新状态
*/
fun addUpdate(packageName: String, cachedGameEntity: GameEntity? = null) {
performUninstallSuccessAction(packageName)
performInstallSuccessAction(packageName, cachedGameEntity)
}
/**
* 对应包名安装成功后的操作,继承至 PackageChangeBroadcastObserver
* @param packageName 包名
* @param cachedGameEntity 缓存的 GameEntity用于快速更新状态
* @param withLog 是否需要记录日志
*/
private fun performInstallSuccessAction(packageName: String,
cachedGameEntity: GameEntity? = null,
withLog: Boolean = true) {
Utils.log(TAG, "安装了: $packageName 包名的程序")
val downloadEntity = DownloadManager.getInstance().getDownloadEntitySnapshotByPackageName(packageName)
val gameId = if (downloadEntity != null && downloadEntity.gameId != null) downloadEntity.gameId else ""
val gameName = if (downloadEntity != null && downloadEntity.name != null) downloadEntity.name else ""
if (withLog) {
NewFlatLogUtils.logGameInstallComplete(gameId, gameName)
SensorsBridge.trackInstallGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeInstall(packageName)
PackageHelper.refreshLocalPackageList()
val versionName = PackageUtils.getVersionNameByPackageName(packageName)
val installEb = EBPackage(EBPackage.TYPE_INSTALLED, packageName, versionName)
PackageObserver.onPackageChanged(installEb, cachedGameEntity)
EventBus.getDefault().post(installEb)
}
fun addUninstall(packageName: String) {
performUninstallSuccessAction(packageName)
}
/**
* 对应包名卸载成功后的操作,继承至 PackageChangeBroadcastObserver
*/
private fun performUninstallSuccessAction(packageName: String, withLog: Boolean = true) {
Utils.log(TAG, "卸载了: $packageName 包名的程序")
val install = PackagesManager.getInstalledData(packageName)
val gameId = if (install?.id != null) install.id else ""
val gameName = if (install?.name != null) install.name else ""
if (withLog) {
NewFlatLogUtils.logGameUninstallComplete(gameId!!, gameName!!)
SensorsBridge.trackUnloadGameFinish(gameId, gameName)
}
InstallUtils.getInstance().removeUninstall(packageName)
PackageHelper.refreshLocalPackageList()
val uninstallEb = EBPackage(EBPackage.TYPE_UNINSTALLED, packageName, "")
PackageObserver.onPackageChanged(uninstallEb)
EventBus.getDefault().post(uninstallEb)
}
}

View File

@ -1,6 +1,5 @@
package com.gh.common.util
import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.ApplicationInfo
import android.content.pm.PackageInfo
@ -8,31 +7,15 @@ import android.content.pm.PackageManager
import android.content.pm.PermissionInfo
import android.os.Build
import android.provider.Settings
import androidx.fragment.app.FragmentActivity
import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData
import com.gh.common.constant.Config
import com.gh.gamecenter.common.retrofit.BiResponse
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.common.utils.PermissionHelper
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.toJson
import com.gh.gamecenter.common.utils.toObject
import com.gh.gamecenter.core.GHThreadFactory
import com.gh.gamecenter.core.runOnUiThread
import com.gh.gamecenter.common.utils.PermissionHelper.isGetInstalledListPermissionDisabled
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.entity.WhitePackageListEntity
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.retrofit.RetrofitManager
import com.halo.assistant.HaloApp
import com.lightgame.utils.Utils
import io.reactivex.schedulers.Schedulers
import kotlinx.coroutines.*
import java.util.HashMap
import java.util.concurrent.Executors
import java.io.BufferedReader
import java.io.IOException
import java.io.InputStreamReader
import kotlin.collections.ArrayList
import kotlin.collections.HashSet
@ -40,34 +23,16 @@ object PackageHelper {
private const val TAG = "PackageHelper"
private const val SP_GET_INSTALLED_PACKAGES_AGREED = "get_installed_packages_agreed" // 用户是否同意使用已安装应用列表 API
private const val SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API = "get_installed_packages_by_alternative_api" // 是否使用另类方式获取已安装应用列表
private const val SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE = "user_used_get_installed_api_switch_package" // 用户是否使用过含有已安装应用列表获取开关的包
private const val SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST = "additional_whitelist_package_name_list" // 额外的白名单包名列表 (曾经更正为已安装的包名列表)
private const val SP_GET_INSTALLED_API_AGREED = "get_installed_api_agreed"
private const val UNKNOWN = -1
private const val UNSUPPORTED = 0
private const val SUPPORTED = 1
private const val ENABLED = 2
private const val DISABLED = 3
private var lastSuccessfullyGetInstalledPackagesTimeMills = 0L
private var cachedInstalledPackagesList: List<PackageInfo> = arrayListOf()
private var additionalWhiteListPackageNameSet: HashSet<String> = hashSetOf()
private var isGetInstalledPackagesAgreed = false // 用户是否已经同意使用已安装应用列表
private var isGetInstalledPackagesAgreedRequired = UNKNOWN // 需要用户手动授权才获取已安装应用列表的功能的开关
private var isGetInstalledPackagesPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表
private var useAlternativeWayToGetInstalledPackages = false
private var cachedPkgNameAndGameEntityMap = HashMap<String, GameEntity>() // 缓存的状态异常包名和游戏实体的映射 (用于免接口请求更新状态)
private val packageExecutor by lazy { Executors.newSingleThreadExecutor(GHThreadFactory("GH_PACKAGE_THREAD")) }
private var lastInstalledPackageListTime = 0L
private var installedPackageList: List<PackageInfo> = arrayListOf()
private var isGetInstalledPackagesApiAgreed = false
private var isGetInstalledListPermissionSupported = UNKNOWN // 设备是否支持禁用获取已安装应用列表。-1 代表支持情况未知0 代表不支持, 1 代表支持
// 评论黑名单包名列表,避免用户安装了 Xposed Installer 这样的工具,也能在包含该安装包的游戏详情页评论
private var _commentPackageNameBlackList = arrayListOf<String>()
@ -85,10 +50,6 @@ object PackageHelper {
private var _relatedPackageList = arrayListOf<SettingsEntity.GameWithPackages>()
val relatedPackageList: ArrayList<SettingsEntity.GameWithPackages> = _relatedPackageList
// 接口控制的已安装应用列表获取开关状态 (UI 显示)
private var _installedPackageApiSwitchStatusLiveData = MutableLiveData<Boolean>()
val installedPackageApiSwitchStatusLiveData: LiveData<Boolean> = _installedPackageApiSwitchStatusLiveData
// 本地已安装包的列表
var localPackageNameSet = hashSetOf<String>()
get() {
@ -100,62 +61,6 @@ object PackageHelper {
}
}
/**
* 检查是否需要忽略接口控制的已安装应用列表获取开关
* 用于覆盖安装首次启动时的检查 https://jira.shanqu.cc/browse/GHZSCY-5694
*/
fun checkIfGetInstalledApiSwitchShouldBeIgnored(context: Context) {
val userHasUsedGetInstalledApiSwitchPackage = SPUtils.getBoolean(SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE, false)
if (!userHasUsedGetInstalledApiSwitchPackage) {
val isSupportGetInstalledAppsPermission = isSupportGetInstalledAppsPermission(context)
if (!isSupportGetInstalledAppsPermission) {
// 设备不支持动态管理获取已安装应用列表,忽略接口控制,使用另类方式获取已安装应用列表
updateUseAlternativeWayToGetInstalledPackages()
onGetInstalledPackagesAgreed()
} else if (!PermissionHelper.isGetInstalledListPermissionDisabled(context)) {
// 设备支持动态管理获取已安装应用列表但已经授权,忽略接口控制,使用另类方式获取已安装应用列表
updateUseAlternativeWayToGetInstalledPackages()
onGetInstalledPackagesAgreed()
}
SPUtils.getBoolean(SP_USER_USED_GET_INSTALLED_API_SWITCH_PACKAGE, true)
}
}
/**
* 获取包名白名单列表(为了在没有已安装应用列表获取能力的时候也能正常判断更新、插件化)
* @param additionalWhiteList 额外的已安装白名单
*/
@SuppressLint("CheckResult")
fun getPackagesWhiteList(additionalWhiteList: HashSet<String>) {
RetrofitManager.getInstance().newApi.installWhitelist
.subscribeOn(Schedulers.io())
.subscribe(object : BiResponse<WhitePackageListEntity>() {
override fun onSuccess(data: WhitePackageListEntity) {
val installedWhiteList = hashSetOf<String>()
if (additionalWhiteList.isNotEmpty()) {
installedWhiteList.addAll(additionalWhiteList)
}
data.data?.let {
installedWhiteList.addAll(it)
}
addInstalledButMissingPackages(installedWhiteList)
}
override fun onFailure(exception: Exception) {
super.onFailure(exception)
if (additionalWhiteList.isNotEmpty()) {
addInstalledButMissingPackages(additionalWhiteList)
}
}
})
}
@JvmStatic
fun refreshLocalPackageList() {
localPackageNameSet = getAllPackageName(HaloApp.getInstance().application)
@ -163,7 +68,7 @@ object PackageHelper {
}
@JvmStatic
fun refreshPackageNameList() {
fun refreshList() {
Config.getSettings()?.gameCommentBlackList?.let { _commentPackageNameBlackList = ArrayList(it) }
Config.getSettings()?.gameDownloadBlackList?.let { _downloadPackageNameBlackList = ArrayList(it) }
Config.getSettings()?.gamePackageMatch?.let { _relatedPackageList = ArrayList(it) }
@ -201,90 +106,24 @@ object PackageHelper {
* 弃用已安装列表缓存
*/
fun dumpInstalledListCache() {
lastSuccessfullyGetInstalledPackagesTimeMills = 0
}
/**
* 在超时后,若后台没有开启获取已安装应用列表的功能,回落到以接口不控制的方式获取已安装应用列表
*/
fun ignoreInstalledPackageApiSwitchAfterTimeout(timeout: Long) {
CoroutineScope(SupervisorJob()).launch {
delay(timeout)
if (isGetInstalledPackagesAgreedRequired == UNKNOWN && !isGetInstalledPackagesAgreed()) {
Utils.log(TAG, "后台没有开启获取已安装应用列表的功能,超时后回落到以接口不控制的方式获取已安装应用列表")
updateIsGetInstalledPackagesApiAgreedRequired(false)
}
}
}
/**
* 更新已安装应用列表获取开关状态
*/
fun updateIsGetInstalledPackagesApiAgreedRequired(isEnabled: Boolean) {
Utils.log(TAG, "updateIsGetInstalledPackagesApiAgreedRequired 入参为 $isEnabled")
// 状态不变,无需更新
if ((isEnabled == true && isGetInstalledPackagesAgreedRequired == ENABLED)
|| (isEnabled == false && isGetInstalledPackagesAgreedRequired == DISABLED)
) {
Utils.log(TAG, "isGetInstalledPackagesApiAgreedRequired 状态不变,无需更新")
return
}
// 若用户已经同意使用了,无需更新
if (isGetInstalledPackagesAgreed()) {
Utils.log(TAG, "用户已经同意使用了,无需再更新已安装应用列表获取开关状态")
return
}
if (isEnabled) {
Utils.log(TAG, "开启获取已安装应用列表限制")
additionalWhiteListPackageNameSet = SPUtils.getString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST).toObject() ?: hashSetOf()
Utils.log(TAG, "额外的白名单为 $additionalWhiteListPackageNameSet")
getPackagesWhiteList(additionalWhiteListPackageNameSet)
_installedPackageApiSwitchStatusLiveData.postValue(true)
isGetInstalledPackagesAgreedRequired = ENABLED
} else {
Utils.log(TAG, "不开启获取已安装应用列表限制")
isGetInstalledPackagesAgreedRequired = DISABLED
// 启用另类获取已安装应用列表的 API
updateUseAlternativeWayToGetInstalledPackages()
onGetInstalledPackagesAgreed()
}
lastInstalledPackageListTime = 0
}
/**
* 用户是否已经允许了调用获取已安装应用列表接口
* 优先用内存的值,没有再从 SP 中获取并更新
*/
fun isGetInstalledPackagesAgreed(): Boolean {
return isGetInstalledPackagesAgreed
|| (SPUtils.getBoolean(SP_GET_INSTALLED_PACKAGES_AGREED).also { isGetInstalledPackagesAgreed = it })
fun isGetInstalledPackagesApiAgreed(): Boolean {
return isGetInstalledPackagesApiAgreed
|| (SPUtils.getBoolean(SP_GET_INSTALLED_API_AGREED).also { isGetInstalledPackagesApiAgreed = it })
}
/**
* 用户是否已经允许了调用获取已安装应用列表接口
* 优先用内存的值,没有再从 SP 中获取并更新
* 同意使用已安装应用列表 API
*/
private fun isUseAlternativeWayToGetInstalledPackages(): Boolean {
return useAlternativeWayToGetInstalledPackages
|| (SPUtils.getBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API).also { useAlternativeWayToGetInstalledPackages = it })
}
private fun updateUseAlternativeWayToGetInstalledPackages() {
// 启用另类获取已安装应用列表的 API
useAlternativeWayToGetInstalledPackages = true
SPUtils.setBoolean(SP_GET_INSTALLED_PACKAGES_BY_ALTERNATIVE_API, true)
}
fun isGetInstalledPackagesAgreedRequired(): Boolean {
return isGetInstalledPackagesAgreedRequired == ENABLED
fun agreeOnGetInstalledPackagesApi() {
isGetInstalledPackagesApiAgreed = true
SPUtils.setBoolean(SP_GET_INSTALLED_API_AGREED, true)
}
/**
@ -293,158 +132,52 @@ object PackageHelper {
fun getInstalledPackages(context: Context?, flags: Int): List<PackageInfo> {
Utils.log(TAG, "即将获取已安装应用列表")
// Utils.log(TAG, "即将获取已安装应用列表" + Thread.currentThread().getStackTrace().contentToString().replace( ',', '\n' ))
// 用户未同意使用已安装应用列表 API返回空列表
if (!isGetInstalledPackagesAgreed()) {
if (!isGetInstalledPackagesApiAgreed()) {
Utils.log(TAG, "用户未同意使用已安装应用列表 API返回空列表")
return cachedInstalledPackagesList
return installedPackageList
}
// 简单 debounce 过于频繁的获取已安装应用列表调用
if (System.currentTimeMillis() - lastSuccessfullyGetInstalledPackagesTimeMills < 20 * 1000
&& cachedInstalledPackagesList.isNotEmpty()) {
if (System.currentTimeMillis() - lastInstalledPackageListTime < 3000 && installedPackageList.isNotEmpty()) {
Utils.log(TAG, "使用了缓存的已安装应用列表")
return cachedInstalledPackagesList
return installedPackageList
}
var shouldGetNewInstalledPackagedList = false
// 当前设备是否支持限制获取已安装应用列表的功能
if (isSupportGetInstalledAppsPermission(context!!)) {
Utils.log(TAG, "当前设备支持动态管理获取已安装应用列表的功能")
Utils.log(TAG, "当前设备支持限制获取已安装应用列表的功能")
// 当前设备是否支持禁用了获取已安装应用列表
if (!PermissionHelper.isGetInstalledListPermissionDisabled(context)) {
Utils.log(TAG, "当前设备支持动态管理但没有限制获取已安装应用列表的功能")
if (!isGetInstalledListPermissionDisabled(context)) {
Utils.log(TAG, "当前设备没有限制获取已安装应用列表的功能")
shouldGetNewInstalledPackagedList = true
} else {
Utils.log(TAG, "当前设备支持动态管理且已限制获取已安装应用列表的功能")
Utils.log(TAG, "当前设备已限制获取已安装应用列表的功能")
}
} else {
Utils.log(TAG, "当前设备不支持动态管理获取已安装应用列表的功能")
Utils.log(TAG, "当前设备不支持限制获取已安装应用列表的功能")
shouldGetNewInstalledPackagedList = true
}
if (shouldGetNewInstalledPackagedList) {
lastSuccessfullyGetInstalledPackagesTimeMills = System.currentTimeMillis()
cachedInstalledPackagesList = getInstalledPackagesInternal(context, flags)
Utils.log(TAG, "获取已安装应用列表成功,数量为 ${cachedInstalledPackagesList.size}")
lastInstalledPackageListTime = System.currentTimeMillis()
installedPackageList = getInstalledPackagesInternal(context, flags)
}
return cachedInstalledPackagesList
}
/**
* 显示获取已安装应用列表的对话框并请求权限
*/
fun showGetInstallAppsListDialogAndRequestPermissionIfNeeded(
activity: FragmentActivity,
ignorePermanentlyDenied: Boolean = false,
resultClosure: (Boolean) -> Unit
) {
if (isSupportGetInstalledAppsPermission(activity)) {
// 若系统已经授予了获取应用列表的权限,直接执行授权成功回调
if (!PermissionHelper.isGetInstalledListPermissionDisabled(activity)) {
onGetInstalledPackagesAgreed()
resultClosure.invoke(true)
return
}
PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = true,
ignorePermanentlyDenied = ignorePermanentlyDenied
) { isGranted ->
if (isGranted) {
SensorsBridge.trackInstalledListPermissionsResult("成功")
onGetInstalledPackagesAgreed()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
} else {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsResult("拒绝")
}
}
} else {
val hintDialog = PermissionHelper.showGetInstalledAppsListPermissionDialog(
activity = activity,
requestPermission = false,
) {
// do nothing
}
SensorsBridge.trackInstalledListPermissionsCustomDialogShow()
val noticeDialog = DialogHelper.showGuideDialog(
context = activity,
title = "权限申请",
content = "是否允许“光环助手”获取已安装的应用信息",
confirmText = "开启",
cancelText = "拒绝",
confirmClickCallback = {
SensorsBridge.trackInstalledListPermissionsCustomClick("开启")
onGetInstalledPackagesAgreed()
resultClosure.invoke(true)
trackInstalledListAfterDelay()
},
cancelClickCallback = {
resultClosure.invoke(false)
SensorsBridge.trackInstalledListPermissionsCustomClick("拒绝")
}
)
noticeDialog?.setOnDismissListener {
hintDialog?.dismiss()
}
}
}
/**
* 执行用户授权获取已安装应用列表的操作
*/
private fun onGetInstalledPackagesAgreed() {
isGetInstalledPackagesAgreed = true
SPUtils.setBoolean(SP_GET_INSTALLED_PACKAGES_AGREED, true)
_installedPackageApiSwitchStatusLiveData.postValue(false)
// 进行包名初始化相关的操作
initPackageRelatedData()
}
/**
* 进行包名初始化相关的操作
*/
fun initPackageRelatedData() {
PackageRepository.initData()
refreshLocalPackageList()
refreshPackageNameList()
}
/**
* 延迟5秒后上报已安装应用列表
*/
private fun trackInstalledListAfterDelay() {
CoroutineScope(SupervisorJob()).launch {
delay(5000)
SensorsBridge.trackNumberOfInstalledList(localPackageNameSet.size, localPackageNameSet)
}
return installedPackageList
}
/**
* 是否支持动态获取已安装应用列表权限
*/
fun isSupportGetInstalledAppsPermission(context: Context): Boolean {
if (isUseAlternativeWayToGetInstalledPackages()) {
// 已经使用另类获取已安装应用列表形式,强制判定为不支持动态获取已安装应用列表权限
return false
}
// 若存在缓存,直接返回缓存结果。
if (isGetInstalledPackagesPermissionSupported != UNKNOWN) {
return isGetInstalledPackagesPermissionSupported != UNSUPPORTED
if (isGetInstalledListPermissionSupported != UNKNOWN) {
return isGetInstalledListPermissionSupported != UNSUPPORTED
}
try {
@ -452,7 +185,7 @@ object PackageHelper {
val flag =
Settings.Secure.getInt(context.contentResolver, "oem_installed_apps_runtime_permission_enable", 0)
if (flag == 1) {
isGetInstalledPackagesPermissionSupported = SUPPORTED
isGetInstalledListPermissionSupported = SUPPORTED
return true
}
@ -461,239 +194,62 @@ object PackageHelper {
val permissionInfo = packageManager.getPermissionInfo("com.android.permission.GET_INSTALLED_APPS", 0)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
if (permissionInfo.protection == PermissionInfo.PROTECTION_DANGEROUS) {
isGetInstalledPackagesPermissionSupported = SUPPORTED
isGetInstalledListPermissionSupported = SUPPORTED
return true
} else {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
isGetInstalledListPermissionSupported = UNSUPPORTED
return false
}
} else {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
isGetInstalledListPermissionSupported = UNSUPPORTED
return false
}
} catch (e: PackageManager.NameNotFoundException) {
isGetInstalledPackagesPermissionSupported = UNSUPPORTED
isGetInstalledListPermissionSupported = UNSUPPORTED
return false
}
}
/**
* 确保指定包名的应用在已安装了的情况下能正常收录
* 在5.1系统手机使用PackageManager获取已安装应用容易发生Package manager has died异常
* https://stackoverflow.com/questions/13235793/transactiontoolargeeception-when-trying-tÏo-get-a-list-of-applications-installed/30062632#30062632
*/
fun addInstalledButMissingPackages(packageNameSet: HashSet<String>) {
Utils.log(TAG, "addInstalledButMissingPackages 检查已安装但未收录的应用")
private fun getInstalledPackagesInternal(context: Context, flags: Int): List<PackageInfo> {
Utils.log(TAG, "调用系统 API 获取已安装应用列表")
val installedPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
val installedPkgNameExistInMemory = PackagesManager.isInstalled(packageName)
val packageNameInstalledOnDevice = PackageUtils.getVersionNameByPackageName(packageName) != null
if (!installedPkgNameExistInMemory && packageNameInstalledOnDevice) {
installedPackageNameSet.add(packageName)
}
if (!packageNameInstalledOnDevice) {
additionalWhiteListPackageNameSet.remove(packageName)
}
}
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toJson())
Utils.log(TAG, "addInstalledButMissingPackages 需要请求接口获取的包数量为 ${installedPackageNameSet.size}")
PackageRepository.addInstalledGames(
pkgNameList = ArrayList(installedPackageNameSet),
updateInstallStatus = true
)
}
/**
* 更新额外的白名单包名列表
* @param packageName 包名
* @param isAdd 是否添加
*/
fun updateAdditionalWhiteListPackageName(packageName: String, isAdd: Boolean) {
val isUpdated = if (isAdd) {
additionalWhiteListPackageNameSet.add(packageName)
} else {
additionalWhiteListPackageNameSet.remove(packageName)
}
Utils.log(TAG, "updateAdditionalWhiteListPackageName 更新额外的白名单包名列表 $isAdd $packageName ,结果是 $isUpdated")
if (isUpdated) {
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toJson())
}
}
/**
* 刷新安装状态异常 (适用于仅存在包名信息的列表)
*/
fun refreshWrongInstallStatus(packageNameSet: MutableSet<String>) {
packageExecutor.execute {
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
for (packageName in packageNameSet) {
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
if (PackagesManager.isInstalled(packageName)
&& installedVersionName == null) {
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName != null
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)) {
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
}
}
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}")
runOnUiThread {
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUninstall(packageName)
additionalWhiteListPackageNameSet.remove(packageName)
}
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toString())
}
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUpdate(packageName)
}
}
}
}
}
/**
* 刷新安装状态异常 (适用于存在游戏信息的列表)
*/
fun refreshWrongInstallStatus(gameEntityList: ArrayList<GameEntity>) {
packageExecutor.execute {
Utils.log(TAG, "refreshWrongInstallStatus 检查安装状态异常的应用")
val installedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val uninstalledButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
val updatedButKeepingWrongStatusPackageNameSet: HashSet<String> = hashSetOf()
for (game in gameEntityList) {
for (apk in game.getApk()) {
val packageName = apk.packageName
val installedVersionName = PackageUtils.getVersionNameByPackageName(packageName)
if (!PackagesManager.isInstalled(packageName)
&& installedVersionName != null
) {
cachedPkgNameAndGameEntityMap.put(packageName, game)
installedButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName == null
) {
uninstalledButKeepingWrongStatusPackageNameSet.add(packageName)
} else if (PackagesManager.isInstalled(packageName)
&& installedVersionName != null
&& !PackagesManager.isInstalledWithSpecificVersion(packageName, installedVersionName)
&& !PackagesManager.isCanUpdate(game.id, packageName, false)
) {
cachedPkgNameAndGameEntityMap.put(packageName, game)
updatedButKeepingWrongStatusPackageNameSet.add(packageName)
}
}
}
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已安装状态的包数量为 ${installedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要更新已更新状态的包数量为 ${updatedButKeepingWrongStatusPackageNameSet.size}")
Utils.log(TAG, "refreshWrongInstallStatus 需要移除已安装的包数量为 ${uninstalledButKeepingWrongStatusPackageNameSet.size}")
runOnUiThread {
if (installedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in installedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addInstall(packageName, cachedPkgNameAndGameEntityMap.remove(packageName))
additionalWhiteListPackageNameSet.add(packageName)
}
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toString())
}
if (uninstalledButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in uninstalledButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUninstall(packageName)
additionalWhiteListPackageNameSet.remove(packageName)
}
SPUtils.setString(SP_ADDITIONAL_WHITELIST_PACKAGE_NAME_LIST, additionalWhiteListPackageNameSet.toString())
}
if (updatedButKeepingWrongStatusPackageNameSet.isNotEmpty()) {
for (packageName in updatedButKeepingWrongStatusPackageNameSet) {
PackageChangeHelper.addUpdate(packageName, cachedPkgNameAndGameEntityMap.remove(packageName))
}
}
}
}
}
private fun getInstalledPackagesInternal(
context: Context,
flags: Int
): List<PackageInfo> {
return if (isUseAlternativeWayToGetInstalledPackages()) {
Utils.log(TAG, "调用另类系统 API 获取已安装应用列表")
getInstalledPackageByAlternative(context)
} else {
Utils.log(TAG, "调用默认系统 API 获取已安装应用列表")
getInstalledPackageByDefault(context, flags)
}
}
private fun getInstalledPackageByDefault(context: Context, flags: Int): List<PackageInfo> {
val pm = context.packageManager
try {
return pm.getInstalledPackages(flags)
} catch (ignored: java.lang.Exception) {
//we don't care why it didn't succeed. We'll do it using an alternative way instead
}
// use fallback:
val process: Process
val result: MutableList<PackageInfo> = java.util.ArrayList()
var bufferedReader: BufferedReader? = null
try {
process = Runtime.getRuntime().exec("pm list packages")
bufferedReader = BufferedReader(InputStreamReader(process.inputStream))
var line: String
while ((bufferedReader.readLine().also { line = it }) != null) {
val packageName = line.substring(line.indexOf(':') + 1)
val packageInfo = pm.getPackageInfo(packageName, flags)
result.add(packageInfo)
}
process.waitFor()
} catch (e: java.lang.Exception) {
e.printStackTrace()
}
return arrayListOf()
}
private fun getInstalledPackageByAlternative(context: Context): ArrayList<PackageInfo> {
val packageManager = context.getPackageManager()
val packageList = arrayListOf<PackageInfo>()
var packagesArray: Array<String>? = null
var uid = android.os.Process.FIRST_APPLICATION_UID
while (uid <= android.os.Process.LAST_APPLICATION_UID) {
try {
packagesArray = packageManager.getPackagesForUid(uid)
if (packagesArray != null && packagesArray.isNotEmpty()) {
for (packageName in packagesArray) {
try {
val packageInfo = packageManager.getPackageInfo(packageName, 0)
if (packageInfo == null) {
break
}
packageList.add(packageInfo)
} catch (e: PackageManager.NameNotFoundException) {
e.printStackTrace()
}
}
}
} catch (securityException: SecurityException) {
securityException.printStackTrace()
if (e is InterruptedException) {
Thread.currentThread().interrupt()
}
} finally {
if (bufferedReader != null) try {
bufferedReader.close()
} catch (e: IOException) {
e.printStackTrace()
}
uid++
}
return packageList
return result
}
}

View File

@ -6,6 +6,7 @@ import android.content.Context
import android.content.Intent
import android.net.Uri
import android.os.Build
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.FileProvider
import com.gh.common.dialog.InstallPermissionDialogFragment
@ -19,13 +20,13 @@ import com.gh.gamecenter.core.utils.CurrentActivityHolder
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.core.utils.ToastUtils
import com.gh.gamecenter.install.InstallService
import com.gh.gamecenter.vpn.VpnHelper
import com.gh.vspace.VHelper
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadEntity
import com.lightgame.utils.Utils
import java.io.File
// TODO 将弹窗改成以责任链模式来处理
object PackageInstaller {
/**
@ -107,12 +108,6 @@ object PackageInstaller {
return
}
val packageName = downloadEntity?.packageName ?: PackageUtils.getPackageNameByPath(context, pkgPath)
packageName?.let {
PackageChangeHelper.addInstallPendingPackage(packageName)
}
try {
// 判断是否需要使用浏览器来进行安装
if (BrowserInstallHelper.isUseBrowserToInstallEnabled()
@ -123,13 +118,13 @@ object PackageInstaller {
}
if (PackageUtils.isCanLaunchSetup(context, pkgPath)) {
installWithPureModeHandled(
context,
pkgPath,
downloadEntity?.gameId ?: "unknown",
downloadEntity?.name ?: "unknown",
downloadEntity?.categoryChinese ?: "unknown"
)
val currentActivity = CurrentActivityHolder.getCurrentActivity()
if (VpnHelper.shouldUseVpn() && currentActivity is AppCompatActivity) {
turnOnVpnThenInstall(currentActivity, pkgPath, downloadEntity)
} else {
install(context, pkgPath)
}
} else {
if (isPluggin) {
DialogHelper.showPluginDialog(
@ -159,21 +154,6 @@ object PackageInstaller {
}
}
/**
* 处理纯净模式后的安装
*/
private fun installWithPureModeHandled(
context: Context,
pkgPath: String,
gameId: String,
gameName: String,
gameType: String,
) {
PureModeHelper.handlePureModeIfNeeded(context, gameId, gameName, gameType) {
install(context, pkgPath)
}
}
/**
* 最终执行安装的方法
*/
@ -270,8 +250,6 @@ object PackageInstaller {
fun uninstallForPackageName(context: Context, pkn: String?) {
if (pkn.isNullOrEmpty()) return
PackageChangeHelper.addUninstallPendingPackage(pkn)
val uninstallIntent = Intent()
uninstallIntent.action = Intent.ACTION_DELETE
uninstallIntent.addCategory(Intent.CATEGORY_DEFAULT)
@ -305,4 +283,82 @@ object PackageInstaller {
return MD5Utils.getContentMD5(gameName + "_" + System.currentTimeMillis())
}
/**
* 启动 VPN 然后安装应用 (若没有授权会先提醒授权 VPN ,若拒绝授权会回落到直接执行安装)
*/
private fun turnOnVpnThenInstall(currentActivity: AppCompatActivity,
pkgPath: String,
downloadEntity: DownloadEntity?) {
if (VpnHelper.isVpnPermissionGranted(currentActivity) == true) {
VpnHelper.startVpn(currentActivity) { shouldShowVpnError ->
if (shouldShowVpnError) {
ToastUtils.toast("安装防护功能启动失败")
}
install(currentActivity, pkgPath)
}
} else {
val isTheFirstTimeToShowVpnHintDialog = VpnHelper.isTheFistTimeToShowVpnHintDialog()
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogShow(it.gameId, it.name)
}
// 将 VPN 提示弹窗标记为已读(已读后的下一次显示"不再提醒"按钮)
VpnHelper.setVpnHintDialogShowed()
if (!VpnHelper.shouldShowVpnHintDialog()) {
VpnHelper.startVpn(currentActivity) { shouldShowVpnError ->
if (shouldShowVpnError) {
ToastUtils.toast("安装防护功能启动失败")
}
install(currentActivity, pkgPath)
}
} else {
DialogHelper.showGuideDialog(
context = currentActivity,
title = "开启安装防护",
content = "建议您开启安装防护功能该功能有助于帮助您更快的完成安装避免因提示和置换导致的重复下载等问题安装防护功能需要获取您的VPN权限",
confirmText = "立即授权开启防护",
cancelText = "不再提醒",
confirmClickCallback = {
VpnHelper.ignoreVpnHintDialog()
VpnHelper.startVpn(currentActivity) { shouldShowVpnError ->
if (shouldShowVpnError) {
ToastUtils.toast("安装防护功能启动失败")
}
install(currentActivity, pkgPath)
}
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "立即授权")
}
},
cancelClickCallback = {
VpnHelper.ignoreVpnFunction()
install(currentActivity, pkgPath)
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "不再提醒")
}
},
extraConfig = DialogHelper.Config(
showCloseIcon = true,
showAlternativeCancelStyle = !isTheFirstTimeToShowVpnHintDialog
),
uiModificationCallback = { binding ->
binding.cancelTv.visibility = View.GONE
binding.closeContainer.setOnClickListener {
binding.markDismissByTouchInside()
install(currentActivity, pkgPath)
binding.dismiss()
downloadEntity?.let {
NewFlatLogUtils.logVpnHintDialogClick(it.gameId, it.name, "关闭按钮")
}
}
}
)
}
}
}
}

View File

@ -7,12 +7,14 @@ import android.content.pm.ApplicationInfo;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.PackageManager.NameNotFoundException;
import android.content.pm.PermissionInfo;
import android.content.pm.Signature;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.os.PowerManager;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
@ -27,6 +29,7 @@ import com.gh.common.xapk.XapkInstaller;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.PermissionHelper;
import com.gh.gamecenter.core.utils.MD5Utils;
import com.gh.gamecenter.core.utils.SentryHelper;
import com.gh.gamecenter.feature.entity.ApkEntity;
@ -45,10 +48,12 @@ import org.json.JSONArray;
import org.json.JSONException;
import org.json.JSONObject;
import java.io.BufferedReader;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.security.cert.CertificateException;
import java.security.cert.CertificateFactory;
import java.security.cert.X509Certificate;
@ -69,7 +74,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).applicationInfo.sourceDir;
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return null;
}
@ -307,7 +312,7 @@ public class PackageUtils {
return new String[]{null, null};
}
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return new String[]{null, null};
}
@ -587,7 +592,7 @@ public class PackageUtils {
.getPackageInfo(packageName, 0);
return packageInfo.firstInstallTime;
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return 0;
}
@ -614,7 +619,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager()
.getPackageInfo(BuildConfig.APPLICATION_ID, 0).lastUpdateTime;
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return 0;
@ -628,7 +633,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionName;
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return null;
}
@ -641,7 +646,7 @@ public class PackageUtils {
return HaloApp.getInstance().getApplication().getPackageManager().getPackageInfo(packageName,
PackageManager.COMPONENT_ENABLED_STATE_DEFAULT).versionCode;
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return 0;
}
@ -655,7 +660,7 @@ public class PackageUtils {
PackageManager packageManager = context.getApplicationContext().getPackageManager();
return packageManager.getApplicationIcon(packageName);
} catch (NameNotFoundException e) {
// do nothing
e.printStackTrace();
}
return null;
}

View File

@ -5,12 +5,12 @@ import android.os.Build;
import android.text.TextUtils;
import com.gh.gamecenter.R;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.retrofit.JSONObjectResponse;
import com.gh.gamecenter.common.retrofit.Response;
import com.gh.gamecenter.feature.entity.CommentEntity;
import com.gh.gamecenter.retrofit.RetrofitManager;
import com.lightgame.utils.Utils;
import com.walkud.rom.checker.RomIdentifier;
import org.json.JSONObject;
@ -37,7 +37,7 @@ public class PostCommentUtils {
device.put("model", Build.MODEL);
device.put("manufacturer", Build.MANUFACTURER);
device.put("android_version", android.os.Build.VERSION.RELEASE);
device.put("rom", MetaUtil.INSTANCE.getRom().name() + " " + MetaUtil.INSTANCE.getRom().getVersionName());
device.put("rom", RomIdentifier.getRom().name() + " " + RomIdentifier.getRom().getVersionName());
content.put("device", device);
} catch (Exception e) {
e.printStackTrace();

View File

@ -1,342 +0,0 @@
package com.gh.common.util
import android.app.Activity
import android.content.Context
import android.content.Intent
import android.provider.Settings
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gh.common.constant.Config
import com.gh.gamecenter.common.utils.DialogHelper
import com.gh.gamecenter.entity.NewApiSettingsEntity
import com.lightgame.utils.Utils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.enlargeTouchArea
import com.gh.gamecenter.common.utils.setDrawableEnd
import com.gh.gamecenter.core.utils.SPUtils
/**
* 处理厂商纯净/安全模式的辅助类
*/
object PureModeHelper {
private const val TAG = "PureModeHelper"
private const val HW = "HUAWEI"
private const val HW_PURE_MODE_STATE = "pure_mode_state"
private const val HW_PURE_ENHANCED_MODE_STATE = "pure_enhanced_mode_state"
private const val HW_PURE_MODE = "harmony_os_pure_mode"
private const val HW_PURE_ENHANCED_MODE = "harmony_os_pure_enhanced_mode"
private const val SP_USER_IGNORE_GUIDE = "user_ignore_guide"
private const val SWITCH_EXIST = 1
private const val SWITCH_ON = 0
private const val SWITCH_OFF = -1
private var isThisDeviceUsingEnhancedMode = false // 当前设备是否是增强纯净模式
fun handlePureModeIfNeeded(
context: Context,
gameId: String,
gameName: String,
gameType: String,
callback: () -> Unit
) {
// 用户忽略提醒,不再走下面的逻辑
if (SPUtils.getBoolean(SP_USER_IGNORE_GUIDE, false)) {
callback()
return
}
// 仅适用于华为手机
val manufacturer = android.os.Build.MANUFACTURER
if (manufacturer != HW) {
callback.invoke()
return
}
val solidContext = DialogHelper.checkDialogContext(context)
if (solidContext == null) {
callback.invoke()
return
}
if (readState(solidContext, HW_PURE_ENHANCED_MODE_STATE) == SWITCH_ON) {
Utils.log(TAG, "增强纯净模式开启!")
isThisDeviceUsingEnhancedMode = true
val pureModeGuide =
Config.getNewApiSettingsEntity()?.install?.guides?.firstOrNull { it.type == HW_PURE_ENHANCED_MODE }
if (pureModeGuide != null) {
showHWHintDialog(solidContext, pureModeGuide, gameId, gameName, gameType, true, callback)
} else {
callback.invoke()
}
} else if (readState(solidContext, HW_PURE_ENHANCED_MODE_STATE) != SWITCH_EXIST
&& readState(solidContext, HW_PURE_MODE_STATE) == SWITCH_ON
) {
Utils.log(TAG, "纯净模式开启!")
isThisDeviceUsingEnhancedMode = false
val pureModeGuide =
Config.getNewApiSettingsEntity()?.install?.guides?.firstOrNull { it.type == HW_PURE_MODE }
if (pureModeGuide != null) {
showHWHintDialog(solidContext, pureModeGuide, gameId, gameName, gameType, false, callback)
} else {
callback.invoke()
}
} else {
callback()
}
}
private fun showHWHintDialog(
context: Context,
guide: NewApiSettingsEntity.Guide,
gameId: String,
gameName: String,
gameType: String,
isEnhancedMode: Boolean,
callback: () -> Unit
) {
var isIgnored = false
if (context is Activity) {
if (context.isFinishing) {
callback.invoke()
return
}
}
trackDialogShow(isEnhancedMode, gameId, gameName, gameType)
DialogHelper.showGuideDialog(
context = context,
title = guide.title,
content = guide.content,
confirmText = guide.buttonTextSettingJump,
cancelText = guide.buttonTextClose,
uiModificationCallback = { binding ->
binding.hintTv.buildSpannableString {
addText(guide.linkTextPrefix)
addText(guide.linkText) {
setColor(ColorResId(R.color.text_theme))
onClick(false) {
binding.extraHintIv.performClick()
}
}
}
binding.extraHintIv.setImageResource(R.drawable.ic_home_head_arrow)
binding.extraHintIv.setOnClickListener {
directToLink(
context,
guide,
gameId,
gameName,
gameType,
isEnhancedMode,
isIgnored
)
}
binding.extraHintIv.enlargeTouchArea()
binding.extraHintIv.visibility = View.VISIBLE
binding.hintTv.visibility = View.VISIBLE
binding.selectorContainer.visibility = if (guide.dialogFrequency == "once") View.VISIBLE else View.GONE
binding.selectorContainer.setOnClickListener {
binding.selectorContainer.isChecked = !binding.selectorContainer.isChecked
isIgnored = binding.selectorContainer.isChecked
}
binding.confirmTv.setOnClickListener {
toHWPureModeSetting(context)
trackDialogClicked(
isEnhancedMode = isEnhancedMode,
gameId = gameId,
gameName = gameName,
gameType = gameType,
buttonName = guide.buttonTextSettingJump,
isIgnored = isIgnored
)
}
// 监听是否已授权,授权后自动调起安装并 dismiss dialog
if (context is AppCompatActivity) {
val lifecycle = context.lifecycle
lifecycle.addObserver(object : DefaultLifecycleObserver {
override fun onResume(owner: LifecycleOwner) {
val granted = (isThisDeviceUsingEnhancedMode && readState(context, HW_PURE_ENHANCED_MODE_STATE) != SWITCH_ON)
|| (!isThisDeviceUsingEnhancedMode && readState(context, HW_PURE_MODE_STATE) != SWITCH_ON)
if (granted) {
binding.dialog.dismiss()
callback.invoke()
lifecycle.removeObserver(this)
}
}
override fun onDestroy(owner: LifecycleOwner) {
super.onDestroy(owner)
lifecycle.removeObserver(this)
}
})
}
},
confirmClickCallback = {
// do nothing, 上面 setOnClickListener 处理了
},
cancelClickCallback = {
if (guide.dialogFrequency == "once" && isIgnored) {
SPUtils.setBoolean(SP_USER_IGNORE_GUIDE, true)
}
trackDialogClicked(
isEnhancedMode = isEnhancedMode,
gameId = gameId,
gameName = gameName,
gameType = gameType,
buttonName = guide.buttonTextClose,
isIgnored = isIgnored
)
callback()
},
touchOutsideCallback = {
if (guide.dialogFrequency == "once" && isIgnored) {
SPUtils.setBoolean(SP_USER_IGNORE_GUIDE, true)
}
trackDialogClicked(
isEnhancedMode = isEnhancedMode,
gameId = gameId,
gameName = gameName,
gameType = gameType,
buttonName = "关闭弹窗",
isIgnored = isIgnored
)
callback()
},
extraConfig = DialogHelper.Config(centerTitle = true)
)
}
private fun directToLink(
context: Context,
guide: NewApiSettingsEntity.Guide,
gameId: String,
gameName: String,
gameType: String,
isEnhancedMode: Boolean,
isIgnored: Boolean
) {
DirectUtils.directToLinkPage(context, guide.link, "纯净模式弹窗", "")
trackDialogClicked(
isEnhancedMode,
gameId,
gameName,
gameType,
"跳转链接",
isIgnored,
guide.link.link ?: "",
guide.link.type ?: "",
guide.link.text ?: ""
)
}
private fun trackDialogShow(
isEnhancedMode: Boolean,
gameId: String,
gameName: String,
gameType: String
) {
if (isEnhancedMode) {
SensorsBridge.trackAddedProtectionDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameType
)
} else {
SensorsBridge.trackPureModeDialogShow(
gameId = gameId,
gameName = gameName,
gameType = gameType
)
}
}
private fun trackDialogClicked(
isEnhancedMode: Boolean,
gameId: String,
gameName: String,
gameType: String,
buttonName: String,
isIgnored: Boolean,
linkId: String? = null,
linkType: String? = null,
linkText: String? = null
) {
if (isEnhancedMode) {
SensorsBridge.trackAddedProtectionDialogClick(
gameId = gameId,
gameName = gameName,
gameType = gameType,
buttonName = buttonName,
isIgnored = isIgnored,
linkId = linkId ?: "",
linkType = linkType ?: "",
linkText = linkText ?: ""
)
} else {
SensorsBridge.trackPureModeDialogClick(
gameId = gameId,
gameName = gameName,
gameType = gameType,
buttonName = buttonName,
isIgnored = isIgnored,
linkId = linkId ?: "",
linkType = linkType ?: "",
linkText = linkText ?: ""
)
}
}
private fun toHWPureModeSetting(context: Context) {
try {
val intent = Intent()
intent.setPackage("com.huawei.security.privacycenter")
intent.setAction("com.huawei.securitycenter.PURE_MODE_ACTIVITY")
intent.putExtra("intent_from_settings", true)
context.startActivity(intent)
} catch (_: Exception) {
toSystemSettings(context)
}
}
private fun toSystemSettings(context: Context) {
try {
val intent = Intent(Settings.ACTION_SETTINGS)
context.startActivity(intent)
} catch (e: Exception) {
e.printStackTrace()
}
}
private fun readState(context: Context, key: String): Int {
try {
val result = Settings.Secure.getInt(context.getContentResolver(), key, SWITCH_OFF)
Utils.log(TAG, "readState $key state >>> $result")
return result
} catch (e: Exception) {
e.printStackTrace()
}
return SWITCH_OFF
}
}

View File

@ -27,8 +27,8 @@ import com.gh.gamecenter.gamedetail.rating.RatingReplyActivity
object SyncDataBetweenPageHelper {
const val REQUEST_CODE_TAG = "REQUEST_CODE_TAG"
const val DATA_POSITION_TAG = "DATA_POSITION_TAG"
private const val REQUEST_CODE_TAG = "REQUEST_CODE_TAG"
private const val DATA_POSITION_TAG = "DATA_POSITION_TAG"
private const val DEFAULT_NUMBER = -1111
fun startActivityForResult(context: Context, intent: Intent, requestCode: Int, dataPosition: Int) {

View File

@ -62,8 +62,7 @@ object ViewPagerFragmentHelper {
const val TYPE_GAME_LIST = "game_list" // 游戏单广场
const val TYPE_FEEDBACK = "feedback" // 帮助与反馈
const val TYPE_COLUMN = "column" // 游戏专题详情页
const val TYPE_QQ_MINI_GAME_COLUMN = "qq_mini_game_column_detail" // QQ游戏专题详情页
const val TYPE_WECHAT_GAME_COLUMN = "wechat_game_column_detail" // 微信小游戏专题详情页
const val TYPE_QQ_MINI_GAME_COLUMN = "qq_mini_game_column_detail" // QQ游戏专题详情页
const val TYPE_COLUMN_COLLECTION = "column_collection" // 专题合集详情页
const val TYPE_SERVER = "server" // 开服表
const val TYPE_COLUMN_TEST = "column_test_v2" // 新游开测
@ -152,16 +151,11 @@ object ViewPagerFragmentHelper {
className = GameCollectionSquareFragment::class.java.name
}
// 游戏专题详情页/QQ游戏专题详情页
TYPE_COLUMN, TYPE_QQ_MINI_GAME_COLUMN, TYPE_WECHAT_GAME_COLUMN -> {
val subjectType = when(entity.type) {
TYPE_QQ_MINI_GAME_COLUMN -> SubjectData.SubjectType.QQ_GAME
TYPE_WECHAT_GAME_COLUMN -> SubjectData.SubjectType.WECHAT_GAME
else -> SubjectData.SubjectType.NORMAL
}
TYPE_COLUMN, TYPE_QQ_MINI_GAME_COLUMN -> {
className = SubjectFragment::class.java.name
bundle.putParcelable(
EntranceConsts.KEY_SUBJECT_DATA,
SubjectData(entity.link, entity.text, false, subjectType = subjectType)
SubjectData(entity.link, entity.text, false, isQQMiniGame = entity.type == "qq_mini_game_column_detail")
)
bundle.putBoolean(EntranceConsts.KEY_SHOW_DOWNLOAD_MENU, !isTabWrapper)
}

View File

@ -6,21 +6,25 @@ import android.view.LayoutInflater
import android.view.View
import android.widget.LinearLayout
import androidx.core.content.ContextCompat
import androidx.core.view.get
import com.facebook.drawee.drawable.ScalingUtils
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.util.DirectUtils
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.ImageUtils
import com.gh.gamecenter.common.utils.debounceActionWithInterval
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.common.utils.toResString
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.TopCutProcess
import com.gh.gamecenter.databinding.ItemCommunityImageBinding
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ImageInfo
import com.gh.gamecenter.feature.entity.CommunityVideoEntity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
class ImageContainerView : LinearLayout {
private var data: ImageContainerData? = null
private var mAnswerEntity: AnswerEntity? = null
//三图默认宽度
private var mDefaultWidth = 0f
@ -37,6 +41,9 @@ class ImageContainerView : LinearLayout {
//长图比例
private var mLongPictureRatio = 9 / 18f
private var mEntrance = ""
private var mPath = ""
//图片之间的间距
private val mItemSpace = 4f.dip2px()
private var mOffset = 0
@ -44,8 +51,6 @@ class ImageContainerView : LinearLayout {
private val imageViewList = arrayListOf<SimpleDraweeView>()
private var onImageContainerEventListener: OnImageContainerEventListener? = null
constructor(context: Context) : this(context, null)
constructor(context: Context, attrs: AttributeSet?) : this(context, attrs, 0)
constructor(context: Context, attrs: AttributeSet?, defStyleAttr: Int) : super(context, attrs, defStyleAttr) {
@ -70,34 +75,40 @@ class ImageContainerView : LinearLayout {
calculateWidth()
}
fun bindData(
data: ImageContainerData,
listener: OnImageContainerEventListener? = null
) {
this.data = data
onImageContainerEventListener = listener
fun bindData(entity: AnswerEntity, entrance: String = "", path: String = "", imageClick: (() -> Unit)? = null) {
imageViewList.clear()
removeAllViews()
if (entity.id != mAnswerEntity?.id) {
removeAllViews()
}
mAnswerEntity = entity
mEntrance = entrance
mPath = path
index = 0
if (!data.show) {
if ((entity.user.id == UserManager.getInstance().userId && entity.videos.isNotEmpty()) ||
(entity.user.id != UserManager.getInstance().userId && entity.getPassVideos().isNotEmpty()) ||
entity.images.isNullOrEmpty()
) {
visibility = View.GONE
return
}
visibility = View.VISIBLE
if (data.isPostCard) {
if (mAnswerEntity?.type == "community_article") {
//若文章内容含有图片及视频,则信息流卡片,仅展示图片,且标题后带有‘有视频’标签
//若文章内容仅含有图片,则信息流卡片,仅展示图片,无标签
//若文章内容仅含有视频,则信息流卡片,仅展示视频,无标签
when {
data.images.isNotEmpty() -> {
data.images.take(3)
.forEach {
bindImage(it.url, it.width, it.height, data.images.size == 1)
}
entity.images.isNotEmpty() -> {
val imagesInfo = entity.imagesInfo
val images = entity.images.take(3)
images.forEachIndexed { index, url ->
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
bindImage(url, width, height, images.size == 1, imageClick)
}
}
data.video != null -> {
val video = data.video
entity.getPassVideos().isNotEmpty() -> {
val video = entity.getPassVideos()[0]
bindVideo(video, video.width, video.height, true)
}
@ -109,22 +120,28 @@ class ImageContainerView : LinearLayout {
//若问答内容含有图片及视频,则信息流卡片,同时展示图片及视频,且参考以往排序逻辑(视频优先放置第一位),无标签
//若问答内容仅含有图片,则信息流卡片,仅展示图片,无标签
//若问答内容仅含有视频,则信息流卡片,仅展示视频,无标签
if (data.video != null) {
val video = data.video
bindVideo(video, video.width, video.height, data.images.isNullOrEmpty())
data.images.take(2).forEach {
bindImage(it.url, it.width, it.height, false)
if (entity.getPassVideos().isNotEmpty()) {
val video = entity.getPassVideos()[0]
bindVideo(video, video.width, video.height, mAnswerEntity?.images.isNullOrEmpty())
entity.images.take(2).forEachIndexed { index, url ->
val imagesInfo = entity.imagesInfo
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
bindImage(url, width, height, false, imageClick)
}
} else {
data.images.take(3)
.forEach {
bindImage(it.url, it.width, it.height, data.images.size == 1)
}
val images = entity.images.take(3)
images.forEachIndexed { index, url ->
val imagesInfo = entity.imagesInfo
val width = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].width else 0
val height = if (index <= entity.imagesInfo.size - 1) imagesInfo[index].height else 0
bindImage(url, width, height, images.size == 1, imageClick)
}
}
}
}
private fun bindVideo(video: ImageContainerData.VideoInfo, width: Int, height: Int, isChangeRatio: Boolean) {
private fun bindVideo(video: CommunityVideoEntity, width: Int, height: Int, isChangeRatio: Boolean) {
val oldView = if (childCount == 0 || index >= childCount) null else getChildAt(index)
val binding = if (oldView != null) {
ItemCommunityImageBinding.bind(oldView)
@ -141,7 +158,16 @@ class ImageContainerView : LinearLayout {
displayImage(binding, video.poster, width.toFloat(), height.toFloat(), isChangeRatio, true)
binding.root.setOnClickListener {
debounceActionWithInterval(it.id, 1000) {
onImageContainerEventListener?.onVideoCLick(video.id)
if (mAnswerEntity == null) return@debounceActionWithInterval
val videoEntity = mAnswerEntity!!.getPassVideos().firstOrNull()
DirectUtils.directToVideoDetail(
context,
videoEntity?.id ?: "",
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
showComment = false,
entrance = mEntrance,
path = mPath
)
}
}
index++
@ -151,7 +177,8 @@ class ImageContainerView : LinearLayout {
url: String,
width: Int,
height: Int,
isChangeRatio: Boolean
isChangeRatio: Boolean,
imageClick: (() -> Unit)?
) {
val oldView = if (childCount == 0 || index >= childCount) null else getChildAt(index)
val binding = if (oldView != null) {
@ -168,21 +195,25 @@ class ImageContainerView : LinearLayout {
binding.videoPlay.visibility = View.GONE
displayImage(binding, url, width.toFloat(), height.toFloat(), isChangeRatio)
binding.root.setOnClickListener {
if (data?.status == "pending" || data?.status == "fail") return@setOnClickListener
if (mAnswerEntity?.status == "pending" || mAnswerEntity?.status == "fail") return@setOnClickListener
imageClick?.invoke()
debounceActionWithInterval(it.id, 1000) {
data?.run {
val position = if (isPostCard) {
binding.root.tag as Int
} else {
if (video == null) binding.root.tag as Int else (binding.root.tag as Int) - 1
}
onImageContainerEventListener?.onImageClick(
images.map(ImageContainerData.ImageInfo::url),
position,
imageViewList
)
if (mAnswerEntity == null) return@debounceActionWithInterval
val position = if (mAnswerEntity?.type == "community_article") {
binding.root.tag as Int
} else {
if (mAnswerEntity!!.getPassVideos()
.isNullOrEmpty()
) binding.root.tag as Int else (binding.root.tag as Int) - 1
}
if (mAnswerEntity?.communityId.isNullOrEmpty()) {
mAnswerEntity?.communityId = mAnswerEntity?.bbs?.id
}
val intent = ImageViewerActivity.getIntent(
context, mAnswerEntity!!.images as ArrayList<String>, position, imageViewList,
if (mAnswerEntity?.type == "community_article") mAnswerEntity else null, mEntrance, true
)
context.startActivity(intent)
}
}
index++
@ -248,7 +279,7 @@ class ImageContainerView : LinearLayout {
}
binding.pendingView.run {
when (data?.status) {
when (mAnswerEntity?.status) {
"pending" -> {
visibility = View.VISIBLE
text = R.string.pending_status.toResString()
@ -266,7 +297,7 @@ class ImageContainerView : LinearLayout {
}
val imageCount = data?.images?.size ?: 0
val imageCount = mAnswerEntity?.images?.size ?: 0
if (!isVideo && index == 2 && imageCount > 3) {
binding.labelIcon.visibility = View.GONE
binding.durationOrNumTv.visibility = View.VISIBLE
@ -277,80 +308,4 @@ class ImageContainerView : LinearLayout {
if (index != 0) params.leftMargin = mItemSpace
binding.root.layoutParams = params
}
companion object {
private const val COMMUNITY_ARTICLE = "community_article"
fun AnswerEntity.toImageContainerData(): ImageContainerData {
val imageInfoList = arrayListOf<ImageContainerData.ImageInfo>()
images.forEachIndexed { index, url ->
if (index < 3) {
imageInfoList.add(ImageContainerData.ImageInfo(url))
}
}
imageInfoList.forEachIndexed { index, imageInfo ->
val item = imagesInfo.getOrNull(index)
if (item != null) {
imageInfo.width = item.width
imageInfo.height = item.height
}
}
val video =
getPassVideos().firstOrNull()?.let {
ImageContainerData.VideoInfo(
it.id,
it.duration,
it.poster,
it.width,
it.height
)
}
val show = !((user.id == UserManager.getInstance().userId && videos.isNotEmpty())
|| (user.id != UserManager.getInstance().userId && getPassVideos().isNotEmpty())
|| images.isNullOrEmpty())
return ImageContainerData(
status = status,
isPostCard = type == COMMUNITY_ARTICLE,
images = imageInfoList,
video = video,
show
)
}
}
data class ImageContainerData(
val status: String,
val isPostCard: Boolean, // 是否是帖子卡片
val images: List<ImageInfo>,
val video: VideoInfo? = null,
val show: Boolean
) {
data class ImageInfo(
val url: String,
var width: Int = 0,
var height: Int = 0
)
data class VideoInfo(
val id: String,
val duration: String,
val poster: String,
var width: Int = 0,
var height: Int = 0,
)
}
interface OnImageContainerEventListener {
fun onImageClick(
images: List<String>,
position: Int,
imageViewList: ArrayList<SimpleDraweeView>
)
fun onVideoCLick(videoId: String)
}
}

View File

@ -6,12 +6,8 @@ import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Build
import android.text.SpannableStringBuilder
import android.view.View
import androidx.appcompat.app.AppCompatActivity
import androidx.constraintlayout.widget.ConstraintLayout
import androidx.core.text.underline
import androidx.core.view.updateLayoutParams
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.R
import com.gh.gamecenter.common.activityresult.ActResultRequest
@ -41,14 +37,6 @@ object XapkDialogHelper {
val previousShowedDialog = mUnzipFailureDialogRef?.get()
val useRebootStyle = Build.VERSION.SDK_INT >= Build.VERSION_CODES.S
val content = if (useRebootStyle) {
"未授权允许未知来源安装、数据包格式、设备兼容性…等均可能导致解压失败。"
} else {
"未授权允许未知来源安装、数据包格式、设备兼容性…等均可能导致解压失败。\n如果开启权限后仍未能解决,请提交反馈帮助我们改进。"
}
if (previousShowedDialog != null
&& previousShowedDialog.isShowing
&& context == previousShowedDialog.ownerActivity
@ -60,9 +48,9 @@ object XapkDialogHelper {
val dialog = DialogHelper.showGuideDialog(
context = context,
title = "",
content = content,
content = "未授权允许未知来源安装、数据包格式、设备兼容性…等均可能导致解压失败。\n如果开启权限后仍未能解决,请提交反馈帮助我们改进。",
confirmText = "开启权限",
cancelText = if (useRebootStyle) "重启助手" else "提交反馈",
cancelText = "提交反馈",
confirmClickCallback = {
if (context is AppCompatActivity) {
val intent = PermissionHelper.getToInstallPermissionSettingIntent(context)
@ -95,7 +83,11 @@ object XapkDialogHelper {
)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
restart(context)
val pm = context.packageManager
val restartIntent = pm.getLaunchIntentForPackage(context.packageName)
val mainIntent = Intent.makeRestartActivityTask(restartIntent!!.component)
context.startActivity(mainIntent)
Runtime.getRuntime().exit(0)
} else {
XapkInstaller.install(context, downloadEntity, true)
}
@ -108,22 +100,27 @@ object XapkDialogHelper {
}
},
cancelClickCallback = {
if (useRebootStyle) {
// 记录应用重启前需要重解压的信息
SPUtils.setString(Constants.SP_XAPK_UNZIP_ACTIVITY, context.javaClass.name)
SPUtils.setString(Constants.SP_XAPK_URL, downloadEntity.url)
val hint = "游戏安装包解压失败,问题反馈:"
HelpAndFeedbackBridge.startSuggestionActivity(
context,
SuggestType.GAME,
null,
hint,
SimpleGameEntity(downloadEntity.gameId, downloadEntity.name, downloadEntity.icon)
)
NewFlatLogUtils.logXApkUnzipFailedDialogClick(
"提交反馈",
false,
downloadEntity.gameId,
downloadEntity.name
)
SensorsBridge.trackGameDecompressionFailedDialogClick(
buttonName = "重启助手",
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.categoryChinese
)
restart(context)
} else {
doFeedback(context, downloadEntity)
}
SensorsBridge.trackGameDecompressionFailedDialogClick(
buttonName = "提交反馈",
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.categoryChinese
)
},
uiModificationCallback = { binding ->
binding.headIv.setBackgroundResource(R.drawable.dialog_unzip_failure_head_background)
@ -149,30 +146,6 @@ object XapkDialogHelper {
)
binding.dismiss()
}
if (useRebootStyle) {
val spannableString = SpannableStringBuilder()
.append("未能解决问题?点击 ")
.underline { append("提交反馈") }
.append(" ")
binding.alternativeCancelTv.visibility = View.VISIBLE
binding.alternativeCancelTv.text = spannableString
binding.alternativeCancelTv.setTextColor(R.color.text_secondary.toColor(context))
binding.alternativeCancelTv.setDrawableEnd(R.drawable.ic_right_arrow_xapk)
binding.alternativeCancelTv.setOnClickListener {
doFeedback(context, downloadEntity)
}
binding.hintTv.text = "开启权限后请务必重启光环助手,再进行安装"
binding.hintTv.setTextColor(R.color.text_theme.toColor(context))
binding.hintTv.setTextAppearance(R.style.TextCaption1)
binding.hintTv.visibility = View.VISIBLE
binding.hintTv.updateLayoutParams<ConstraintLayout.LayoutParams> {
topMargin = 8F.dip2px()
}
binding.confirmTv.updateLayoutParams<ConstraintLayout.LayoutParams> {
topMargin = 16F.dip2px()
}
}
},
touchOutsideCallback = {
SensorsBridge.trackGameDecompressionFailedDialogClick(
@ -191,37 +164,4 @@ object XapkDialogHelper {
mUnzipFailureDialogRef = WeakReference(dialog)
}
private fun restart(context: Context) {
val pm = context.packageManager
val restartIntent = pm.getLaunchIntentForPackage(context.packageName)
val mainIntent = Intent.makeRestartActivityTask(restartIntent!!.component)
context.startActivity(mainIntent)
Runtime.getRuntime().exit(0)
}
private fun doFeedback(context: Context, downloadEntity: DownloadEntity) {
val hint = "游戏安装包解压失败,问题反馈:"
HelpAndFeedbackBridge.startSuggestionActivity(
context,
SuggestType.GAME,
null,
hint,
SimpleGameEntity(downloadEntity.gameId, downloadEntity.name, downloadEntity.icon)
)
NewFlatLogUtils.logXApkUnzipFailedDialogClick(
"提交反馈",
false,
downloadEntity.gameId,
downloadEntity.name
)
SensorsBridge.trackGameDecompressionFailedDialogClick(
buttonName = "提交反馈",
downloadEntity.gameId,
downloadEntity.name,
downloadEntity.categoryChinese
)
}
}

View File

@ -10,8 +10,8 @@ import com.gh.gamecenter.common.exposure.meta.MetaUtil
import com.gh.gamecenter.common.exposure.meta.MetaUtil.getMeta
import com.gh.gamecenter.common.loghub.LoghubUtils
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.SentryHelper
import com.gh.ndownload.NDataChanger
import com.gh.ndownload.NDownloadBridge
import com.halo.assistant.HaloApp
import com.lightgame.download.DownloadConfig
import com.lightgame.download.DownloadEntity
@ -124,7 +124,6 @@ object DownloadDataHelper {
val payloadObject = JSONObject()
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
@ -213,7 +212,6 @@ object DownloadDataHelper {
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
@ -253,7 +251,6 @@ object DownloadDataHelper {
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
@ -318,7 +315,6 @@ object DownloadDataHelper {
payloadObject.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
payloadObject.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
payloadObject.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
payloadObject.put("game_id", downloadEntity.gameId)
payloadObject.put("gameName", downloadEntity.name)
payloadObject.put("platform", downloadEntity.platform)
@ -361,7 +357,6 @@ object DownloadDataHelper {
sheet = JSONObject()
sheet.put("host", downloadEntity.meta[DownloadEntity.DOWNLOAD_HOST_KEY] ?: "unknown")
sheet.put("path", downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown")
sheet.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
sheet.put("game_id", downloadEntity.gameId)
sheet.put("platform", downloadEntity.platform)
sheet.put("package", downloadEntity.packageName)
@ -381,7 +376,6 @@ object DownloadDataHelper {
"path",
downloadEntity.meta[DownloadEntity.DOWNLOAD_PATH_KEY] ?: "unknown"
) // 初始化记录的 path 为空
sheet.put("redirected_host_list", downloadEntity.meta[NDownloadBridge.REDIRECTED_HOST_LIST] ?: "unknown")
sheet.put("total_size", downloadEntity.size / 1024 / 1024) // 初始化记录的 total_size 有可能为0
sheet.put("progress_size", downloadEntity.progress / 1024 - progressSize)
sheet.put("current_progress_size", downloadEntity.progress / 1024)

View File

@ -22,6 +22,7 @@ import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.feature.entity.TagStyleEntity;
import com.gh.gamecenter.feature.entity.CustomPageTrackData;
import com.gh.gamecenter.feature.entity.TagStyleEntity;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.common.exposure.ExposureUtils;
import com.gh.common.history.HistoryHelper;
@ -33,10 +34,16 @@ import com.gh.common.util.LunchType;
import com.gh.common.util.PackageInstaller;
import com.gh.common.util.PackageUtils;
import com.gh.gamecenter.BuildConfig;
import com.gh.gamecenter.common.base.GlobalActivityManager;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.exposure.meta.MetaUtil;
import com.gh.gamecenter.common.utils.DeviceUtils;
import com.gh.gamecenter.common.utils.ExtensionsKt;
import com.gh.gamecenter.common.utils.FileUtils;
import com.gh.gamecenter.common.utils.NetworkUtils;
import com.gh.gamecenter.common.utils.SensorsBridge;
import com.gh.gamecenter.core.AppExecutor;
import com.gh.gamecenter.core.utils.AppDebugConfig;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.PageSwitchDataHelper;
import com.gh.gamecenter.core.utils.SPUtils;
@ -48,6 +55,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus;
import com.gh.gamecenter.feature.entity.ApkEntity;
import com.gh.gamecenter.feature.entity.GameEntity;
import com.gh.gamecenter.feature.entity.PluginLocation;
import com.gh.gamecenter.feature.exposure.ExposureEvent;
import com.gh.gamecenter.login.user.UserManager;
import com.gh.gamecenter.manager.PackagesManager;
import com.gh.gamecenter.packagehelper.PackageRepository;
@ -56,6 +64,7 @@ import com.gh.ndownload.NDownloadBridge;
import com.gh.ndownload.NDownloadService;
import com.gh.vspace.VHelper;
import com.halo.assistant.HaloApp;
import com.lightgame.download.ConnectionUtils;
import com.lightgame.download.DataWatcher;
import com.lightgame.download.DownloadConfig;
import com.lightgame.download.DownloadDao;
@ -170,6 +179,9 @@ public class DownloadManager implements DownloadStatusListener {
mUpdateMarks = SPUtils.getStringSet(UPDATE_IS_READ_MARK);
// 只有下载模块需要这坨东西,因此移动到这里初始化
ConnectionUtils.initHttpsUrlConnection(mContext);
updateDownloadMetaMap();
lastTimeMap = new ArrayMap<>();

View File

@ -14,13 +14,12 @@ import com.gh.gamecenter.common.retrofit.EmptyResponse
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.utils.NewFlatLogUtils
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.runOnIoThread
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.core.utils.ThirdPartyPackageHelper
import com.gh.gamecenter.core.utils.UrlFilterUtils
import com.gh.gamecenter.entity.GameDigestEntity
import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.utils.ConcernUtils
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.manager.PackagesManager
@ -56,7 +55,7 @@ object PackageObserver {
}
@JvmStatic
fun onPackageChanged(busFour: EBPackage, cachedGameEntity: GameEntity? = null) {
fun onPackageChanged(busFour: EBPackage) {
val application = HaloApp.getInstance().application
val packageName = busFour.packageName
val versionName = busFour.versionName
@ -100,7 +99,7 @@ object PackageObserver {
if (EBPackage.TYPE_INSTALLED == busFour.type) {
if (!busFour.isVGame) {
// 非畅玩游戏才执行下面的代码
mPackageViewModel.addInstalledGame(packageName, cachedGameEntity)
mPackageViewModel.addInstalledGame(packageName)
BrowserInstallHelper.onApkInstalled(mDownloadEntity?.path)
}
@ -154,7 +153,7 @@ object PackageObserver {
}
}
AppExecutor.logExecutor.execute { postNewlyInstalledApp(gameId, packageName) }
runOnIoThread { postNewlyInstalledApp(gameId, packageName) }
}
if (EBPackage.TYPE_UNINSTALLED == busFour.type) {

View File

@ -24,7 +24,6 @@ import com.gh.gamecenter.common.base.fragment.BaseDraggableDialogFragment
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.goneIf
import com.gh.gamecenter.common.utils.observeNonNull
import com.gh.gamecenter.common.utils.safelyGetInRelease
import com.gh.gamecenter.common.utils.throwExceptionInDebug
import com.gh.gamecenter.core.AppExecutor
import com.gh.gamecenter.core.utils.TimeElapsedHelper
@ -182,7 +181,7 @@ class DownloadDialog : BaseDraggableDialogFragment() {
AppExecutor.uiExecutor.executeWithDelay({
recyclerView.adapter?.let {
for (i in 0 until it.itemCount) {
val apkEntity = itemList.safelyGetInRelease(i)?.normal ?: continue
val apkEntity = itemList[i].normal ?: continue
val apkCollection = apkEntity.apkCollection
val platformName = platformList[0].name
val packageName = platformList[0].packageName

View File

@ -10,9 +10,6 @@ import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.gh.gamecenter.entity.SubjectRecommendEntity
import com.gh.gamecenter.game.GameFragment
/**
* 板块
*/
class BlockActivity : DownloadToolbarActivity() {
companion object {

View File

@ -14,7 +14,6 @@ import com.halo.assistant.fragment.ApkCleanerFragment;
/**
* Created by khy on 2017/1/24.
* 清理安装包
*/
@Route(path = RouteConsts.activity.cleanApkActivity)
public class CleanApkActivity extends ToolBarActivity {

View File

@ -16,7 +16,6 @@ import java.util.ArrayList;
/**
* Created by khy on 18/07/17.
* 我的收藏
*/
public class CollectionActivity extends ToolBarActivity {
@Override

View File

@ -23,9 +23,6 @@ import java.lang.ref.SoftReference;
import androidx.annotation.LayoutRes;
import androidx.annotation.NonNull;
/**
* 裁剪图片
*/
public class CropImageActivity extends ToolBarActivity {
protected CropImageCustom mCropImageCustom;

View File

@ -24,7 +24,7 @@ import com.halo.assistant.HaloApp
/**
* Created by khy on 2017/3/24.
* 游戏详情
* 游戏详情适配器
*/
class GameDetailActivity : DownloadToolbarActivity() {

View File

@ -16,7 +16,6 @@ import java.util.ArrayList;
/**
* Created by khy on 2016/12/12.
* 礼包中心
*/
@Deprecated
public class LibaoActivity extends ToolBarActivity {

View File

@ -285,6 +285,9 @@ public class MainActivity extends BaseActivity {
// 耗时操作
AppExecutor.getIoExecutor().execute(() -> {
// 上传数据
DataCollectionManager.getInstance().upload();
// 初始化PlatformUtils
PlatformUtils.getInstance(getApplicationContext());
@ -553,10 +556,7 @@ public class MainActivity extends BaseActivity {
} else {
TextView jumpBtn = findViewById(R.id.jumpBtn);
jumpBtn.setText(String.format(Locale.CHINA, "跳过 %d", COUNTDOWN_MAX_COUNT - mCountdownCount));
Message newMsg = Message.obtain();
newMsg.what = COUNTDOWN_AD;
newMsg.obj = msg.obj;
mBaseHandler.sendMessageDelayed(newMsg, 1000);
mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_AD, 1000);
}
}
}
@ -877,6 +877,13 @@ public class MainActivity extends BaseActivity {
return true;
}
@Override
public void finish() {
// 上传数据
DataCollectionManager.getInstance().statClickData();
super.finish();
}
@Override
protected void onSaveInstanceState(@NotNull Bundle outState) {
super.onSaveInstanceState(outState);
@ -922,7 +929,7 @@ public class MainActivity extends BaseActivity {
Config.refreshVSettingEntity();
}
// mPackageViewModel.checkData();
mPackageViewModel.checkData();
deleteSimulatorGame();
}
}

View File

@ -17,7 +17,6 @@ import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.db.SearchHistoryDao
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.search.SearchDefaultFragment
@ -30,9 +29,6 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.util.concurrent.TimeUnit
/**
* 游戏搜索页
*/
open class SearchActivity : BaseActivity() {
lateinit var searchEt: EditText
@ -40,7 +36,7 @@ open class SearchActivity : BaseActivity() {
lateinit var backBtn: RelativeLayout
private lateinit var deleteIv: ImageView
protected val mDao: ISearchHistoryDao by lazy { provideDao() }
private var mDao: SearchHistoryDao? = null
protected var mSearchKey: String? = null
protected var mIsAutoSearchDisabled: Boolean = false
@ -79,6 +75,7 @@ open class SearchActivity : BaseActivity() {
val searchImmediately = intent.getBooleanExtra(KEY_SEARCH_IMMEDIATELY, false)
var ignoreTextChanges = savedInstanceState != null
mDao = SearchHistoryDao(this)
mPublishSubject = PublishSubject.create()
mPublishSubject!!
@ -102,7 +99,7 @@ open class SearchActivity : BaseActivity() {
searchEt.hint = hint
if (searchImmediately) {
mDisplayType = GAME_DETAIL
mDao.add(hint)
mDao?.add(hint)
search(SearchType.DEFAULT, hint)
}
} else {
@ -190,12 +187,12 @@ open class SearchActivity : BaseActivity() {
mIsAutoSearchDisabled = false
}
protected open fun handleAutoSearch(key: String?) {
private fun handleAutoSearch(key: String?) {
val newSearchKey = searchEt.text.toString().trim { it <= ' ' }
if (newSearchKey.isEmpty()) {
val hint = searchEt.hint.toString()
if (!TextUtils.isEmpty(hint) && HINT_TEXT != hint) {
mDao.add(hint)
mDao?.add(hint)
search(SearchType.DEFAULT, hint)
}
} else {
@ -213,7 +210,7 @@ open class SearchActivity : BaseActivity() {
}
}
protected open fun handleDefaultSearch(key: String?) {
private fun handleDefaultSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
@ -230,14 +227,14 @@ open class SearchActivity : BaseActivity() {
// MtaHelper.onEvent("游戏搜索", "默认搜索", key)
}
protected open fun handleHotSearch(key: String?) {
private fun handleHotSearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
updateDisplayType(GAME_DETAIL)
}
protected open fun handleHistorySearch(key: String?) {
private fun handleHistorySearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
@ -254,12 +251,12 @@ open class SearchActivity : BaseActivity() {
// MtaHelper.onEvent("游戏搜索", "历史搜索", key)
}
protected open fun handleManualSearch() {
private fun handleManualSearch() {
val newSearchKey = searchEt.text.toString().trim { it <= ' ' }
if (newSearchKey.isEmpty()) {
val hint = searchEt.hint.toString()
if (!TextUtils.isEmpty(hint) && HINT_TEXT != hint) {
mDao.add(hint)
mDao?.add(hint)
search(SearchType.DEFAULT, hint)
}
} else if (newSearchKey != mSearchKey || mDisplayType != GAME_DETAIL) {
@ -273,7 +270,7 @@ open class SearchActivity : BaseActivity() {
mSourceEntrance
)
mDao.add(mSearchKey)
mDao?.add(mSearchKey)
updateDisplayType(GAME_DETAIL)
} else {
toast("请输入搜索内容")
@ -283,8 +280,6 @@ open class SearchActivity : BaseActivity() {
// MtaHelper.onEvent("游戏搜索", "主动搜索", newSearchKey)
}
protected open fun provideDao(): ISearchHistoryDao = SearchHistoryDao(this)
open fun updateDisplayType(type: DisplayType) {
val transaction = supportFragmentManager.beginTransaction()
when (type) {

View File

@ -48,7 +48,6 @@ import io.reactivex.functions.Consumer;
/**
* Created by khy on 2016/11/7.
* 分享卡片
*/
public class ShareCardPicActivity extends ToolBarActivity {

View File

@ -21,7 +21,6 @@ import com.tencent.tauth.Tencent;
/**
* Created by khy on 2017/2/6.
* 分享光环
*/
public class ShareGhActivity extends ToolBarActivity {

View File

@ -55,18 +55,15 @@ import com.gh.common.util.CheckLoginUtils;
import com.gh.common.util.DirectUtils;
import com.gh.common.util.EntranceUtils;
import com.gh.gamecenter.common.base.activity.BaseActivity;
import com.gh.gamecenter.common.constant.Constants;
import com.gh.gamecenter.common.constant.EntranceConsts;
import com.gh.gamecenter.common.entity.CommunityEntity;
import com.gh.gamecenter.common.entity.LinkEntity;
import com.gh.gamecenter.common.entity.SimpleGameEntity;
import com.gh.gamecenter.core.utils.GsonUtils;
import com.gh.gamecenter.core.utils.ToastUtils;
import com.gh.gamecenter.entity.SubjectData;
import com.gh.gamecenter.entity.SubjectRecommendEntity;
import com.gh.gamecenter.entity.VideoLinkEntity;
import com.gh.gamecenter.feature.utils.PlatformUtils;
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper;
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel;
import com.gh.gamecenter.video.videomanager.VideoManagerActivity;
import com.gh.vspace.shortcut.OnCreateShortcutResult;
@ -155,7 +152,7 @@ public class SkipActivity extends BaseActivity {
DirectUtils.directToGameDetail(this, path, "", entrance, "true".equals(uri.getQueryParameter("auto_download")), to, null);
break;
case HOST_COLUMN:
DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), entrance, null, SubjectData.SubjectType.NORMAL);
DirectUtils.directToSubject(this, path, uri.getQueryParameter(KEY_NAME), entrance, null, false);
break;
case HOST_SUGGESTION:
if (!TextUtils.isEmpty(qaId)) {
@ -404,7 +401,7 @@ public class SkipActivity extends BaseActivity {
try {
JSONObject extJsonObject = new JSONObject(extJson);
String qqGameId = extJsonObject.optString("aid");
MiniGameItemHelper.INSTANCE.launchMiniGame(qqGameId, Constants.QQ_MINI_GAME);
DirectUtils.directToQGameById(this, qqGameId);
} catch (JSONException ignored) {
}
break;

View File

@ -115,7 +115,6 @@ class SplashScreenActivity : BaseActivity() {
)
}
} else {
PackageHelper.checkIfGetInstalledApiSwitchShouldBeIgnored(this)
cancelPreviousUpdateTask()
launchMainActivity()
}
@ -168,12 +167,10 @@ class SplashScreenActivity : BaseActivity() {
if (isMalfunctioningHonorDevice) {
showHonorNotification()
mBaseHandler.postDelayed({
launchMainActivity()
// requestGetInstallListPermissionAndLaunchMainActivity()
requestGetInstallListPermissionAndLaunchMainActivity()
}, 100L)
} else {
launchMainActivity()
// requestGetInstallListPermissionAndLaunchMainActivity()
requestGetInstallListPermissionAndLaunchMainActivity()
}
} else {
DialogUtils.showPrivacyPolicyDisallowDialog(this) {
@ -199,17 +196,9 @@ class SplashScreenActivity : BaseActivity() {
}
// 尝试获取安装应用列表权限并启动首页(不在乎结果)
// private fun requestGetInstallListPermissionAndLaunchMainActivity() {
// if (PackageHelper.isSupportGetInstalledAppsPermission(this)
// && PermissionHelper.isGetInstalledListPermissionDisabled(this)
// ) {
// PermissionHelper.requestGetInstalledAppsListPermission(this, true) {
// launchMainActivity()
// }
// } else {
// launchMainActivity()
// }
// }
private fun requestGetInstallListPermissionAndLaunchMainActivity() {
launchMainActivity()
}
// 删除更新后的光环助手包
private fun cancelPreviousUpdateTask() {

View File

@ -7,9 +7,6 @@ import com.gh.gamecenter.common.base.activity.ToolBarActivity
import com.gh.gamecenter.common.utils.updateStatusBarColor
import com.halo.assistant.fragment.user.UserInfoFragment
/**
* 编辑资料
*/
class UserInfoActivity : ToolBarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -8,9 +8,6 @@ import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.login.user.UserViewModel
import com.halo.assistant.fragment.user.UserInfoEditFragment
/**
* 修改个人信息
*/
class UserInfoEditActivity : ToolBarActivity() {
companion object {
fun getIntent(context: Context, editType: String): Intent {

View File

@ -8,7 +8,6 @@ import com.halo.assistant.fragment.user.SelectRegionFragment;
/**
* Created by khy on 25/09/17.
* 选择地区
*/
public class UserRegionActivity extends ToolBarActivity {

View File

@ -45,7 +45,7 @@ import kotlin.math.abs
class AmwayFragment : LazyListFragment<AmwayListItemData, AmwayViewModel>() {
private val mViewModel: AmwayViewModel by lazy { viewModelProvider() }
private lateinit var mViewModel: AmwayViewModel
private val mElapsedHelper by lazy { TimeElapsedHelper() }
private lateinit var mExposureListener: ExposureListener
@ -131,13 +131,14 @@ class AmwayFragment : LazyListFragment<AmwayListItemData, AmwayViewModel>() {
}
}
override fun getItemDecoration() = VerticalItemDecoration(context, 12F, false)
.apply { mItemDecoration = this }
override fun provideListViewModel(): AmwayViewModel? {
override fun provideListViewModel(): AmwayViewModel {
mViewModel = viewModelProvider()
return mViewModel
}
override fun getItemDecoration() = VerticalItemDecoration(context, 12F, false)
.apply { mItemDecoration = this }
override fun provideListAdapter(): ListAdapter<*> {
if (mAdapter == null) {
val basicExposureSource = arrayListOf<ExposureSource>().apply {

View File

@ -9,12 +9,12 @@ import com.gh.gamecenter.DisplayType
import com.gh.gamecenter.R
import com.gh.gamecenter.SearchActivity
import com.gh.gamecenter.SearchType
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.search.SearchDefaultFragment
class AmwaySearchActivity : SearchActivity() {
private lateinit var mViewModel: AmwaySearchViewModel
private val mSearchHistoryDao by lazy { AmwaySearchDao() }
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
@ -22,43 +22,38 @@ class AmwaySearchActivity : SearchActivity() {
mViewModel = viewModelProvider()
}
override fun provideDao(): ISearchHistoryDao = AmwaySearchDao()
override fun handleAutoSearch(key: String?) {
mSearchKey = key
updateDisplayType(DisplayType.GAME_DIGEST)
mViewModel.getSearchResult(mSearchKey!!)
}
override fun handleHistorySearch(key: String?) {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
mViewModel.getSearchResult(mSearchKey!!)
updateDisplayType(DisplayType.GAME_DETAIL)
}
private fun handleOtherSearch() {
val newSearchKey = searchEt.text.toString().trim { it <= ' ' }
if (newSearchKey != mSearchKey || mDisplayType != DisplayType.GAME_DETAIL) {
mSearchKey = newSearchKey
if (!TextUtils.isEmpty(mSearchKey)) {
mViewModel.getSearchResult(mSearchKey!!)
mDao.add(mSearchKey!!)
updateDisplayType(DisplayType.GAME_DETAIL)
} else {
toast("请先输入游戏名再搜索~")
}
}
}
override fun search(type: SearchType, key: String?) {
mSearchType = type
mIsAutoSearchDisabled = true
when (type) {
SearchType.AUTO -> handleAutoSearch(key)
SearchType.HISTORY -> handleHistorySearch(key)
else -> handleOtherSearch()
SearchType.AUTO -> {
mSearchKey = key
updateDisplayType(DisplayType.GAME_DIGEST)
mViewModel.getSearchResult(mSearchKey!!)
}
SearchType.HISTORY -> {
mSearchKey = key
searchEt.setText(key)
searchEt.setSelection(searchEt.text.length)
mViewModel.getSearchResult(mSearchKey!!)
updateDisplayType(DisplayType.GAME_DETAIL)
}
else -> {
val newSearchKey = searchEt.text.toString().trim { it <= ' ' }
if (newSearchKey != mSearchKey || mDisplayType != DisplayType.GAME_DETAIL) {
mSearchKey = newSearchKey
if (!TextUtils.isEmpty(mSearchKey)) {
mViewModel.getSearchResult(mSearchKey!!)
mSearchHistoryDao.add(mSearchKey!!)
updateDisplayType(DisplayType.GAME_DETAIL)
} else {
toast("请先输入游戏名再搜索~")
}
}
}
}
mIsAutoSearchDisabled = false
}

View File

@ -1,11 +1,9 @@
package com.gh.gamecenter.amway.search
import com.gh.gamecenter.core.utils.SPUtils
import com.gh.gamecenter.db.ISearchHistoryDao
class AmwaySearchDao : ISearchHistoryDao {
override fun add(keyword: String) {
class AmwaySearchDao {
fun add(keyword: String) {
val originString = SPUtils.getString(SP_KEY)
if (originString.isEmpty()) {
@ -30,18 +28,16 @@ class AmwaySearchDao : ISearchHistoryDao {
}
}
override fun getAll(): ArrayList<String>? {
fun getAll(): ArrayList<String>? {
val list = SPUtils.getString(SP_KEY).split(SEARCH_KEY_DIVIDER)
return if (list.size == 1 && list[0].isEmpty()) null else ArrayList(list)
}
override fun deleteAll() {
fun deleteAll() {
SPUtils.setString(SP_KEY, "")
}
override fun delete(item: String) {}
companion object {
const val SP_KEY = "amway_key"
const val SEARCH_KEY_DIVIDER = "<-||->"

View File

@ -7,7 +7,6 @@ import androidx.recyclerview.widget.LinearLayoutManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.databinding.FragmentAmwaySearchDefaultBinding
import com.gh.gamecenter.db.ISearchHistoryDao
import com.gh.gamecenter.eventbus.EBSearch
import com.gh.gamecenter.search.SearchDefaultFragment
import com.lightgame.utils.Util_System_Keyboard
@ -15,6 +14,7 @@ import org.greenrobot.eventbus.EventBus
class AmwaySearchDefaultFragment : SearchDefaultFragment() {
private lateinit var mSearchDao: AmwaySearchDao
private lateinit var mViewModel: AmwaySearchViewModel
private val mAmwayBinding by lazy { FragmentAmwaySearchDefaultBinding.inflate(layoutInflater) }
@ -22,15 +22,11 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
override fun getLayoutId() = 0
override fun getInflatedLayout() = mAmwayBinding.root
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
mViewModel = viewModelProviderFromParent()
}
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
mViewModel.playedGames.observeNonNull(viewLifecycleOwner) {
mViewModel = viewModelProviderFromParent()
mViewModel.playedGames.observeNonNull(this) {
defaultViewModel?.isExistHotSearch = it.isNotEmpty()
updateView()
mBinding.hotList.run {
@ -42,21 +38,28 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
}
}
override fun provideDao(): ISearchHistoryDao = AmwaySearchDao()
override fun initView() {
mBinding = mAmwayBinding.searchContent
mBinding.hotHeadContainer.headTitle.text = "最近玩过"
mBinding.historyFlexContainer.setLimitHeight(mFlexMaxHeight)
updateHistorySearchView(null)
defaultViewModel?.historySearchLiveData?.observe(this) {
updateHistorySearchView(it)
}
initHeadView()
override fun initDao() {
mSearchDao = AmwaySearchDao()
mHistoryList = mSearchDao.getAll()
}
override fun initHeadView() {
override fun initView() {
mBinding = mAmwayBinding.searchContent
defaultViewModel?.isExistHistory = mHistoryList?.isNotEmpty() == true
updateView()
mBinding.hotHeadContainer.headTitle.text = "最近玩过"
mBinding.historyFlexContainer.setLimitHeight(mFlexMaxHeight)
createFlexContent(mBinding.historyFlex, mHistoryList, clickListener = {
val key = mHistoryList!![it]
mSearchDao.add(key)
EventBus.getDefault().post(EBSearch("history", key))
Util_System_Keyboard.hideSoftKeyboardByIBinder(context, mBinding.historyFlex.windowToken)
})
initHistoryHeadView()
}
private fun initHistoryHeadView() {
mBinding.historyHeadContainer.run {
headTitle.text = getString(R.string.search_history)
headTitle.textSize = 16F
@ -70,15 +73,17 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
)
headActionTv.setOnClickListener {
DialogHelper.showCenterWarningDialog(requireContext(), "清空记录", "确定清空历史搜索记录?", confirmClickCallback = {
defaultViewModel?.deleteAll()
mSearchDao.deleteAll()
defaultViewModel?.isExistHistory = false
updateView()
updateNoPlayedGameHint()
})
}
}
}
private fun updateNoPlayedGameHint() {
val historyList = defaultViewModel?.historySearchLiveData?.value
if (!historyList.isNullOrEmpty()) {
if (mSearchDao.getAll() != null && mSearchDao.getAll()?.size != 0) {
mAmwayBinding.noDataContainer.visibility = View.GONE
return
}
@ -90,21 +95,4 @@ class AmwaySearchDefaultFragment : SearchDefaultFragment() {
}
}
override fun updateHistorySearchView(historyList: List<String>?) {
defaultViewModel?.isExistHistory = historyList?.isNotEmpty() == true
updateView()
updateNoPlayedGameHint()
historyList?.let {
createFlexContent(mBinding.historyFlex, historyList, clickListener = { id ->
val key = it[id]
defaultViewModel?.add(key)
EventBus.getDefault().post(EBSearch("history", key))
Util_System_Keyboard.hideSoftKeyboardByIBinder(
context,
mBinding.historyFlex.windowToken
)
})
}
}
}

View File

@ -15,9 +15,7 @@ import com.gh.gamecenter.common.utils.viewModelProvider
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.ActivityAuthorizationBinding
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.view.LoginActivity
import com.gh.vspace.VHelper
import com.lightgame.utils.Utils
/**
* @author : liujiarui
@ -164,12 +162,9 @@ class AuthorizationActivity : ToolBarActivity() {
private fun checkLogin(block: () -> Unit) {
//判断光环是否登陆
if (CheckLoginUtils.isLogin()) {
CheckLoginUtils.checkLogin(this, "光环助手授权登陆") {
initData()
block()
} else {
Utils.toast(this, "需要登录")
startActivity(LoginActivity.getIntent(this, "光环助手授权登陆"))
}
}

View File

@ -8,9 +8,6 @@ import com.gh.gamecenter.R
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
/**
* 新分类2.0
*/
class CategoryV2Activity : DownloadToolbarActivity() {
override fun onCreate(savedInstanceState: Bundle?) {

View File

@ -5,7 +5,6 @@ import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.baselist.ListAdapter
import com.gh.gamecenter.common.utils.formatTime
import com.gh.gamecenter.common.utils.toBinding
import com.gh.gamecenter.databinding.ItemArchiveLimitBinding
import com.gh.gamecenter.entity.ArchiveEntity
@ -30,7 +29,10 @@ class ArchiveLimitAdapter(context: Context) : ListAdapter<ArchiveLimitAdapter.Ar
if (holder is ArchiveLimitViewHolder) {
val item = mEntityList[position]
holder.binding.tvTitle.text = item.data.name
holder.binding.tvTime.text = item.data.time.create.formatTime("yyyy-MM-dd HH:mm")
val timeLong = item.data.time.create
val sdf = SimpleDateFormat("yyyy-MM-dd HH:mm", Locale.CHINA)
val date = Date(timeLong)
holder.binding.tvTime.text = sdf.format(date)
val resId = if (item.isChecked) R.drawable.ic_selector_selected else R.drawable.ic_selector_default
holder.binding.ivSelector.setImageResource(resId)
@ -51,10 +53,10 @@ class ArchiveLimitAdapter(context: Context) : ListAdapter<ArchiveLimitAdapter.Ar
}
override fun onBindViewHolder(holder: RecyclerView.ViewHolder, position: Int, payloads: MutableList<Any?>) {
if (holder is ArchiveLimitViewHolder) {
if (payloads.isEmpty()) {
if(holder is ArchiveLimitViewHolder){
if(payloads.isEmpty()){
onBindViewHolder(holder, position)
} else {
}else{
val item = mEntityList[position]
val resId = if (item.isChecked) R.drawable.ic_selector_selected else R.drawable.ic_selector_default
holder.binding.ivSelector.setImageResource(resId)

View File

@ -41,9 +41,6 @@ import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
import java.io.File
/**
* 云存档管理
*/
class CloudArchiveManagerActivity : BaseActivity_TabLayout(), ArchiveLimitSelectedListener {
private lateinit var mBinding: ActivityCloudArchiveManagerBinding

View File

@ -0,0 +1,120 @@
package com.gh.gamecenter.db;
import android.content.Context;
import com.gh.gamecenter.db.info.DataCollectionInfo;
import com.j256.ormlite.dao.Dao;
import java.sql.SQLException;
import java.util.List;
// TODO 这个数据库其实没有用了,上传到 loghub 已经有相关的逻辑处理,有空删掉它
public class DataCollectionDao {
private DatabaseHelper helper;
private Dao<DataCollectionInfo, String> dao;
public DataCollectionDao(Context context) {
try {
helper = DatabaseHelper.getHelper(context);
dao = helper.getDao(DataCollectionInfo.class);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 查找一个数据
*/
public List<DataCollectionInfo> findByType(String type) {
try {
return dao.queryForEq("type", type);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 添加一个数据
*/
public void add(DataCollectionInfo entity) {
try {
dao.create(entity);
} catch (Exception e) {
// java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
e.printStackTrace();
}
}
/**
* 删除一个数据
*/
public void delete(String id) {
try {
dao.deleteById(id);
} catch (Exception e) {
// java.lang.IllegalStateException: attempt to re-open an already-closed object: SQLiteDatabase:
e.printStackTrace();
}
}
/**
* 删除一组数据
*/
public void delete(List<String> ids) {
try {
dao.deleteIds(ids);
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 根据id获取某一个数据
*/
public DataCollectionInfo find(String id) {
try {
return dao.queryForId(id);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取所有的数据
*/
public List<DataCollectionInfo> getAll() {
try {
return dao.queryForAll();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 获取点击数据
*/
public List<DataCollectionInfo> getClickData() {
try {
return dao.queryForEq("type", "click-item");
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
/**
* 更新数据
*/
public void update(DataCollectionInfo entity) {
try {
dao.update(entity);
} catch (Exception e) {
e.printStackTrace();
}
}
}

View File

@ -5,7 +5,9 @@ import android.database.sqlite.SQLiteDatabase;
import androidx.collection.ArrayMap;
import com.gh.gamecenter.db.info.DataCollectionInfo;
import com.gh.gamecenter.db.info.GameTrendsInfo;
import com.gh.gamecenter.db.info.PackageInfo;
import com.gh.gamecenter.db.info.SearchHistoryInfo;
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper;
import com.j256.ormlite.dao.Dao;
@ -50,6 +52,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
try {
Utils.log("DatabaseHelper onCreate");
TableUtils.createTable(connectionSource, SearchHistoryInfo.class);
TableUtils.createTable(connectionSource, DataCollectionInfo.class);
TableUtils.createTable(connectionSource, PackageInfo.class);
TableUtils.createTable(connectionSource, GameTrendsInfo.class);
} catch (SQLException e) {
e.printStackTrace();
@ -61,6 +65,8 @@ public class DatabaseHelper extends OrmLiteSqliteOpenHelper {
try {
Utils.log("DatabaseHelper onUpgrade");
TableUtils.dropTable(connectionSource, SearchHistoryInfo.class, true);
TableUtils.dropTable(connectionSource, DataCollectionInfo.class, true);
TableUtils.dropTable(connectionSource, PackageInfo.class, true);
TableUtils.dropTable(connectionSource, GameTrendsInfo.class, true);
onCreate(database, connectionSource);
} catch (SQLException e) {

View File

@ -1,14 +0,0 @@
package com.gh.gamecenter.db;
import java.util.List;
public interface ISearchHistoryDao {
void deleteAll();
void add(String item);
void delete(String item);
List<String> getAll();
}

View File

@ -11,7 +11,7 @@ import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
public class SearchHistoryDao implements ISearchHistoryDao {
public class SearchHistoryDao {
private DatabaseHelper helper;
private Dao<SearchHistoryInfo, String> dao;
@ -25,7 +25,6 @@ public class SearchHistoryDao implements ISearchHistoryDao {
}
}
@Override
public void add(String item) {
AppExecutor.getIoExecutor().execute(() -> {
try {
@ -36,7 +35,6 @@ public class SearchHistoryDao implements ISearchHistoryDao {
});
}
@Override
public void delete(String item) {
AppExecutor.getIoExecutor().execute(() -> {
try {
@ -47,7 +45,6 @@ public class SearchHistoryDao implements ISearchHistoryDao {
});
}
@Override
public void deleteAll() {
AppExecutor.getIoExecutor().execute(() -> {
CloseableIterator<SearchHistoryInfo> iterator = dao.iterator();
@ -62,7 +59,6 @@ public class SearchHistoryDao implements ISearchHistoryDao {
return;
}
@Override
public List<String> getAll() {
List<String> history = new ArrayList<String>();

View File

@ -0,0 +1,65 @@
package com.gh.gamecenter.db.info;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import java.io.Serializable;
@DatabaseTable(tableName = "tb_datacollection")
public class DataCollectionInfo implements Serializable {
/**
*
*/
private static final long serialVersionUID = 3196892351397147390L;
@DatabaseField(id = true, columnName = "id")
private String id;
@DatabaseField(columnName = "type")
private String type;
@DatabaseField(columnName = "data")
private String data;
public DataCollectionInfo() {
}
public DataCollectionInfo(String id, String type, String data) {
this.id = id;
this.type = type;
this.data = data;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
@Override
public String toString() {
return "DataCollectionEntity [id=" + id + ", type=" + type + ", data="
+ data + "]";
}
}

View File

@ -0,0 +1,42 @@
package com.gh.gamecenter.db.info;
import com.j256.ormlite.field.DatabaseField;
import com.j256.ormlite.table.DatabaseTable;
import java.io.Serializable;
@DatabaseTable(tableName = "tb_package")
public class PackageInfo implements Serializable {
@DatabaseField(id = true, columnName = "packageName")
private String packageName;
@DatabaseField(columnName = "time")
private long time;
public PackageInfo() {
}
public PackageInfo(String packageName, long time) {
this.packageName = packageName;
this.time = time;
}
public String getPackageName() {
return packageName;
}
public void setPackageName(String packageName) {
this.packageName = packageName;
}
public long getTime() {
return time;
}
public void setTime(long time) {
this.time = time;
}
}

View File

@ -6,10 +6,12 @@ import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.PagerSnapHelper
import androidx.recyclerview.widget.RecyclerView
import com.gh.common.exposure.ExposureManager
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
import com.gh.common.util.NewFlatLogUtils
import com.gh.gamecenter.GameDetailActivity
import com.gh.gamecenter.adapter.viewholder.GameViewHolder
import com.gh.gamecenter.common.base.GlobalActivityManager
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.exposure.ExposureSource
import com.gh.gamecenter.common.utils.safelyGetInRelease
@ -19,7 +21,6 @@ import com.gh.gamecenter.entity.AdConfig
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.exposure.ExposureEvent
import com.gh.gamecenter.feature.game.GameItemViewHolder
import com.gh.gamecenter.feature.minigame.MiniGameItemHelper
import com.lightgame.adapter.BaseRecyclerAdapter
import com.lightgame.download.DownloadEntity
@ -92,8 +93,11 @@ class AdGameBannerAdapter(
it.id,
it.name ?: ""
)
if (it.isMiniGame()) {
MiniGameItemHelper.launchMiniGame(it.miniGameAppId, it.miniGameType)
if (it.isQQMiniGame()) {
GlobalActivityManager.currentActivity?.let { activity ->
NewFlatLogUtils.logQGameClick(it.qqMiniGameAppId, it.name)
DirectUtils.directToQGameById(activity, it.qqMiniGameAppId)
}
} else {
GameDetailActivity.startGameDetailActivity(
mContext,

View File

@ -21,7 +21,6 @@ import androidx.recyclerview.widget.RecyclerView
import com.gh.ad.AdDelegateHelper
import com.gh.common.util.HomePluggableHelper
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.PackageHelper
import com.gh.common.util.PackageInstaller
import com.gh.download.DownloadManager
import com.gh.gamecenter.DownloadManagerActivity
@ -277,12 +276,6 @@ class DownloadFragment : BaseFragment_TabLayout() {
if (mBinding.adGameItemContainer.isVisible) {
DownloadManager.getInstance().addObserver(mDataWatcher)
}
refreshInstallStatus()
}
private fun refreshInstallStatus() {
PackageHelper.refreshWrongInstallStatus(PackagesManager.getInstalledSet())
}
override fun onParentActivityFinish() {

View File

@ -10,8 +10,8 @@ import com.ethanhua.skeleton.ViewSkeletonScreen
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DirectUtils
import com.gh.common.util.DownloadItemUtils
import com.gh.common.util.PackageHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.MainActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.ToolbarFragment
import com.gh.gamecenter.common.eventbus.EBReuse
@ -27,7 +27,9 @@ import com.gh.gamecenter.eventbus.EBPackage
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.GameInstall
import com.gh.gamecenter.manager.PackagesManager
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.packagehelper.PackageViewModel
import com.gh.gamecenter.wrapper.MainWrapperFragment
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
import com.lightgame.download.DownloadStatus
@ -108,11 +110,6 @@ class NewInstalledGameFragment : ToolbarFragment() {
mPackageViewModel =
ViewModelProvider(this, PackageViewModel.Factory()).get(PackageViewModel::class.java)
if (mPackageViewModel?.getGameInstalledLiveData()?.value.isNullOrEmpty() == true) {
mInstallGameViewModel.initData(arrayListOf())
}
mPackageViewModel?.getGameInstalledLiveData()?.observe(
this
) { gameInstalls ->
@ -150,20 +147,15 @@ class NewInstalledGameFragment : ToolbarFragment() {
}
mBinding.run {
val isGetInstalledListDisagreed = PackageHelper.isGetInstalledPackagesAgreedRequired()
&& !PackageHelper.isGetInstalledPackagesAgreed()
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
if (PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
reuseNoneData.reuseNoneDataIv.visibility = View.GONE
reuseNoneData.reuseNoneDataTv.text = "开启应用列表权限"
reuseNoneData.reuseNoneDataDescTv.text = " 及时获悉游戏最新的更新消息"
reuseNoneData.reuseResetLoadTv.text = "去开启"
reuseNoneData.reuseResetLoadTv.setOnClickListener {
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
if (isGranted) {
updateNoDataView()
}
PermissionHelper.showGetInstalledAppsListPermissionDialogAndRequestPermission(requireActivity()) {
updateNoDataView()
PackageRepository.initData()
}
}
} else {

View File

@ -3,7 +3,6 @@ package com.gh.gamecenter.download
import android.view.View
import com.gh.common.exposure.ExposureListener
import com.gh.common.util.DirectUtils
import com.gh.common.util.PackageHelper
import com.gh.download.DownloadManager
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.fragment.LazyFragment
@ -14,6 +13,7 @@ import com.gh.gamecenter.common.view.FixLinearLayoutManager
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.FragmentGameUpdatableBinding
import com.gh.gamecenter.eventbus.EBDownloadStatus
import com.gh.gamecenter.packagehelper.PackageRepository
import com.gh.gamecenter.packagehelper.PackageViewModel
import com.lightgame.download.DataWatcher
import com.lightgame.download.DownloadEntity
@ -51,16 +51,10 @@ class UpdatableGameFragment : LazyFragment() {
super.onFragmentFirstVisible()
mPackageViewModel = viewModelProvider(PackageViewModel.Factory())
if (mPackageViewModel?.getGameUpdateIncludeCurrentVersion()?.value.isNullOrEmpty() == true) {
mViewModel?.setUpdatableList(arrayListOf())
}
mPackageViewModel?.getGameUpdateIncludeCurrentVersion()
?.observeNonNull(viewLifecycleOwner) { updatableList ->
mViewModel?.setUpdatableList(updatableList)
}
mViewModel?.updatableData?.observe(viewLifecycleOwner) {
mAdapter?.submitList(it)
mBinding?.noDataContainer?.root?.goneIf(!it.isNullOrEmpty())
@ -100,19 +94,18 @@ class UpdatableGameFragment : LazyFragment() {
noDataContainer.reuseResetLoadTv.layoutParams = layoutParam
noDataContainer.reuseResetLoadTv.visibility = View.VISIBLE
noDataContainer.reuseNoneDataDescTv.visibility = View.VISIBLE
val isGetInstalledListDisagreed = !PackageHelper.isGetInstalledPackagesAgreed()
val isGetInstalledListPermissionDisabled = PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())
if (isGetInstalledListDisagreed || isGetInstalledListPermissionDisabled) {
if (PermissionHelper.isGetInstalledListPermissionDisabled(requireContext())) {
noDataContainer.reuseNoneDataIv.visibility = View.GONE
noDataContainer.reuseNoneDataTv.text = "开启应用列表权限"
noDataContainer.reuseNoneDataDescTv.text = "及时获悉游戏最新的更新消息"
noDataContainer.reuseResetLoadTv.text = "去开启"
noDataContainer.reuseResetLoadTv.setOnClickListener {
PackageHelper.showGetInstallAppsListDialogAndRequestPermissionIfNeeded(requireActivity()) { isGranted ->
PermissionHelper.showGetInstalledAppsListPermissionDialogAndRequestPermission(
requireActivity()
) { isGranted ->
if (isGranted) {
updateNoDataView()
PackageRepository.initData()
}
}
}

View File

@ -26,19 +26,19 @@ data class BottomTab(
@SerializedName("is_default_page")
var default: Boolean = false, // 是否为默认显示页
var isTransparentStyle: Boolean = false // 本地字段透明底部Tab
) : Parcelable {
): Parcelable {
@Parcelize
data class SearchStyle(
@SerializedName("style_type")
var styleType: String = STYLE_TWO_LINES, // 样式类型two_lines搜索栏与顶部tab分为两行、apposition搜索栏与顶部tab同一行
@SerializedName("search_type")
var searchType: String = TYPE_HALO_GAME, // 搜索类型halo_game光环游戏、bbs论坛、mini_game小游戏搜索低于v5.36版本返回qq_mini_game
) : Parcelable {
var searchType: String = TYPE_HALO_GAME, // 搜索类型halo_game光环游戏、qq_mini_gameQQ小游戏、bbs论坛
): Parcelable {
companion object {
const val STYLE_TWO_LINES = "two_lines"
const val STYLE_APPOSITION = "apposition"
const val TYPE_HALO_GAME = "halo_game"
const val TYPE_MINI_GAME = "mini_game"
const val TYPE_QQ_MINI_GAME = "qq_mini_game"
const val TYPE_BBS = "bbs"
}
}

View File

@ -1,23 +0,0 @@
package com.gh.gamecenter.entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.home.custom.model.CustomPageData
import com.google.gson.annotations.SerializedName
data class FollowCommonContentCollection(
@SerializedName("title")
private val _title: String? = null,
@SerializedName("link")
private val _link: LinkEntity? = null,
@SerializedName("link_common_collection")
private val _linkCommonCollection: CustomPageData.CommonContentCollection? = null
) {
val title: String
get() = _title ?: ""
val link: LinkEntity
get() = _link ?: LinkEntity()
val linkCommonCollection: CustomPageData.CommonContentCollection
get() = _linkCommonCollection ?: CustomPageData.CommonContentCollection()
}

View File

@ -1,137 +0,0 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.gh.gamecenter.feature.entity.*
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
@Parcelize
data class FollowDynamicEntity(
@SerializedName("type")
private val _type: String? = null,
@SerializedName("libao")
val libao: LibaoEntity? = null,
@SerializedName("libao_exchange")
val libaoExchange: LibaoEntity? = null,
@SerializedName("game")
private val _game: GameEntity? = null,
@SerializedName("me")
private val _me: MeEntity? = null,
@SerializedName("time")
private val _time: Long? = null,
@SerializedName("article")
val article: Article? = null,
@SerializedName("user_post")
val userPost: ArticleEntity? = null
) : Parcelable {
val type: String
get() = _type ?: ""
val game: GameEntity
get() = _game ?: GameEntity()
val me: MeEntity
get() = _me ?: MeEntity()
val time: Long
get() = _time ?: 0L
companion object {
const val FOLLOW_UPDATE_TYPE_LIBAO = "libao"
const val FOLLOW_UPDATE_TYPE_LIBAO_EXCHANGE = "libao_exchange"
const val FOLLOW_UPDATE_TYPE_ARTICLE = "article"
const val FOLLOW_UPDATE_TYPE_USER_POST = "user_post"
}
@Parcelize
data class Article(
@SerializedName("_id")
private val _id: String? = null,
@SerializedName("title")
private val _title: String? = null,
@SerializedName("content")
private val _content: String? = null,
@SerializedName("img")
private val _img: List<String>? = null,
@SerializedName("count")
private val _count: Count? = null,
@SerializedName("game")
private val _game: GameEntity? = null,
@SerializedName("time")
private val _time: Long? = null,
@SerializedName("_seq")
private val _shortId: String? = null
) : Parcelable {
val id: String
get() = _id ?: ""
val title: String
get() = _title ?: ""
val content: String
get() = _content ?: ""
val img: List<String>
get() = _img ?: listOf()
val count: Count
get() = _count ?: Count()
val game: GameEntity
get() = _game ?: GameEntity()
val time: Long
get() = _time ?: 0L
val shortId: String
get() = _shortId ?: ""
@Parcelize
data class Count(
@SerializedName("comment")
private val _comment: Int? = null
) : Parcelable {
val comment: Int
get() = _comment ?: 0
}
}
@Parcelize
data class LibaoExchange(
@SerializedName("_id")
private val _id: String? = null,
@SerializedName("libao_id")
private val _libaoId: String? = null,
@SerializedName("libao_code")
private val _libaoCode: String? = null,
@SerializedName("name")
private val _name: String? = null,
@SerializedName("content")
private val _content: String? = null,
@SerializedName("active")
private val _active: Boolean? = null
) : Parcelable {
val id: String
get() = _id ?: ""
val libaoId: String
get() = _libaoId ?: ""
val libaoCode: String
get() = _libaoCode ?: ""
val name: String
get() = _name ?: ""
val content: String
get() = _content ?: ""
val active: Boolean
get() = _active ?: false
}
}

View File

@ -1,7 +0,0 @@
package com.gh.gamecenter.entity
data class FollowOperateTopRequest(
val _id: String,
val order: Int
) {
}

View File

@ -1,159 +0,0 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.User
import com.gh.gamecenter.feature.entity.UserEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.Parcelize
import java.util.Objects
@Parcelize
data class FollowUserEntity(
@SerializedName("_id")
private val _id: String? = null,
@SerializedName("type")
private val _type: String? = null, // user用户、bbs论坛
@SerializedName("user")
private val _user: UserEntity? = null,
@SerializedName("bbs")
private var _bbs: Bbs? = null,
@SerializedName("is_top")
private val _isTop: Int? = null,
@SerializedName("is_show_tip")
private val _isShowTip: Int? = null,
) : Parcelable {
val id: String
get() = _id ?: ""
val type: String
get() = _type ?: ""
val user: UserEntity
get() = _user ?: UserEntity()
val isTop: Boolean
get() = _isTop == 1
val isShowTip: Boolean
get() = _isShowTip == 1
val bbs: Bbs
get() = _bbs ?: Bbs()
val isUser: Boolean
get() = type == TYPE_USER
val name: String
get() = if (isUser) {
user.name ?: ""
} else {
bbs.name
}
val icon: String
get() = when {
type == TYPE_USER -> user.icon ?: ""
bbs.type == BBS_TYPE_GAME -> bbs.game.icon ?: ""
else -> bbs.icon
}
override fun equals(other: Any?): Boolean {
return other is FollowUserEntity
&& id == other.id
&& type == other.type
&& user.id == other.user.id
&& user.name == other.user.name
&& bbs.id == other.bbs.id
&& bbs.name == other.bbs.name
&& bbs.type == other.bbs.type
&& bbs.icon == other.bbs.icon
&& bbs.game.icon == other.bbs.game.icon
&& isTop == other.isTop
&& isShowTip == other.isShowTip
&& icon == other.icon
}
override fun hashCode(): Int {
return Objects.hash(
_id,
type,
user.id,
user.name,
bbs.id,
bbs.name,
bbs.type,
bbs.icon,
bbs.game.icon,
isTop,
isShowTip,
icon
)
}
companion object {
private const val TYPE_USER = "user"
private const val BBS_TYPE_GAME = "game_bbs"
}
@Parcelize
data class Bbs(
@SerializedName("_id")
private val _id: String? = null,
@SerializedName("name")
private val _name: String? = null,
@SerializedName("type")
private val _type: String? = null,
@SerializedName("icon")
private val _icon: String? = null,
@SerializedName("game")
private val _game: GameEntity? = null
) : Parcelable {
val id: String
get() = _id ?: ""
val name: String
get() = _name ?: ""
val type: String
get() = _type ?: ""
val icon: String
get() = _icon ?: ""
val game: GameEntity
get() = _game ?: GameEntity()
}
@Parcelize
data class Game(
@SerializedName("_id")
private val _id: String? = null,
@SerializedName("name")
private val _name: String? = null,
@SerializedName("icon")
private val _icon: String? = null,
@SerializedName("ori_icon")
private val _oriIcon: String? = null,
@SerializedName("active")
private val _active: Boolean? = null
) : Parcelable {
val id: String
get() = _id ?: ""
val name: String
get() = _name ?: ""
val icon: String
get() = _icon ?: ""
val oriIcon: String
get() = _oriIcon ?: ""
val active: Boolean
get() = _active ?: false
}
}

View File

@ -1,7 +1,6 @@
package com.gh.gamecenter.entity
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.SettingsEntity
import com.gh.gamecenter.feature.entity.SimulatorEntity
import com.google.gson.annotations.SerializedName
@ -12,16 +11,9 @@ class NewApiSettingsEntity(
var startup: StartupAdEntity? = null,//启动文案广告
@SerializedName("user_interested_game")
var userInterestedGame: Boolean = false, //偏好设置状态开关
@SerializedName("installed_compliance_switch")
var installedComplianceSwitch: Boolean? = false, //安装合规开关
@SerializedName("listen_switch")
var isPackageObserveEnable: Boolean = false, // 安装包监听开关
@SerializedName("listen_str")
var packageObserveActions: PackageObserveActions? = null, // 安装包监听的三个 action
var install: Install, // 安装相关的
@SerializedName("game_shield_contents")
var gameShieldContents: List<String>? = listOf(),//游戏屏蔽内容
var search: SettingsEntity.Search? = null// 游戏搜索配置
) {
/**
*
@ -35,51 +27,23 @@ class NewApiSettingsEntity(
val install: Boolean
)
// VPN 配置
class Install(
@SerializedName("vpn_required")
val vpnRequired: VpnSetting? = null,
@SerializedName("guides")
val guides: List<Guide>? = null
)
/**
* 当为鸿蒙纯净模式相关时
* {
* "type': "harmony_os_pure_mode", // 华为鸿蒙系统纯净模式相关 harmony_os_pure_mode/harmony_os_pure_enhanced_mode
* "link": {
* "type": "qa_content",
* "link": "61026d03802abe4d7260fdc3",
* "text": "点击查看教程" // 链接文案,有设置时替换该字段,无设置时读取对应通用链接原本的文案
* },
* "title": "",
* "content": "",
* "link_text_prefix": "关闭场景模式:", // 链接引导文案
* "button_text_close": "", // 关闭按钮
* "button_text_setting_jump": "", // 前往设置按钮
* "dialog_frequency": "once" // once/every_time
* }
*/
class Guide(
val type: String,
val link: LinkEntity,
val title: String = "",
val content: String = "",
@SerializedName("link_text_prefix")
val linkTextPrefix: String = "",
@SerializedName("link_text")
val linkText: String = "",
@SerializedName("button_text_close")
val buttonTextClose: String = "",
@SerializedName("button_text_setting_jump")
val buttonTextSettingJump: String = "",
@SerializedName("dialog_frequency")
val dialogFrequency: String = "",
class VpnSetting(
@SerializedName("current_device")
val shouldShowVpnOption: Boolean,
@SerializedName("packages")
val vpnMatchedPackagesName: HashSet<String>
)
class PackageObserveActions(
@SerializedName("ADD")
val add: String,
@SerializedName("REM")
val rem: String,
@SerializedName("REP")
val rep: String
class Guide(
val type: String,
val link: LinkEntity
)
}

View File

@ -163,14 +163,6 @@ data class PersonalHistoryEntity(
get() = community.name
set(_) {}
override fun vote(isVote: Boolean) {
_count.vote = if (isVote) {
_count.vote + 1
} else {
_count.vote - 1
}
}
fun getPassVideos(): List<CommunityVideoEntity> {
val passVideos = arrayListOf<CommunityVideoEntity>()
for (video in videos) {

View File

@ -17,7 +17,7 @@ class SubjectData(
var filter: String = "", // Filter: 类型(分类)
var tagType: String? = "", // 游戏Item 标签类型
var briefStyle: String = "",
var subjectType: SubjectType = SubjectType.NORMAL,
var isQQMiniGame: Boolean = false,
var subjectStyle: String = "",
var requireUpdateSetting: Boolean = false, // 多专题页面需要专题页面自行获取专题配置
@ -43,24 +43,4 @@ class SubjectData(
tag = tagType
)
}
/**
* 专题类型
*/
enum class SubjectType {
/**
* 普通专题
*/
NORMAL,
/**
* QQ小游戏专题
*/
QQ_GAME,
/**
* 微信小游戏专题
*/
WECHAT_GAME
}
}

View File

@ -1,6 +1,8 @@
package com.gh.gamecenter.entity
import android.os.Parcelable
import android.text.TextUtils
import com.gh.common.constant.Config
import com.gh.common.filter.RegionSettingHelper
import com.gh.gamecenter.common.entity.LinkEntity
import com.gh.gamecenter.feature.entity.GameEntity
@ -100,9 +102,6 @@ data class SubjectEntity(
@SerializedName("is_qq_column")
var isQQColumn: Boolean = false,
@SerializedName("is_wechat_column")
var isWechatColumn: Boolean = false,
var explain: String = "", // 游戏单合集说明
@SerializedName("show_star")
@ -111,22 +110,11 @@ data class SubjectEntity(
@IgnoredOnParcel
var data: MutableList<GameEntity>?
get() = filteredData ?: RegionSettingHelper.filterGame(mData).also { filteredData = it }
get() = RegionSettingHelper.filterGame(mData)
set(value) {
mData = RegionSettingHelper.filterGame(value).also { filteredData = it }
mData = value
}
@IgnoredOnParcel
private var filteredData: MutableList<GameEntity>? = null
val subjectType: SubjectData.SubjectType get() = when {
isQQColumn -> SubjectData.SubjectType.QQ_GAME
isWechatColumn -> SubjectData.SubjectType.WECHAT_GAME
else -> SubjectData.SubjectType.NORMAL
}
val isMiniGame: Boolean get() = isQQColumn || isWechatColumn
val showStar: Boolean
get() = _showStar ?: false

View File

@ -4,7 +4,10 @@ import android.os.Parcelable
import com.alibaba.android.arouter.launcher.ARouter
import com.gh.gamecenter.common.constant.RouteConsts
import com.gh.gamecenter.core.provider.IConfigProvider
import com.gh.gamecenter.feature.entity.*
import com.gh.gamecenter.feature.entity.GameEntity
import com.gh.gamecenter.feature.entity.MeEntity
import com.gh.gamecenter.feature.entity.SourceEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.google.gson.annotations.SerializedName
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@ -59,16 +62,6 @@ open class VideoEntity(
@SerializedName("_source")
val ipSource: SourceEntity? = null,
// 关注首页新增字段
@SerializedName("choiceness")
private val _choiceness: Boolean? = null,
@SerializedName("video_info")
private val _videoInfo: VideoInfo? = null,
@SerializedName("show_me_only")
private val _showMeOnly: Boolean? = null,
@SerializedName("count")
private val _count: Count? = null,
//本地数据
@IgnoredOnParcel
var videoIsMuted: Boolean = false,//是否静音标记
@ -80,18 +73,6 @@ open class VideoEntity(
val gameName: String
get() = mGameName.removeSuffix(".")
val choiceness: Boolean
get() = _choiceness ?: false
val videoInfo: VideoInfo
get() = _videoInfo ?: VideoInfo()
val showMeOnly: Boolean
get() = _showMeOnly ?: false
val count: Count
get() = _count ?: Count()
fun getThumb(): String {
val configProvider = ARouter.getInstance().build(RouteConsts.provider.config).navigation() as? IConfigProvider
return if (!configProvider?.getVideoSnapshotSuffix().isNullOrEmpty()) {

View File

@ -1,5 +0,0 @@
package com.gh.gamecenter.entity
class WhitePackageListEntity {
var data: HashSet<String>? = null
}

View File

@ -42,10 +42,6 @@ class ForumArticleAskListAdapter(
private var mVideoOrderList = listOf("推荐", "发布")
private var mFilterPosition = if (path == "视频") 1 else 0
override fun setListData(updateData: MutableList<AnswerEntity>?) {
super.setListData(updateData)
}
override fun areItemsTheSame(oldItem: AnswerEntity?, newItem: AnswerEntity?): Boolean {
return oldItem?.id == newItem?.id
}

View File

@ -10,9 +10,6 @@ import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.EntranceConsts
import com.gh.gamecenter.common.utils.updateStatusBarColor
/**
* 论坛详情
*/
class ForumDetailActivity : BaseActivity() {
private var mContainerFragment: Fragment? = null

View File

@ -1,86 +0,0 @@
package com.gh.gamecenter.forum.home
import android.app.Activity
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.common.utils.SensorsBridge
import com.gh.gamecenter.common.utils.ShareUtils
import com.gh.gamecenter.common.utils.isPublishEnv
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.login.user.UserManager
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
class AnswerArticleVideoViewEventHelper(
private val entity: ForumVideoEntity,
private val orientationUtils: OrientationUtils
) : ArticleItemVideoView.OnArticleItemVideoViewEventListener {
override fun onTrackVideoStartPlaying() {
SensorsBridge.trackVideoStartPlaying(
articleId = entity.id,
bbsId = entity.bbs?.id ?: "",
bbsType = entity.bbs?.typeChinese ?: "综合论坛",
customerType = entity.user.auth?.text ?: "",
videoId = entity.id,
playType = "视频贴",
gameForumType = entity.bbs?.game?.categoryChinese ?: "",
activityTag = entity.tagActivityName,
articleType = entity.typeChinese
)
}
override fun onTrackVideoEndPlaying(maxPlayedProgress: Int) {
SensorsBridge.trackVideoEndPlaying(
articleId = entity.id,
bbsId = entity.bbs?.id ?: "",
bbsType = entity.bbs?.typeChinese ?: "综合论坛",
customerType = entity.user.auth?.text ?: "",
videoId = entity.id,
playType = "视频贴",
gameForumType = entity.bbs?.game?.categoryChinese ?: "",
activityTag = entity.tagActivityName,
articleType = entity.typeChinese,
result = if (maxPlayedProgress >= 95) "" else ""
)
}
override fun onShare(videoView: ArticleItemVideoView) {
val shareIcon = entity.poster
val shareUrl = if (isPublishEnv()) {
"https://m.ghzs666.com/video/${entity.id}"
} else {
"https://resource.ghzs.com/page/video_play/video/video.html?video=${entity.id}"
}
val additionalParams = AdditionalParamsEntity().apply {
contentType = "视频帖"
contentId = entity.id ?: ""
bbsId = entity.bbs?.id ?: ""
bbsType = entity.bbs?.typeChinese ?: "综合论坛"
customerType = entity.user.auth?.text ?: ""
activityTagName = entity.tagActivityName ?: ""
gameForumType = entity.bbs?.game?.categoryChinese ?: ""
refUserId = UserManager.getInstance().userId
}
val activity = (videoView.context as? Activity) ?: return
ShareUtils.getInstance(videoView.context).showShareWindowsCallback(activity,
videoView,
shareUrl,
shareIcon,
entity.title,
entity.des,
ShareUtils.ShareEntrance.video,
entity.id,
additionalParams,
object : ShareUtils.ShareCallBack {
override fun onSuccess(label: String) = Unit
override fun onCancel() = Unit
})
}
override fun onMutedChanged(isMuted: Boolean) {
entity.videoIsMuted = isMuted
}
override fun onStartWindowFullscreen() {
orientationUtils.resolveByClick()
}
}

View File

@ -1,8 +1,7 @@
package com.gh.gamecenter.forum.home
import android.app.Activity
import android.content.Context
import android.os.Handler
import android.os.Looper
import android.util.AttributeSet
import android.view.MotionEvent
import android.view.Surface
@ -13,9 +12,11 @@ import android.widget.TextView
import androidx.core.content.ContextCompat
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.gamecenter.R
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.MD5Utils
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.video.detail.CustomManager
import com.lightgame.utils.Utils
import com.shuyu.gsyvideoplayer.utils.CommonUtil
@ -24,12 +25,13 @@ import com.shuyu.gsyvideoplayer.video.base.GSYVideoView
import com.shuyu.gsyvideoplayer.video.base.GSYVideoViewBridge
import io.reactivex.disposables.Disposable
import java.util.*
import android.os.Handler
import android.os.Looper
class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
StandardGSYVideoPlayer(context, attrs) {
var data: ArticleVideoData? = null
private var mVideoEntity: ForumVideoEntity? = null
private var mMuteDisposable: Disposable? = null
private var mIsAutoPlay = false
var uuid = UUID.randomUUID().toString()
@ -51,8 +53,6 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
private val mTrackHandler = Handler(Looper.getMainLooper())
private var onEventListener: OnArticleItemVideoViewEventListener? = null
override fun getLayoutId(): Int {
return R.layout.layout_article_item_video
}
@ -81,7 +81,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
fun startPlayLogic(isAutoPlay: Boolean) {
mIsAutoPlay = isAutoPlay
if (mIsAutoPlay) {
val seekTime = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(data?.url))
val seekTime = ForumScrollCalculatorHelper.getPlaySchedule(MD5Utils.getContentMD5(mVideoEntity?.url))
seekOnStart = seekTime
}
startPlayLogic()
@ -137,7 +137,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
}
override fun onClickUiToggle(e: MotionEvent?) {
if (data?.status != "pending" && data?.status != "fail") {
if (mVideoEntity?.status != "pending" && mVideoEntity?.status != "fail") {
if (mCurrentState == CURRENT_STATE_PLAYING) {
if (mStartButton.visibility == View.VISIBLE) {
changeUiToPlayingClear()
@ -156,6 +156,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
super.onSurfaceUpdated(surface)
if (mThumbImageViewLayout != null && mThumbImageViewLayout.visibility == View.VISIBLE) {
mThumbImageViewLayout.visibility = View.INVISIBLE
uploadVideoStreamingPlaying("开始播放")
}
}
@ -168,9 +169,14 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
// }
override fun releaseVideos() {
uploadVideoStreamingPlaying("结束播放")
CustomManager.releaseAllVideos(getKey())
}
override fun onVideoPause() {
super.onVideoPause()
uploadVideoStreamingPlaying("暂停播放")
}
// 重载以减少横竖屏切换的时间
override fun checkoutState() {
@ -200,7 +206,9 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
private fun showBackBtn() {
mTopContainer.background = ContextCompat.getDrawable(context, R.drawable.video_title_bg)
back.visibility = View.VISIBLE
data?.title?.let(titleTv::setText)
mVideoEntity?.run {
titleTv.text = title
}
}
private fun hideBackBtn() {
@ -209,32 +217,19 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
titleTv.text = ""
}
fun updateVideoData(video: ArticleVideoData, listener: OnArticleItemVideoViewEventListener) {
this.onEventListener = listener
fun updateThumb(url: String) {
ImageUtils.display(thumbImage, url)
}
fun updateDurationTv(duration: String) {
durationTv.text = duration
}
fun updateVideoData(video: ForumVideoEntity) {
this.mStopTrackRunnable = null
data = video
mVideoEntity = video
titleTv.text = if (mIfCurrentIsFullscreen) video.title else ""
ImageUtils.display(thumbImage, video.poster)
if (!mIfCurrentIsFullscreen) {
durationTv.text = video.duration
setVideoStatus(video.status)
fullscreenButton.setOnClickListener {
val horizontalVideoView =
startWindowFullscreen(context, true, true) as? ArticleItemVideoView
if (horizontalVideoView == null) {
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
return@setOnClickListener
}
onEventListener?.onStartWindowFullscreen()
horizontalVideoView.uuid = uuid
horizontalVideoView.updateVideoData(video, listener)
horizontalVideoView.violenceUpdateMuteStatus()
horizontalVideoView.setFullViewStatus()
}
}
}
override fun setViewShowState(view: View?, visibility: Int) {
@ -271,16 +266,25 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
override fun setStateAndUi(state: Int) {
if (state != currentState
&& state == CURRENT_STATE_PLAYING
&& currentState != CURRENT_STATE_PLAYING_BUFFERING_START
) {// 上报开始视频播放埋点
&& currentState != CURRENT_STATE_PLAYING_BUFFERING_START) {// 上报开始视频播放埋点
// 视频停止播放后再恢复播放的间隔时间未超过3秒则取消上报结束视频播放埋点
mStopTrackRunnable?.let { runnable ->
mTrackHandler.removeCallbacks(runnable)
}
data?.let {
mVideoEntity?.let {
val startTrackRunnable = Runnable {
onEventListener?.onTrackVideoStartPlaying()
SensorsBridge.trackVideoStartPlaying(
articleId = it.id,
bbsId = it.bbs?.id ?: "",
bbsType = it.bbs?.typeChinese ?: "综合论坛",
customerType = it.user.auth?.text ?: "",
videoId = it.id,
playType = "视频贴",
gameForumType = it.bbs?.game?.categoryChinese ?: "",
activityTag = it.tagActivityName,
articleType = it.typeChinese
)
mStartTrackRunnable = null
}.also {
mStartTrackRunnable = it
@ -299,9 +303,20 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
mTrackHandler.removeCallbacks(it)
mStartTrackRunnable = null
} ?: let {
data?.let {
mVideoEntity?.let {
val stopTrackRunnable = Runnable {
onEventListener?.onTrackVideoEndPlaying(mMaxPlayedProgress)
SensorsBridge.trackVideoEndPlaying(
articleId = it.id,
bbsId = it.bbs?.id ?: "",
bbsType = it.bbs?.typeChinese ?: "综合论坛",
customerType = it.user.auth?.text ?: "",
videoId = it.id,
playType = "视频贴",
gameForumType = it.bbs?.game?.categoryChinese ?: "",
activityTag = it.tagActivityName,
articleType = it.typeChinese,
result = if (mMaxPlayedProgress >= 95) "" else ""
)
}.also {
this.mStopTrackRunnable = it
}
@ -327,9 +342,10 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
replayTv.setOnClickListener {
startButton.performClick()
violenceUpdateMuteStatus()
uploadVideoStreamingPlaying("重新播放")
}
shareTv.setOnClickListener {
onEventListener?.onShare(this)
share()
}
} else {
setViewShowState(completeContainer, View.GONE)
@ -347,33 +363,88 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
}
private fun toggleMute() {
if (data?.videoIsMuted == true) {
unMute()
if (mVideoEntity?.videoIsMuted == true) {
unMute(true)
} else {
mute()
mute(true)
}
}
private fun updateMuteStatus() {
if (data?.videoIsMuted == true) {
fun updateMuteStatus() {
if (mVideoEntity?.videoIsMuted == true) {
mute()
} else {
unMute()
}
}
private fun mute() {
onEventListener?.onMutedChanged(true)
data?.videoIsMuted = true
fun mute(isManual: Boolean = false) {
mVideoEntity?.videoIsMuted = true
volume.setImageResource(R.drawable.ic_article_video_volume_off)
CustomManager.getCustomManager(getKey()).isNeedMute = true
if (isManual) {
// Utils.toast(context, "当前处于静音状态")
uploadVideoStreamingPlaying("点击静音")
}
}
private fun unMute() {
onEventListener?.onMutedChanged(false)
data?.videoIsMuted = false
fun unMute(isManual: Boolean = false) {
mVideoEntity?.videoIsMuted = false
volume.setImageResource(R.drawable.ic_article_video_volume_on)
CustomManager.getCustomManager(getKey()).isNeedMute = false
if (isManual) {
uploadVideoStreamingPlaying("取消静音")
}
}
override fun onAutoCompletion() {
super.onAutoCompletion()
uploadVideoStreamingPlaying("播放完毕")
}
override fun onStopTrackingTouch(seekBar: SeekBar?) {
super.onStopTrackingTouch(seekBar)
uploadVideoStreamingPlaying("拖动")
}
private fun share() {
mVideoEntity?.let {
val shareIcon = it.poster
val shareUrl = if (isPublishEnv()) {
"https://m.ghzs666.com/video/${it.id}"
} else {
"https://dev-and-static.ghzs66.com/page/video_play/video/video.html?video=${it.id}"
}
val additionalParams = AdditionalParamsEntity().apply {
contentType = "视频帖"
contentId = mVideoEntity?.id ?: ""
bbsId = mVideoEntity?.bbs?.id ?: ""
bbsType = mVideoEntity?.bbs?.typeChinese ?: "综合论坛"
customerType = mVideoEntity?.user?.auth?.text ?: ""
activityTagName = mVideoEntity?.tagActivityName ?: ""
gameForumType = mVideoEntity?.bbs?.game?.categoryChinese ?: ""
refUserId = UserManager.getInstance().userId
}
ShareUtils.getInstance(context).showShareWindowsCallback(context as Activity,
this,
shareUrl,
shareIcon,
it.title,
it.des,
ShareUtils.ShareEntrance.video, it.id, additionalParams, object : ShareUtils.ShareCallBack {
override fun onSuccess(label: String) {
// if ("短信" == label || "复制链接" == label) viewModel?.shareVideoStatistics(it)
}
override fun onCancel() {
uploadVideoStreamingPlaying("取消分享")
}
})
}
}
fun uploadVideoStreamingPlaying(action: String) {
}
fun setFullViewStatus() {
@ -385,7 +456,7 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
durationTv.visibility = View.GONE
}
private fun setVideoStatus(status: String) {
fun setVideoStatus(status: String) {
if (status == "pending" || status == "fail") {
pendingView.text = if (status == "pending") "审核中...请耐心等待" else "审核不通过"
pendingView.visibility = View.VISIBLE
@ -423,32 +494,4 @@ class ArticleItemVideoView @JvmOverloads constructor(context: Context, attrs: At
super.onDetachedFromWindow()
mMuteDisposable?.dispose()
}
companion object {
fun ForumVideoEntity.toArticleVideoData() =
ArticleVideoData(url, title, status, poster, duration, videoIsMuted)
}
data class ArticleVideoData(
val url: String,
val title: String,
val status: String,
val poster: String,
val duration: String,
var videoIsMuted: Boolean
)
interface OnArticleItemVideoViewEventListener {
fun onTrackVideoStartPlaying()
fun onTrackVideoEndPlaying(maxPlayedProgress: Int)
fun onShare(videoView: ArticleItemVideoView)
fun onMutedChanged(isMuted: Boolean)
fun onStartWindowFullscreen()
}
}

View File

@ -8,9 +8,6 @@ import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE
import com.gh.gamecenter.video.detail.HomeVideoFragment
/**
* 社区首页
*/
class CommunityActivity : BaseActivity() {
override fun getLayoutId(): Int = R.layout.activity_community

View File

@ -4,7 +4,6 @@ import android.app.Activity
import android.content.Intent
import android.content.res.ColorStateList
import android.graphics.Bitmap
import android.graphics.ColorFilter
import android.graphics.Typeface
import android.os.Build
import android.os.Bundle
@ -12,20 +11,17 @@ import android.view.*
import android.view.animation.AnimationUtils
import android.widget.FrameLayout
import android.widget.TextView
import androidx.appcompat.content.res.AppCompatResources
import androidx.core.graphics.ColorUtils
import androidx.core.os.bundleOf
import androidx.fragment.app.Fragment
import androidx.lifecycle.Observer
import androidx.viewpager.widget.ViewPager
import com.airbnb.lottie.LottieProperty
import com.airbnb.lottie.SimpleColorFilter
import com.airbnb.lottie.model.KeyPath
import com.airbnb.lottie.value.LottieValueCallback
import com.gh.common.browse.BrowseTimer
import com.gh.common.util.DirectUtils
import com.gh.common.browse.withLifecycle
import com.gh.common.util.NewFlatLogUtils
import com.gh.common.util.DirectUtils
import com.gh.common.util.NewLogUtils
import com.gh.common.util.ViewPagerFragmentHelper
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.TrackableDialog
import com.gh.gamecenter.common.base.adapter.FragmentAdapter
@ -36,7 +32,6 @@ import com.gh.gamecenter.common.constant.EntranceConsts.IS_DETAIL_PAGE
import com.gh.gamecenter.common.retrofit.ApiResponse
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.AvatarBorderView
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.core.utils.MtaHelper
import com.gh.gamecenter.core.utils.SPUtils
@ -48,11 +43,8 @@ import com.gh.gamecenter.eventbus.EBSkip
import com.gh.gamecenter.eventbus.EBTypeChange
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.forum.home.follow.FollowHomeFilterPopWindow
import com.gh.gamecenter.forum.home.follow.fragment.FollowHomeFragment
import com.gh.gamecenter.forum.search.ForumOrUserSearchActivity
import com.gh.gamecenter.login.entity.UserInfoEntity
import com.gh.gamecenter.login.user.UserManager
import com.gh.gamecenter.login.user.UserRepository
import com.gh.gamecenter.qa.article.detail.ArticleDetailWebCacheManager
import com.gh.gamecenter.qa.article.edit.ArticleEditActivity
@ -62,7 +54,6 @@ import com.gh.gamecenter.qa.video.publish.VideoPublishActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.gh.gamecenter.wrapper.MainWrapperViewModel
import com.google.android.material.tabs.TabLayout
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
import com.halo.assistant.HaloApp
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
@ -75,7 +66,7 @@ class CommunityHomeFragment : LazyFragment() {
private var mViewModel: CommunityHomeViewModel? = null
private var mMainWrapperViewModel: MainWrapperViewModel? = null
private var mFragmentList = arrayListOf<Fragment>()
private var mTitleList = arrayListOf("关注", "推荐", "论坛", "活动")
private var mTitleList = arrayListOf("推荐", "论坛", "活动")
private var mTabList = arrayListOf<Any>()
private var mDefaultSelectedTab = -1
private var mNavigationBitmap: Bitmap? = null
@ -83,46 +74,11 @@ class CommunityHomeFragment : LazyFragment() {
private var mBottomTabId = ""
private val browseTimer = BrowseTimer()
.withLifecycle(this)
.withResult {
SensorsBridge.trackCommunityBrowsingDuration(it / 1000.0)
}
private val followFilterPopWindow by lazy {
FollowHomeFilterPopWindow.create(requireContext()).apply {
setOnDismissListener {
resetFollowTab()
}
setFilterListener(object : FollowHomeFilterPopWindow.OnFilteredChangedListener {
override fun filterFollowed(position: Int) {
mViewModel?.filterFollowed(position)
}
})
}
}
private val obTabSelectedListener = object : OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab?) {
if (tab?.position == TAB_FOLLOW_INDEX) {
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
}
}
override fun onTabUnselected(tab: TabLayout.Tab?) {
if (tab?.position == TAB_FOLLOW_INDEX) {
mViewModel?.updateFilterResId(null)
}
}
override fun onTabReselected(tab: TabLayout.Tab?) {
if (mViewModel?.followFilterStatus?.value == R.drawable.ic_follow_arrow_down) {
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_up)
mBinding?.tabLayout?.let(followFilterPopWindow::showAsDropDown)
}
}
}
override fun getRealLayoutId(): Int {
return R.layout.fragment_community_home
}
@ -141,19 +97,6 @@ class CommunityHomeFragment : LazyFragment() {
super.onFragmentFirstVisible()
initViewPager()
mViewModel?.followFilterStatus?.observe(viewLifecycleOwner, Observer { resId ->
val tabLayout = mBinding?.tabLayout ?: return@Observer
val tab = tabLayout.getTabAt(TAB_FOLLOW_INDEX)
val tvTitle = tab?.customView?.findViewById<TextView>(R.id.tab_title)
if (resId != null) {
tvTitle?.setDrawableEnd(resId, 8F.dip2px(), 8F.dip2px())
} else {
tvTitle?.setDrawableEnd(null, 8F.dip2px(), 8F.dip2px())
}
})
}
override fun initRealView() {
@ -204,13 +147,7 @@ class CommunityHomeFragment : LazyFragment() {
topBg.visibleIf(!mIsDarkModeOn)
videoLottie.setOnClickListener {
DirectUtils.directToLegacyVideoDetail(
requireContext(),
"",
VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value,
referer = "视频流-社区右上角",
isHomeVideo = true
)
DirectUtils.directToLegacyVideoDetail(requireContext(), "", VideoDetailContainerViewModel.Location.VIDEO_ACTIVITY.value, referer = "视频流-社区右上角", isHomeVideo = true)
}
searchIconIv.setOnClickListener {
NewLogUtils.logCommunitySearchClick()
@ -265,14 +202,13 @@ class CommunityHomeFragment : LazyFragment() {
mBottomTabId = arguments?.getString(EntranceConsts.KEY_BOTTOM_TAB_ID, "") ?: ""
}
override fun onResume() {
super.onResume()
browseTimer.start()
}
override fun onPause() {
super.onPause()
browseTimer.stop()
override fun setUserVisibleHint(isVisibleToUser: Boolean) {
super.setUserVisibleHint(isVisibleToUser)
if (isVisibleToUser) {
browseTimer.start()
} else {
browseTimer.stop()
}
}
override fun onSaveInstanceState(outState: Bundle) {
@ -300,12 +236,7 @@ class CommunityHomeFragment : LazyFragment() {
mTabList.clear()
mFragmentList.clear()
val tag = "android:switcher:${viewPager.id}:"
val followFragment = childFragmentManager.findFragmentByTag("$tag$TAB_FOLLOW_INDEX")
?: FollowHomeFragment()
mFragmentList.add(followFragment)
val forumArticleListFragment = childFragmentManager.findFragmentByTag("$tag$TAB_RECOMMEND_INDEX")
val forumArticleListFragment = childFragmentManager.findFragmentByTag("${tag}0")
?: ForumArticleListFragment().with(
bundleOf(
EntranceConsts.KEY_ENTRANCE to "社区",
@ -314,11 +245,11 @@ class CommunityHomeFragment : LazyFragment() {
)
mFragmentList.add(forumArticleListFragment)
val forumFragment = childFragmentManager.findFragmentByTag("${tag}$TAB_FORUM_INDEX")
val forumFragment = childFragmentManager.findFragmentByTag("${tag}1")
?: ForumFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "社区"))
mFragmentList.add(forumFragment)
val activityFragment = childFragmentManager.findFragmentByTag("${tag}$TAB_ACTIVITY_INDEX")
val activityFragment = childFragmentManager.findFragmentByTag("${tag}2")
?: ForumActivityFragment().with(bundleOf(EntranceConsts.KEY_ENTRANCE to "活动"))
mFragmentList.add(activityFragment)
@ -332,16 +263,8 @@ class CommunityHomeFragment : LazyFragment() {
adapter = FragmentAdapter(childFragmentManager, mFragmentList, mTitleList)
doOnScroll(
onPageSelected = { position ->
communityEditBtn.goneIf(position != TAB_RECOMMEND_INDEX && position != TAB_FOLLOW_INDEX)
communityEditBtn.goneIf(position != 0)
when (position) {
TAB_FOLLOW_INDEX -> {
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
topBg.translationY = 0F
changeNavigationBg()
NewLogUtils.logCommunityHomeEvent("click_for_you_tab")
SensorsBridge.trackCommunityTopTabSelected("关注")
}
TAB_RECOMMEND_INDEX -> {
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
topBg.translationY = 0F
@ -352,7 +275,7 @@ class CommunityHomeFragment : LazyFragment() {
TAB_FORUM_INDEX -> {
root.setBackgroundColor(R.color.ui_surface.toColor(requireContext()))
(mFragmentList[2] as ForumFragment).translationY.run {
(mFragmentList[1] as ForumFragment).translationY.run {
topBg.translationY = -this.toFloat()
changeNavigationBg(this)
}
@ -362,7 +285,7 @@ class CommunityHomeFragment : LazyFragment() {
TAB_ACTIVITY_INDEX -> {
root.setBackgroundColor(R.color.ui_background.toColor(requireContext()))
(mFragmentList[3] as ForumActivityFragment).translationY.run {
(mFragmentList[2] as ForumActivityFragment).translationY.run {
topBg.translationY = -this.toFloat()
changeNavigationBg(this)
}
@ -374,6 +297,8 @@ class CommunityHomeFragment : LazyFragment() {
onPageScrolled = { position, positionOffset, _ ->
if (position + 1 != mTabList.size) {
(mTabList[position] as TextView).run {
layoutParams.width =
(DEFAULT_TAB_TEXT_WIDTH + ((1 - positionOffset) * 4F.dip2px())).roundToInt()
textSize = (DEFAULT_TAB_TEXT_SIZE + ((1 - positionOffset) * 4)).roundTo(1)
setTextColor(
ColorUtils.blendARGB(
@ -385,6 +310,8 @@ class CommunityHomeFragment : LazyFragment() {
}
if (mTabList[position + 1] is TextView) {
(mTabList[position + 1] as TextView).run {
layoutParams.width =
(DEFAULT_TAB_TEXT_WIDTH + ((positionOffset) * 4F.dip2px())).roundToInt()
textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1)
setTextColor(
ColorUtils.blendARGB(
@ -396,12 +323,13 @@ class CommunityHomeFragment : LazyFragment() {
}
} else {
(mTabList[position + 1] as TabItemCommunityBinding).run {
val layoutParams = tabImg.layoutParams
layoutParams.width =
(DEFAULT_TAB_IMG_WIDTH + 8F.dip2px() * positionOffset).roundToInt()
layoutParams.height =
((DEFAULT_TAB_IMG_HEIGHT) + 4F.dip2px() * positionOffset).roundToInt()
tabImg.layoutParams = layoutParams
tabTitle.layoutParams.width =
(DEFAULT_TAB_TEXT_WIDTH + ((positionOffset) * 4F.dip2px())).roundToInt()
tabTitle.textSize = (DEFAULT_TAB_TEXT_SIZE + ((positionOffset) * 4)).roundTo(1)
tabImg.scaleX =
(DEFAULT_TAB_IMG_WIDTH + ((positionOffset) * 8)).roundTo(1) / DEFAULT_TAB_IMG_WIDTH
tabImg.scaleY =
(DEFAULT_TAB_IMG_HEIGHT + ((positionOffset) * 4)).roundTo(1) / DEFAULT_TAB_IMG_HEIGHT
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tabImg.imageTintList =
ColorStateList.valueOf(
@ -412,7 +340,6 @@ class CommunityHomeFragment : LazyFragment() {
)
)
}
}
}
@ -431,26 +358,20 @@ class CommunityHomeFragment : LazyFragment() {
}
)
}
tabLayout.addOnTabSelectedListener(obTabSelectedListener)
tabLayout.setupWithViewPager(viewPager)
indicatorView.run {
setupWithTabLayout(tabLayout)
setupWithViewPager(viewPager)
setIndicatorWidth(18)
}
val selectedPosition = if (UserManager.getInstance().isLoggedIn) {
TAB_FOLLOW_INDEX
} else {
TAB_RECOMMEND_INDEX
}
for (i in 0 until tabLayout.tabCount) {
val tab = tabLayout.getTabAt(i) ?: continue
val tabTitle = if (tab.text != null) tab.text.toString() else ""
val tabViewBinding = generateTabView(tabTitle, i, selectedPosition)
val tabViewBinding = generateTabView(tabTitle, i)
tab.customView = tabViewBinding.root
tab.view.setPadding(0, 0, 0, 0)
}
viewPager.setCurrentItem(selectedPosition, false)
}
}
@ -473,6 +394,7 @@ class CommunityHomeFragment : LazyFragment() {
if (index == selectedPosition) {
if (positionOffset == 0F) {
titleView.setTextColor(TAB_SELECTED_COLOR.toColor(requireContext()))
titleView.setTypeface(null, Typeface.NORMAL)
titleView.setTypeface(titleView.typeface, Typeface.BOLD)
} else if (positionOffset > 0F) {
titleView.setTypeface(null, Typeface.NORMAL)
@ -506,6 +428,9 @@ class CommunityHomeFragment : LazyFragment() {
} else {
tabImg.setImageResource(R.drawable.ic_tab_activity_active)
}
tabTitle.layoutParams.width = 64F.dip2px()
tabImg.scaleX = SCALE_TAB_IMG_WIDTH / DEFAULT_TAB_IMG_WIDTH
tabImg.scaleY = SCALE_TAB_IMG_HEIGHT / DEFAULT_TAB_IMG_HEIGHT
}
}
}
@ -519,22 +444,30 @@ class CommunityHomeFragment : LazyFragment() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tabImg.imageTintList = ColorStateList.valueOf(TAB_DEFAULT_COLOR.toColor(requireContext()))
}
tabTitle.layoutParams.width = DEFAULT_TAB_TEXT_WIDTH
tabImg.scaleX = 1F
tabImg.scaleY = 1F
}
}
}
private fun generateTabView(title: String, index: Int, selectedPosition: Int): TabItemCommunityBinding {
private fun generateTabView(title: String, index: Int): TabItemCommunityBinding {
val binding = TabItemCommunityBinding.inflate(LayoutInflater.from(requireContext()))
binding.run {
if (index == TAB_ACTIVITY_INDEX) {
mTabList.add(binding)
tabTitle.visibility = View.GONE
tabTitle.visibility = View.INVISIBLE
tabImg.visibility = View.VISIBLE
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
tabImg.setImageResource(R.drawable.ic_tab_activity_svg)
} else {
tabImg.setImageResource(R.drawable.ic_tab_activity_default)
}
tabTitle.run {
text = title
// textSize = DEFAULT_TAB_TEXT_SIZE
// setTextColor(TAB_DEFAULT_COLOR.toColor(requireContext()))
}
} else {
mTabList.add(tabTitle)
tabTitle.visibility = View.VISIBLE
@ -543,12 +476,13 @@ class CommunityHomeFragment : LazyFragment() {
text = title
textSize = DEFAULT_TAB_TEXT_SIZE
setTextColor(TAB_DEFAULT_COLOR.toColor(requireContext()))
if (index == TAB_FOLLOW_INDEX && selectedPosition == TAB_FOLLOW_INDEX) {
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
}
}
}
}
// binding.invisibleTabTitle.run {
// text = title
// textSize = DEFAULT_TAB_TEXT_SIZE
// }
return binding
}
@ -636,18 +570,6 @@ class CommunityHomeFragment : LazyFragment() {
}
}
private fun resetFollowTab() {
mBinding?.tabLayout?.run {
if (selectedTabPosition == TAB_FOLLOW_INDEX) {
mViewModel?.updateFilterResId(R.drawable.ic_follow_arrow_down)
} else {
mViewModel?.updateFilterResId(null)
}
}
mBinding?.tabLayout?.removeOnTabSelectedListener(obTabSelectedListener)
mBinding?.tabLayout?.addOnTabSelectedListener(obTabSelectedListener)
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data)
if (resultCode == Activity.RESULT_OK) {
@ -674,13 +596,13 @@ class CommunityHomeFragment : LazyFragment() {
}
private fun insertDataToRecommendTab(entity: ArticleEntity) {
(mFragmentList[TAB_RECOMMEND_INDEX] as? ForumArticleListFragment)?.insertDataToFirstIndex(entity)
(mFragmentList[0] as? ForumArticleListFragment)?.insertDataToFirstIndex(entity)
}
override fun onBackPressed(): Boolean {
mBinding?.viewPager?.run {
if (currentItem == 1) {
return (mFragmentList[1] as ForumArticleListFragment).onBackPressed()
if (currentItem == 0) {
return (mFragmentList[0] as ForumArticleListFragment).onBackPressed()
}
}
return super.onBackPressed()
@ -688,10 +610,7 @@ class CommunityHomeFragment : LazyFragment() {
@Subscribe(threadMode = ThreadMode.MAIN)
fun onEventMainThread(status: EBTypeChange) {
val currentPosition = mBinding?.viewPager?.currentItem ?: 0
if (currentPosition != TAB_FOLLOW_INDEX && currentPosition != TAB_RECOMMEND_INDEX) {
return
}
if (mBinding?.viewPager?.currentItem != 0) return
if (status.type == EB_SHOW_QUESTION_BUTTON) {
setPutQuestionButtonStatus(View.VISIBLE)
@ -717,14 +636,8 @@ class CommunityHomeFragment : LazyFragment() {
private fun scrollToTop() {
if (mFragmentList.isEmpty()) return
val currentPosition = mBinding?.viewPager?.currentItem ?: 0
val fragment = mFragmentList.getOrNull(currentPosition)
if (fragment is IScrollable) {
fragment.scrollToTop()
}
if (currentPosition == TAB_FOLLOW_INDEX || currentPosition == TAB_RECOMMEND_INDEX) {
setPutQuestionButtonStatus(View.VISIBLE)
}
(mFragmentList[0] as ForumArticleListFragment).scrollToTop()
setPutQuestionButtonStatus(View.VISIBLE)
}
@Subscribe(threadMode = ThreadMode.MAIN)
@ -758,7 +671,7 @@ class CommunityHomeFragment : LazyFragment() {
} else if (!mIsDarkModeOn && navigationBg.drawable != null) {
navigationBg.setBackgroundColor(R.color.transparent.toColor(requireContext()))
navigationBg.setImageDrawable(null)
} else if (viewPager.currentItem == TAB_RECOMMEND_INDEX || viewPager.currentItem == TAB_FOLLOW_INDEX) {
} else if (viewPager.currentItem == TAB_RECOMMEND_INDEX) {
navigationBg.setBackgroundColor(
if (mIsDarkModeOn && y > 0) R.color.ui_surface.toColor(requireContext()) else if (mIsDarkModeOn && y == 0) R.color.ui_background.toColor(
requireContext()
@ -779,20 +692,7 @@ class CommunityHomeFragment : LazyFragment() {
for (i in 0 until tabCount) {
val tab: TabLayout.Tab? = getTabAt(i)
if (tab != null) {
val tvTitle = tab.customView?.findViewById<TextView>(R.id.tab_title)
if (tvTitle != null && tvTitle.visibility == View.VISIBLE) {
tvTitle.post {
if (i == TAB_FOLLOW_INDEX && tab.isSelected) {
val resId = mViewModel?.followFilterStatus?.value
if (resId != null) {
mViewModel?.updateFilterResId(resId)
}
}
tvTitle.setTextColor(
ColorStateList.valueOf(R.color.text_primary.toColor(requireContext()))
)
}
}
updateTabStyle(mBinding?.viewPager?.currentItem ?: 0, 0f)
}
}
}
@ -817,21 +717,7 @@ class CommunityHomeFragment : LazyFragment() {
}
}
}
searchIconIv.setImageResource(R.drawable.ic_column_search)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
searchIconIv.imageTintList = ColorStateList.valueOf(R.color.text_primary.toColor(requireContext()))
}
val csl = AppCompatResources.getColorStateList(requireContext(), R.color.text_primary)
val filter = SimpleColorFilter(csl.defaultColor)
val keyPath = KeyPath("**")
val callback = LottieValueCallback<ColorFilter>(filter)
videoLottie.addValueCallback(keyPath, LottieProperty.COLOR_FILTER, callback)
}
followFilterPopWindow.onDarkModeChanged()
}
fun getTopBgView() = mBinding?.topBg
@ -839,14 +725,16 @@ class CommunityHomeFragment : LazyFragment() {
companion object {
var TAB_SELECTED_COLOR: Int = R.color.text_primary
var TAB_DEFAULT_COLOR: Int = R.color.community_forum_more
var DEFAULT_TAB_TEXT_SIZE = 16F
var DEFAULT_TAB_IMG_WIDTH = 34F.dip2px()
var DEFAULT_TAB_IMG_HEIGHT = 16F.dip2px()
var DEFAULT_TAB_TEXT_SIZE = 18F
var DEFAULT_TAB_TEXT_WIDTH = 60F.dip2px()
var DEFAULT_TAB_IMG_WIDTH = 34F
var DEFAULT_TAB_IMG_HEIGHT = 18F
var SCALE_TAB_IMG_WIDTH = 42F
var SCALE_TAB_IMG_HEIGHT = 22F
const val EB_TAB = "forum_tab"
const val TAB_FOLLOW_INDEX = 0
const val TAB_RECOMMEND_INDEX = 1
const val TAB_FORUM_INDEX = 2
const val TAB_ACTIVITY_INDEX = 3
const val TAB_RECOMMEND_INDEX = 0
const val TAB_FORUM_INDEX = 1
const val TAB_ACTIVITY_INDEX = 2
const val ARTICLE_REQUEST_CODE = 200
const val QUESTION_REQUEST_CODE = 201
const val VIDEO_REQUEST_CODE = 202

View File

@ -1,20 +1,19 @@
package com.gh.gamecenter.forum.home
import android.app.Application
import androidx.lifecycle.AndroidViewModel
import androidx.lifecycle.LiveData
import androidx.lifecycle.MediatorLiveData
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.*
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.TimeEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.livedata.Event
import com.gh.gamecenter.common.utils.clearHtmlFormatCompletely
import com.gh.gamecenter.common.utils.observableToMain
import com.gh.gamecenter.common.utils.removeInsertedContent
import com.gh.gamecenter.common.utils.removeVideoContent
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.feature.entity.TimeEntity
import com.gh.gamecenter.common.retrofit.Response
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.feature.entity.UserEntity
import com.gh.gamecenter.retrofit.RetrofitManager
class CommunityHomeViewModel(application: Application) : AndroidViewModel(application) {
@ -132,64 +131,4 @@ class CommunityHomeViewModel(application: Application) : AndroidViewModel(applic
return articleEntity
}
private val _hasFollowedUsers = MutableLiveData<Boolean>()
val hasFollowedUser: LiveData<Boolean> = _hasFollowedUsers
fun updateHasFollowUser(show: Boolean) {
_hasFollowedUsers.value = show
}
private val filterDrawableResId = MutableLiveData<Int?>()
fun updateFilterResId(resId: Int?) {
filterDrawableResId.value = resId
}
val followFilterStatus = MediatorLiveData<Int?>().apply {
addSource(hasFollowedUser) {
if (it) {
value = filterDrawableResId.value
} else {
value = null
}
}
addSource(filterDrawableResId) { resId ->
// 当 hasFollowedUser.value == null 时不需要调用setValue
if (hasFollowedUser.value == true) {
value = resId
} else if (hasFollowedUser.value == false) {
value = null
}
when {
resId == null ||
hasFollowedUser.value == false -> {
// 当resId == null 时表示已切换到别的tab 直接setValue
// 当 resId != null && hasFollowedUser,value 为false时说明关注页数据已加载完毕并且没有关注任何用户直接setValue
value = null
}
hasFollowedUser.value == true -> {
// 当 resId = null 且 有关注用户,显示当前 resId
value = resId
}
else -> {
// do Nothing 当resId != null 但是hasFollowedUser.value == null,说明关注页数据正在loading等待无需setValue
}
}
}
}
private val _filterFollowedAction = MutableLiveData<Event<Int>>()
val filterFollowedAction: LiveData<Event<Int>> = _filterFollowedAction
fun filterFollowed(position: Int) {
val filterName = when (position) {
0 -> "全部"
1 -> "关注的人"
else -> "关注的游戏"
}
SensorsBridge.trackFollowTabFilterOptionClick(filterName)
_filterFollowedAction.value = Event(position)
}
}

View File

@ -13,7 +13,6 @@ import com.gh.gamecenter.common.baselist.LoadStatus
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.SpacingItemDecoration
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.databinding.FragmentForumActivityBinding
import com.gh.gamecenter.databinding.LayoutForumActivityCategoryItemBinding
@ -21,7 +20,7 @@ import com.gh.gamecenter.entity.ForumActivityCategoryEntity
import com.gh.gamecenter.entity.ForumActivityEntity
import com.google.android.flexbox.FlexboxLayout
class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivityViewModel>(), IScrollable {
class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivityViewModel>() {
private var mBinding: FragmentForumActivityBinding? = null
private var mAdapter: ForumActivityAdapter? = null
@ -201,12 +200,4 @@ class ForumActivityFragment : LazyListFragment<ForumActivityEntity, ForumActivit
}
changeCategoryBg()
}
override fun scrollToTop() {
mBinding?.listRv?.let {
if (it.visibility == View.VISIBLE) {
it.scrollToPosition(0)
}
}
}
}

View File

@ -6,18 +6,13 @@ import android.graphics.Color
import android.graphics.drawable.GradientDrawable
import android.text.SpannableStringBuilder
import android.view.View
import com.facebook.drawee.view.SimpleDraweeView
import com.gh.common.util.*
import com.gh.common.util.DialogUtils
import com.gh.common.util.LogUtils
import com.gh.common.util.NewLogUtils
import com.gh.common.view.ImageContainerView
import com.gh.common.view.ImageContainerView.Companion.toImageContainerData
import com.gh.gamecenter.ImageViewerActivity
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.common.callback.ConfirmListener
import com.gh.gamecenter.common.entity.AdditionalParamsEntity
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.core.utils.MtaHelper
@ -33,7 +28,6 @@ import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.CommunityItemData
import com.gh.gamecenter.forum.home.ArticleItemVideoView.Companion.toArticleVideoData
import com.gh.gamecenter.forum.search.CommunitySearchEventListener
import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_COMMENT
import com.gh.gamecenter.forum.search.CommunitySearchEventListener.Companion.SEARCH_BUTTON_DISLIKE
@ -48,7 +42,6 @@ import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.qa.questions.invite.QuestionsInviteActivity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
import com.gh.gamecenter.video.detail.VideoDetailContainerViewModel
import com.shuyu.gsyvideoplayer.builder.GSYVideoOptionBuilder
import com.shuyu.gsyvideoplayer.listener.GSYSampleCallBack
import com.shuyu.gsyvideoplayer.utils.OrientationUtils
@ -155,46 +148,15 @@ class ForumArticleAskItemViewHolder(
}
}
binding.imageContainer.bindData(
entity.toImageContainerData(),
object : ImageContainerView.OnImageContainerEventListener {
override fun onImageClick(
images: List<String>,
position: Int,
imageViewList: ArrayList<SimpleDraweeView>,
) {
clickListener?.onItemChildClick(
entity.id,
entity.type,
htmlToString(entity.questions.title),
position,
SEARCH_BUTTON_VIEW_IMAGE,
)
if (entity.communityId.isNullOrEmpty()) {
entity.communityId = entity.bbs.id
}
val intent = ImageViewerActivity.getIntent(
itemView.context, entity.images as ArrayList<String>, position, imageViewList,
if (entity.type == "community_article") entity else null, entrance, true,
)
itemView.context.startActivity(intent)
}
override fun onVideoCLick(videoId: String) {
DirectUtils.directToVideoDetail(
itemView.context,
videoId,
VideoDetailContainerViewModel.Location.VIDEO_HOT.value,
showComment = false,
entrance = entrance,
path = path
)
}
},
)
binding.imageContainer.bindData(entity, entrance, path) {
clickListener?.onItemChildClick(
entity.id,
entity.type,
htmlToString(entity.questions.title),
position,
SEARCH_BUTTON_VIEW_IMAGE
)
}
bindVideoData(entity.transformForumVideoEntity())
val user = entity.user
@ -353,13 +315,34 @@ class ForumArticleAskItemViewHolder(
.setVideoAllCallBack(object : GSYSampleCallBack() {
override fun onQuitFullscreen(url: String?, vararg objects: Any) {
orientationUtils.backToProtVideo()
visibleView.uploadVideoStreamingPlaying("退出全屏")
}
})
.build(visibleView)
visibleView.updateVideoData(
entity.toArticleVideoData(),
AnswerArticleVideoViewEventHelper(entity, orientationUtils)
)
visibleView.run {
updateVideoData(entity)
updateThumb(entity.poster)
updateDurationTv(entity.duration)
setVideoStatus(entity.status)
fullscreenButton.setOnClickListener {
val horizontalVideoView =
startWindowFullscreen(itemView.context, true, true) as? ArticleItemVideoView
if (horizontalVideoView == null) {
toastInInternalRelease("全屏失败,请向技术人员提供具体的操作步骤")
return@setOnClickListener
}
orientationUtils.resolveByClick()
horizontalVideoView.uuid = uuid
horizontalVideoView.updateVideoData(entity)
horizontalVideoView.updateThumb(entity.poster)
horizontalVideoView.violenceUpdateMuteStatus()
horizontalVideoView.setFullViewStatus()
uploadVideoStreamingPlaying("开始播放")
uploadVideoStreamingPlaying("点击全屏")
}
}
}
}
}
@ -399,13 +382,7 @@ class ForumArticleAskItemViewHolder(
SEARCH_BUTTON_VIEW_FORUM_DETAIL
)
MtaHelper.onEvent(getEventId(entrance), getKey(entrance), entity.community.name)
itemView.context.startActivity(
ForumDetailActivity.getIntent(
itemView.context,
entity.community.id,
entrance
)
)
itemView.context.startActivity(ForumDetailActivity.getIntent(itemView.context, entity.community.id, entrance))
LogUtils.uploadAccessToBbs(entity.community.id, "文章外所属论坛")
}

View File

@ -22,7 +22,6 @@ import com.gh.gamecenter.common.base.fragment.LazyFragment
import com.gh.gamecenter.common.constant.Constants
import com.gh.gamecenter.common.utils.*
import com.gh.gamecenter.common.view.GridSpacingItemColorDecoration
import com.gh.gamecenter.core.iinterface.IScrollable
import com.gh.gamecenter.databinding.ForumBannerIndicatorItemBinding
import com.gh.gamecenter.databinding.FragmentForumBinding
import com.gh.gamecenter.entity.ForumBannerEntity
@ -35,7 +34,7 @@ import com.halo.assistant.HaloApp
import org.greenrobot.eventbus.Subscribe
import org.greenrobot.eventbus.ThreadMode
class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener, IScrollable {
class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener {
private var mBinding: FragmentForumBinding? = null
private var mViewModel: ForumViewModel? = null
@ -514,12 +513,4 @@ class ForumFragment : LazyFragment(), SwipeRefreshLayout.OnRefreshListener, IScr
super.onDarkModeChanged()
mBinding?.hotForumRv?.adapter?.let { it.notifyItemRangeChanged(0, it.itemCount) }
}
override fun scrollToTop() {
mBinding?.contentContainer?.let {
if (it.visibility == View.VISIBLE) {
it.scrollTo(0, 0)
}
}
}
}

View File

@ -1,31 +0,0 @@
package com.gh.gamecenter.forum.home.follow
import android.content.Context
import android.content.Intent
import android.graphics.Color
import android.os.Bundle
import com.gh.gamecenter.R
import com.gh.gamecenter.common.base.activity.BaseActivity
import com.gh.gamecenter.core.utils.DisplayUtils
import com.gh.gamecenter.forum.home.follow.fragment.AllFollowedFragment
class AllFollowedActivity : BaseActivity() {
override fun getLayoutId(): Int = R.layout.activity_all_followed
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
DisplayUtils.setLightStatusBar(this, true)
setStatusBarColor(Color.TRANSPARENT)
val containerFragment = supportFragmentManager.findFragmentByTag(AllFollowedFragment::class.java.name)
?: AllFollowedFragment().with(intent.extras)
// 若 placeholder 外层为 RelativeLayout 的话,会出现莫名的偏移
supportFragmentManager.beginTransaction()
.replace(R.id.layout_activity_content, containerFragment, AllFollowedFragment::class.java.name)
.commitAllowingStateLoss()
}
companion object {
fun getIntent(context: Context): Intent = Intent(context, AllFollowedActivity::class.java)
}
}

View File

@ -1,50 +0,0 @@
package com.gh.gamecenter.forum.home.follow
import com.gh.gamecenter.entity.FollowUserEntity
abstract class AllFollowedItem() {
abstract val itemType: Int
open fun areItemsTheSame(other: AllFollowedItem): Boolean {
return other.itemType == itemType && doAreItemsTheSame(other)
}
open fun areContentsTheSame(other: AllFollowedItem): Boolean {
return other.itemType == itemType && doAreContentsTheSame(other)
}
open fun doAreItemsTheSame(other: AllFollowedItem): Boolean = true
open fun doAreContentsTheSame(other: AllFollowedItem): Boolean = this == other
companion object {
const val ALL_FOLLOWED_ITEM_TYPE_NO_TOP = 0
const val ALL_FOLLOWED_ITEM_TYPE_NORMAL = 1
const val ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER = 2
}
}
class AllFollowedNoTopItem : AllFollowedItem() {
override val itemType: Int
get() = ALL_FOLLOWED_ITEM_TYPE_NO_TOP
}
data class AllFollowedMyFollowedHeaderItem(val isTop: Boolean) : AllFollowedItem() {
override val itemType: Int
get() = ALL_FOLLOWED_ITEM_TYPE_MY_FOLLOW_HEADER
}
data class AllFollowedNormalItem(
val data: FollowUserEntity,
var isTop: Boolean
) : AllFollowedItem() {
override val itemType: Int
get() = ALL_FOLLOWED_ITEM_TYPE_NORMAL
override fun doAreItemsTheSame(other: AllFollowedItem): Boolean {
return other is AllFollowedNormalItem && data.id == other.data.id
}
}

View File

@ -1,306 +0,0 @@
package com.gh.gamecenter.forum.home.follow
import android.content.Context
import android.content.Intent
import androidx.activity.result.ActivityResultLauncher
import androidx.activity.result.ActivityResultRegistry
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.result.contract.ActivityResultContracts.GetContent
import androidx.lifecycle.DefaultLifecycleObserver
import androidx.lifecycle.LifecycleOwner
import com.gh.common.util.SyncDataBetweenPageHelper
import com.gh.gamecenter.common.entity.CommunityEntity
import com.gh.gamecenter.entity.PersonalHistoryEntity
import com.gh.gamecenter.feature.entity.AnswerEntity
import com.gh.gamecenter.feature.entity.ArticleEntity
import com.gh.gamecenter.feature.entity.CommentEntity
import com.gh.gamecenter.feature.entity.ForumVideoEntity
import com.gh.gamecenter.personalhome.home.UserHistoryFragment
import com.gh.gamecenter.qa.article.detail.ArticleDetailActivity
import com.gh.gamecenter.qa.entity.ArticleDetailEntity
import com.gh.gamecenter.qa.entity.QuestionsDetailEntity
import com.gh.gamecenter.qa.questions.newdetail.NewQuestionDetailActivity
import com.gh.gamecenter.qa.video.detail.ForumVideoDetailActivity
class FollowActivityResultLauncher(
private val registry: ActivityResultRegistry,
private val onResultListener: OnResultListener
) : DefaultLifecycleObserver {
private lateinit var articleDetailLauncher: ActivityResultLauncher<LauncherDestination>
private lateinit var commentEntityLauncher: ActivityResultLauncher<LauncherDestination>
private lateinit var questionsDetailLauncher: ActivityResultLauncher<LauncherDestination>
private lateinit var forumVideoLauncher: ActivityResultLauncher<LauncherDestination>
override fun onCreate(owner: LifecycleOwner) {
super.onCreate(owner)
articleDetailLauncher = registry.register(
KEY_ARTICLE_DETAIL_LAUNCHER,
owner,
object : ActivityResultContract<LauncherDestination, ArticleDetailResult?>() {
override fun createIntent(context: Context, input: LauncherDestination): Intent {
val intent =
if (input.historyEntity != null) {
ArticleDetailActivity.getIntent(
context,
input.historyEntity.community,
input.historyEntity.id,
"",
UserHistoryFragment.PATH_USER_QUESTION_ANSWER,
sourceEntrance = "关注-用户动态"
)
} else {
ArticleDetailActivity.getIntent(
context,
CommunityEntity(input.answerEntity?.bbs?.id ?: ""),
input.answerEntity?.id ?: "",
"",
"",
sourceEntrance = "关注-论坛动态",
)
}
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 101)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): ArticleDetailResult? {
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
val resultData =
intent?.getParcelableExtra<ArticleDetailEntity>(ArticleDetailEntity::class.java.simpleName)
if (position != null && resultData != null) {
return ArticleDetailResult(position, resultData)
} else {
return null
}
}
})
{
it?.run {
onResultListener.onArticleDetailResult(position, articleDetailEntity)
}
}
commentEntityLauncher = registry.register(
KEY_COMMENT_LAUNCHER,
owner,
object : ActivityResultContract<LauncherDestination, CommentResult?>() {
override fun createIntent(context: Context, input: LauncherDestination): Intent {
val intent = NewQuestionDetailActivity.getSpecifiedCommentIntent(
context,
input.historyEntity?.question?.id ?: "",
input.historyEntity?.id ?: "",
"",
UserHistoryFragment.PATH_USER_QUESTION_ANSWER,
sourceEntrance = "关注-动态"
)
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 102)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): CommentResult? {
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
val commentEntity = intent?.getParcelableExtra<CommentEntity>(CommentEntity::class.java.simpleName)
if (position != null && commentEntity != null) {
return CommentResult(position, commentEntity)
} else {
return null
}
}
})
{
it?.run {
onResultListener.onCommentResult(position, commentEntity)
}
}
questionsDetailLauncher = registry.register(
KEY_QUESTION_DETAIL_LAUNCHER,
owner,
object : ActivityResultContract<LauncherDestination, QuestionsDetailResult?>() {
override fun createIntent(context: Context, input: LauncherDestination): Intent {
val intent = if (input.historyEntity != null) {
NewQuestionDetailActivity.getIntent(
context,
input.historyEntity.id,
"",
UserHistoryFragment.PATH_USER_QUESTION_ANSWER
)
} else {
if (input.answerEntity?.type == "question") {
NewQuestionDetailActivity.getIntent(
context, input.answerEntity.id,
"",
"",
sourceEntrance = "关注-论坛动态"
)
} else {
NewQuestionDetailActivity.getCommentIntent(
context,
input.answerEntity?.questions?.id ?: "",
input.answerEntity?.answerId ?: "",
"",
""
)
}
}
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 103)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): QuestionsDetailResult? {
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
val questionsDetailEntity =
intent?.getParcelableExtra<QuestionsDetailEntity>(QuestionsDetailEntity::class.java.simpleName)
return if (position != null && questionsDetailEntity != null) {
QuestionsDetailResult(position, questionsDetailEntity)
} else {
null
}
}
}
) {
it?.run {
onResultListener.onQuestionsDetailResult(position, questionsDetailEntity)
}
}
forumVideoLauncher = registry.register(
KEY_FORUM_VIDEO_LAUNCHER,
owner,
object : ActivityResultContract<LauncherDestination, ForumVideoResult?>() {
override fun createIntent(context: Context, input: LauncherDestination): Intent {
val intent = if (input.historyEntity != null) {
ForumVideoDetailActivity.getIntent(
context,
input.historyEntity.id,
input.historyEntity.community.id,
sourceEntrance = "关注-个人动态"
)
} else {
ForumVideoDetailActivity.getIntent(
context,
input.answerEntity?.id ?: "",
input.answerEntity?.bbs?.id ?: "",
"关注-论坛动态"
)
}
intent.putExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, input.position)
intent.putExtra(SyncDataBetweenPageHelper.REQUEST_CODE_TAG, 104)
return intent
}
override fun parseResult(resultCode: Int, intent: Intent?): ForumVideoResult? {
val position = intent?.getIntExtra(SyncDataBetweenPageHelper.DATA_POSITION_TAG, -1)
val forumVideoEntity =
intent?.getParcelableExtra<ForumVideoEntity>(ForumVideoEntity::class.java.simpleName)
return if (position != null && forumVideoEntity != null) {
ForumVideoResult(position, forumVideoEntity)
} else {
null
}
}
}
)
{
it?.run {
onResultListener.onForumVideoResult(position, forumVideoEntity)
}
}
}
fun onPersonItemClick(position: Int, entity: PersonalHistoryEntity) {
when {
entity.type == "community_article"
|| entity.type == "community_article_vote" -> {
articleDetailLauncher.launch(LauncherDestination(position, historyEntity = entity))
}
entity.type.contains("video") -> {
forumVideoLauncher.launch(LauncherDestination(position, historyEntity = entity))
}
entity.type.contains("question") -> {
questionsDetailLauncher.launch(LauncherDestination(position, historyEntity = entity))
}
else -> {
commentEntityLauncher.launch(LauncherDestination(position, historyEntity = entity))
}
}
}
fun onBbsItemClick(position: Int, answerEntity: AnswerEntity) {
when (answerEntity.type) {
"community_article" -> {
articleDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
}
"video" -> {
forumVideoLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
}
"question" -> {
questionsDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
}
"answer" -> {
questionsDetailLauncher.launch(LauncherDestination(position, answerEntity = answerEntity))
}
}
}
companion object {
private const val KEY_ARTICLE_DETAIL_LAUNCHER = "key_article_detail_launcher"
private const val KEY_COMMENT_LAUNCHER = "key_comment_launcher"
private const val KEY_QUESTION_DETAIL_LAUNCHER = "key_question_detail_launcher"
private const val KEY_FORUM_VIDEO_LAUNCHER = "KEY_forum_video_LAUNCHER"
}
private data class LauncherDestination(
val position: Int,
val historyEntity: PersonalHistoryEntity? = null,
val answerEntity: AnswerEntity? = null
)
private data class ArticleDetailResult(
val position: Int,
val articleDetailEntity: ArticleDetailEntity
)
private data class CommentResult(
val position: Int,
val commentEntity: CommentEntity
)
private data class QuestionsDetailResult(
val position: Int,
val questionsDetailEntity: QuestionsDetailEntity
)
private data class ForumVideoResult(
val position: Int,
val forumVideoEntity: ForumVideoEntity
)
interface OnResultListener {
fun onArticleDetailResult(position: Int, articleDetailEntity: ArticleDetailEntity)
fun onCommentResult(position: Int, commentEntity: CommentEntity)
fun onQuestionsDetailResult(position: Int, questionsDetailEntity: QuestionsDetailEntity)
fun onForumVideoResult(position: Int, forumVideoEntity: ForumVideoEntity)
}
}

View File

@ -1,32 +0,0 @@
package com.gh.gamecenter.forum.home.follow
import android.content.Context
import android.graphics.Canvas
import android.graphics.Path
import android.util.AttributeSet
import androidx.core.content.ContextCompat
import com.gh.gamecenter.R
import com.gh.gamecenter.common.utils.dip2px
import com.gh.gamecenter.core.utils.DisplayUtils
class FollowClippedView @JvmOverloads constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0
) : androidx.appcompat.widget.AppCompatImageView(context, attrs, defStyleAttr) {
override fun onDraw(canvas: Canvas) {
val path = Path()
path.addRect(
0f,
0f,
width.toFloat(),
(DisplayUtils.getStatusBarHeight(resources) + 52f.dip2px() + 56f.dip2px()).toFloat(),
Path.Direction.CW
)
canvas.clipPath(path)
canvas.drawColor(ContextCompat.getColor(context, R.color.ui_background))
super.onDraw(canvas)
}
}

Some files were not shown because too many files have changed in this diff Show More