From 64449ec38bd6d24358fe84a4f85834d43ed8a32d Mon Sep 17 00:00:00 2001 From: chenjuntao Date: Tue, 25 Jul 2023 14:35:59 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E5=B9=BF=E5=91=8A?= =?UTF-8?q?=E4=BD=8D=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD=20https://jira.sh?= =?UTF-8?q?anqu.cc/browse/GHZS-2919?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../main/java/com/gh/ad/AdDelegateHelper.kt | 399 +++++++++++++++++- .../java/com/gh/common/FixedRateJobHelper.kt | 5 - .../java/com/gh/common/constant/Config.java | 1 - .../main/java/com/gh/common/util/AdHelper.kt | 33 -- .../java/com/gh/common/util/DirectUtils.kt | 47 --- .../java/com/gh/gamecenter/MainActivity.java | 301 +++---------- .../com/gh/gamecenter/SplashScreenActivity.kt | 19 +- .../gamecenter/download/DownloadFragment.kt | 22 + .../java/com/gh/gamecenter/entity/AdConfig.kt | 35 ++ .../gamecenter/entity/NewApiSettingsEntity.kt | 2 - .../gh/gamecenter/entity/StartupAdEntity.kt | 3 +- .../com/gh/gamecenter/home/HomeFragment.kt | 6 +- .../receiver/NetworkStateReceiver.java | 2 + .../retrofit/service/ApiService.java | 13 +- .../search/SearchGameIndexAdapter.kt | 5 - .../search/SearchGameIndexFragment.kt | 15 +- .../search/SearchGameResultAdapter.kt | 59 ++- .../search/SearchGameResultFragment.kt | 8 +- .../search/SearchGameResultViewModel.kt | 130 ++++-- .../gh/gamecenter/search/SearchItemData.kt | 2 + .../main/java/com/halo/assistant/HaloApp.java | 3 + .../main/res/layout/search_game_ad_item.xml | 12 + .../gh/gamecenter/beiziad/BeiziAdHelper.kt | 18 +- .../gamecenter/beiziad/BeiziAdProviderImpl.kt | 25 +- feature/csj_ad/proguard-rules.pro | 8 +- .../java/com/gh/gamecenter/csj/CsjAdHelper.kt | 246 ++++++++++- .../gh/gamecenter/csj/CsjAdProviderImpl.kt | 44 +- .../view/help/QaFeedbackDialogFragment.kt | 6 +- module_common/build.gradle | 1 - .../common/base/activity/BaseActivity.java | 31 +- .../common/base/fragment/BaseFragment.java | 11 +- .../gamecenter/common/utils/DarkModeUtils.kt | 44 +- .../gh/gamecenter/common/utils/Extensions.kt | 86 ++-- .../src/main/res/layout/fragment_download.xml | 53 +++ .../gamecenter/core/provider/IAdProvider.kt | 26 -- .../core/provider/IBeiziAdProvider.kt | 23 + .../core/provider/ICsjAdProvider.kt | 46 ++ .../gamecenter/core/utils/DisplayUtils.java | 7 + .../view/help/QaFeedbackDialogFragment.kt | 7 +- 39 files changed, 1188 insertions(+), 616 deletions(-) create mode 100644 app/src/main/java/com/gh/gamecenter/entity/AdConfig.kt create mode 100644 app/src/main/res/layout/search_game_ad_item.xml create mode 100644 module_common/src/main/res/layout/fragment_download.xml delete mode 100644 module_core/src/main/java/com/gh/gamecenter/core/provider/IAdProvider.kt create mode 100644 module_core/src/main/java/com/gh/gamecenter/core/provider/IBeiziAdProvider.kt create mode 100644 module_core/src/main/java/com/gh/gamecenter/core/provider/ICsjAdProvider.kt diff --git a/app/src/main/java/com/gh/ad/AdDelegateHelper.kt b/app/src/main/java/com/gh/ad/AdDelegateHelper.kt index fa55536a24..635b3be089 100644 --- a/app/src/main/java/com/gh/ad/AdDelegateHelper.kt +++ b/app/src/main/java/com/gh/ad/AdDelegateHelper.kt @@ -1,49 +1,245 @@ package com.gh.ad +import android.annotation.SuppressLint import android.app.Activity import android.content.Context +import android.content.SharedPreferences +import android.text.TextUtils import android.view.View import android.view.ViewGroup import android.widget.FrameLayout +import android.widget.TextView +import androidx.appcompat.content.res.AppCompatResources +import androidx.fragment.app.Fragment import com.alibaba.android.arouter.launcher.ARouter +import com.facebook.drawee.view.SimpleDraweeView +import com.gh.common.exposure.ExposureManager +import com.gh.common.util.DirectUtils.directToLinkPage +import com.gh.common.util.LogUtils +import com.gh.common.util.NewFlatLogUtils.logOpenScreenAdSkip +import com.gh.gamecenter.MainActivity +import com.gh.gamecenter.R +import com.gh.gamecenter.common.base.activity.BaseActivity +import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.core.provider.IAdProvider +import com.gh.gamecenter.common.retrofit.BiResponse +import com.gh.gamecenter.common.utils.* +import com.gh.gamecenter.core.provider.IBeiziAdProvider +import com.gh.gamecenter.core.provider.ICsjAdProvider +import com.gh.gamecenter.core.utils.CurrentActivityHolder +import com.gh.gamecenter.core.utils.SPUtils +import com.gh.gamecenter.core.utils.TimeUtils.getToday +import com.gh.gamecenter.entity.AdConfig +import com.gh.gamecenter.entity.StartupAdEntity +import com.gh.gamecenter.feature.exposure.ExposureEvent.Companion.createEvent +import com.gh.gamecenter.feature.exposure.ExposureSource +import com.gh.gamecenter.feature.exposure.ExposureType +import com.gh.gamecenter.retrofit.RetrofitManager +import com.halo.assistant.HaloApp +import io.reactivex.schedulers.Schedulers /** - * 广告代理类 + * 广告实现代理类 * - * 由它来分发功能实现到具体的 SDK + * 由它来分发功能实现到具体的实现 + * + * 以最复杂的开屏广告为例,有三种实现(1. 自有的广告实现 2. 穿山甲的开屏广告实现 3. Beizi 的开屏广告实现) + * + * 由于两个广告 SDK 有可能在一次启动中都被使用,所以会根据获取到的广告配置 config 来决定是否需要出是很好两个 SDK */ object AdDelegateHelper { - private var mActivatedSdkType = 1 - private var mAdImpl: IAdProvider? = null + private var mCsjAdImpl: ICsjAdProvider? = null + private var mBeiziAdImpl: IBeiziAdProvider? = null - // TODO 记录广告相关的 config + private var mAdConfigList: ArrayList? = null - private const val TYPE_BEIZI = 0 - private const val TYPE_CSJ = 1 + private var mSplashAd: AdConfig? = null + private var mDownloadManagerAd: AdConfig? = null + private val mGameSearchAdList: ArrayList by lazy { arrayListOf() } + private var mVGameLaunchAd: AdConfig? = null + + private var mIsCsjRequired: Boolean = false // 是否需要初始化穿山甲 SDK + private var mIsBeiziRequired: Boolean = false // 师傅需要初始化 Beizi SDK + + private const val AD_SDK_CSJ = "穿山甲" + private const val AD_SDK_BEIZI = "倍孜" + private const val AD_TYPE_SDK = "third_party_ads" // 第三方 SDK 广告 + + private const val KEY_CACHE_CONFIG = "cache_config" // 放在 SP 里的广告缓存(避免接口加载问题) + + private val mAdConfigSp: SharedPreferences by lazy { + HaloApp.getInstance().getSharedPreferences("AdConfig", Context.MODE_PRIVATE) + } /** - * 初始化 + * 请求接口获取广告相关配置 */ - fun init(context: Context) { - mAdImpl = when (mActivatedSdkType) { - TYPE_BEIZI -> ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? IAdProvider - - TYPE_CSJ -> ARouter.getInstance().build(RouteConsts.provider.csjAd).navigation() as? IAdProvider - - else -> null + @SuppressLint("CheckResult") + fun requestAdConfig(context: Context, isFromRetry: Boolean) { + // mAdConfigList 不为空不需要重试 + if (isFromRetry && mAdConfigList != null) { + return } - mAdImpl?.initSDK(context) + + RetrofitManager.getInstance() + .newApi + .adConfig + .subscribeOn(Schedulers.io()) + .subscribe(object : BiResponse>() { + override fun onSuccess(data: List) { + handleAdConfig(context, data) + + // 缓存数据到 SP 供接口请求失败用 + SPUtils.setString(mAdConfigSp, KEY_CACHE_CONFIG, data.toJson()) + } + + override fun onFailure(exception: Exception) { + super.onFailure(exception) + + // 若接口请求失败时,从 SP 里获取上次缓存的数据 + val cachedConfig: List? = SPUtils.getString(mAdConfigSp, KEY_CACHE_CONFIG).toObject() + if (cachedConfig != null) { + handleAdConfig(context, cachedConfig) + } + } + }) } + /** + * 获取搜索页的广告列表 + */ + fun getGameSearchAdList(): ArrayList { + return mGameSearchAdList + } + + /** + * 获取下载管理页的广告 + */ + fun getDownloadManagerAd(): AdConfig? { + return mDownloadManagerAd + } + + /** + * 处理广告配置 + */ + fun handleAdConfig(context: Context, configList: List) { + for (config in configList) { + // 处理返回的数据 + when (config.location) { + "halo_launch" -> mSplashAd = config + "download_manager" -> mDownloadManagerAd = config + "game_search" -> config.let { mGameSearchAdList.add(it) } + "helper_launch" -> mVGameLaunchAd = config + } + + // 根据返回的值里判断是否含有 Beizi 的广告 + if (!mIsBeiziRequired) { + if (config.thirdPartyAd?.sourceName == AD_SDK_BEIZI) { + mIsBeiziRequired = true + } + } + + // 根据返回的值里判断是否含有 穿山甲 的广告 + if (!mIsCsjRequired) { + if (config.thirdPartyAd?.sourceName == AD_SDK_CSJ) { + mIsCsjRequired = true + } + } + } + + // 初始化 Beizi + if (mIsBeiziRequired && mBeiziAdImpl == null) { + mBeiziAdImpl = + ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? IBeiziAdProvider + mBeiziAdImpl?.initSDK(context) + } + + // 初始化穿山甲 + if (mIsCsjRequired && mCsjAdImpl == null) { + mCsjAdImpl = + ARouter.getInstance().build(RouteConsts.provider.csjAd).navigation() as? ICsjAdProvider + mCsjAdImpl?.initSDK(context, HaloApp.getInstance().oaid) + // 监听亮色/暗色模式切换 + DarkModeUtils.registerModeChangeListener { + val topActivity = CurrentActivityHolder.getCurrentActivity() ?: return@registerModeChangeListener + updateThemeStatus(context, DarkModeUtils.isDarkModeOn(topActivity)) + } + } + } + + /** + * 是否需要显示开屏广告 + */ fun shouldShowStartUpAd(): Boolean { - return true + return mSplashAd != null } + /** + * 更新主题样式 + */ + private fun updateThemeStatus(context: Context, isDarkMode: Boolean) { + mCsjAdImpl?.updateThemeStatus(context, isDarkMode) + } + + /** + * 请求开屏广告 + */ @JvmStatic - fun requestStartUpAd( + fun requestSplashAd( + activity: Activity, + adViewWidthInPx: Int, + adViewHeightInPx: Int, + adViewWidthInDp: Float, + adViewHeightInDp: Float, + startAdContainer: ViewGroup, + sdkStartAdContainer: ViewGroup, + adsViewGroup: FrameLayout, + handler: BaseActivity.BaseHandler, + hideCallback: () -> Unit + ) { + if (mSplashAd != null) { + if (mSplashAd!!.displayRule.adSource == AD_TYPE_SDK) { + // 第三方开屏广告回调,失败时根据接口配置选项决定是否显示自有开屏广告 + val sdkSplashCallback: (isSuccess: Boolean) -> Unit = { isSuccess -> + if (isSuccess) { + hideCallback.invoke() + } else { + if (mSplashAd?.displayRule?.onFailedAction == "show") { + sdkStartAdContainer.visibility = View.GONE + requestStandardSplashAd(mSplashAd!!.ownerAd, startAdContainer, handler, hideCallback) + } else { + hideCallback.invoke() + } + } + } + + if (mSplashAd?.thirdPartyAd?.sourceName == AD_SDK_BEIZI) { + sdkStartAdContainer.visibility = View.VISIBLE + requestBeiziSplashAd(sdkStartAdContainer, adsViewGroup, adViewWidthInPx, adViewHeightInPx, sdkSplashCallback) + } else if (mSplashAd?.thirdPartyAd?.sourceName == AD_SDK_CSJ) { + sdkStartAdContainer.visibility = View.VISIBLE + requestCsjSplashAd( + activity, + mSplashAd?.thirdPartyAd?.slotId ?: "unknown", + adViewWidthInPx, + adViewHeightInPx, + adViewWidthInDp, + adViewHeightInDp, + sdkStartAdContainer, + sdkSplashCallback + ) + } + } else { + requestStandardSplashAd(mSplashAd!!.ownerAd, startAdContainer, handler, hideCallback) + } + } + } + + /** + * 获取穿山甲的开屏广告 + */ + private fun requestCsjSplashAd( activity: Activity, slotId: String, adViewWidthInPx: Int, @@ -51,9 +247,9 @@ object AdDelegateHelper { adViewWidthInDp: Float, adViewHeightInDp: Float, startAdContainer: ViewGroup, - hideCallback: () -> Unit + callback: (isSuccess: Boolean) -> Unit, ) { - mAdImpl?.requestStartUpAd( + mCsjAdImpl?.requestSplashAd( activity, slotId, adViewWidthInPx, @@ -61,12 +257,169 @@ object AdDelegateHelper { adViewWidthInDp, adViewHeightInDp, startAdContainer, - hideCallback + callback, ) } - fun cancelStartUpAd(context: Context) { + /** + * 获取 Beizi 的开屏广告 + */ + private fun requestBeiziSplashAd( + startAdContainer: View, + adsFl: FrameLayout, + adViewWidthInPx: Int, + adViewHeightInPx: Int, + callback: (isSuccess: Boolean) -> Unit, + ) { + mBeiziAdImpl?.requestSplashAd(startAdContainer, adsFl, adViewWidthInPx, adViewHeightInPx, callback) + } + /** + * 显示自有的开屏广告 + */ + private fun requestStandardSplashAd( + splashAd: StartupAdEntity, + startAdContainer: ViewGroup, + handler: BaseActivity.BaseHandler, + hideCallback: () -> Unit + ) { + if (!TextUtils.isEmpty(splashAd.img)) { + val showedTodayTimestamp = SPUtils.getString(Constants.SP_STARTUP_AD_TIMESTAMP, "") ?: "" + when (splashAd.rule) { + "each" -> showStandardSplashAd(splashAd, startAdContainer, handler, hideCallback) + "once" -> if (TextUtils.isEmpty(showedTodayTimestamp) + || !showedTodayTimestamp.contains(splashAd.id) + ) { + showStandardSplashAd(splashAd, startAdContainer, handler, hideCallback) + } else { + hideCallback.invoke() + } + + "everyday" -> { + val today = getToday() + if (TextUtils.isEmpty(showedTodayTimestamp) + || !showedTodayTimestamp.contains(today) + || !showedTodayTimestamp.contains(splashAd.id) + ) { + showStandardSplashAd(splashAd, startAdContainer, handler, hideCallback) + } else { + hideCallback.invoke() + } + } + + else -> hideCallback.invoke() + } + SPUtils.setString(Constants.SP_STARTUP_AD_TIMESTAMP, splashAd.id + getToday()) + } else { + hideCallback.invoke() + } + } + + private fun showStandardSplashAd( + ad: StartupAdEntity, + startAdContainer: ViewGroup, + handler: BaseActivity.BaseHandler, + hideCallback: () -> Unit + ) { + val jumpBtn: View = startAdContainer.findViewById(R.id.jumpBtn) + val jumpDetailBtn: TextView = startAdContainer.findViewById(R.id.jumpDetailBtn) + val adImage: SimpleDraweeView = startAdContainer.findViewById(R.id.adImage) + startAdContainer.visibility = View.VISIBLE + jumpDetailBtn.text = ad.desc + jumpDetailBtn.setDrawableEnd( + AppCompatResources.getDrawable( + startAdContainer.context, + R.drawable.ic_startup_ad_arrow + ), null, null + ) + ImageUtils.display(adImage, ad.img) + startAdContainer.setOnClickListener { + // 拦截点击事件传递 + } + jumpBtn.setOnClickListener { + handler.removeMessages(MainActivity.COUNTDOWN_AD) + hideCallback.invoke() + val linkEntity = ad.jump + logOpenScreenAdSkip( + ad.id, + (if (linkEntity.text != null) linkEntity.text else "")!!, + (if (linkEntity.type != null) linkEntity.type else "")!!, + (if (linkEntity.link != null) linkEntity.link else "")!! + ) + } + val sources: MutableList = ArrayList() + sources.add(ExposureSource("开屏广告", ad.id)) + val event = createEvent(null, sources, null, ExposureType.EXPOSURE) + ExposureManager.log(event) + if (ad.button) { + jumpDetailBtn.setOnClickListener { v: View -> + directToLinkPage(v.context, ad.jump, "(启动广告)", "", event) + v.postDelayed({ + handler.removeMessages(MainActivity.COUNTDOWN_AD) + hideCallback.invoke() + }, 1000) + } + jumpDetailBtn.visibility = View.VISIBLE + LogUtils.logStartAd("watch_start_ads", ad) + } else { + LogUtils.logStartAd("start_ads", ad) + } + handler.sendEmptyMessageDelayed(MainActivity.COUNTDOWN_AD, 1000) + } + + /** + * 获取信息流广告 + */ + fun requestFlowAd( + fragment: Fragment, + slotId: String, + adContainerView: ViewGroup, + expressViewWidth: Float, + callback: (isSuccess: Boolean) -> Unit, + ) { + mCsjAdImpl?.requestFlowAd(fragment, adContainerView, slotId, expressViewWidth, callback) + } + + /** + * 获取 Banner 广告 + */ + fun requestBannerAd( + fragment: Fragment, + containerView: ViewGroup, + ad: AdConfig.ThirdPartyAd, + expressViewWidthInDp: Float, + callback: (isSuccess: Boolean) -> Unit + ) { + + val slotId = ad.slotId + val displayRatio: Float = if (ad.displaySize.isEmpty()) { + 2F + } else { + val array = ad.displaySize.split("*") + if (array.size == 2) { + array[0].toFloat() / array[1].toFloat() + } else { + 2F + } + } + val expressViewHeightInDp = expressViewWidthInDp / displayRatio + + mCsjAdImpl?.requestBannerAd( + fragment, + containerView, + slotId, + expressViewWidthInDp, + expressViewHeightInDp, + callback + ) + } + + /** + * 取消开屏广告 + */ + fun cancelSplashAd(context: Context) { + mBeiziAdImpl?.cancelSplashAd(context) + mCsjAdImpl?.cancelSplashAd(context) } } \ No newline at end of file diff --git a/app/src/main/java/com/gh/common/FixedRateJobHelper.kt b/app/src/main/java/com/gh/common/FixedRateJobHelper.kt index 06322236b0..c9943845da 100644 --- a/app/src/main/java/com/gh/common/FixedRateJobHelper.kt +++ b/app/src/main/java/com/gh/common/FixedRateJobHelper.kt @@ -84,11 +84,6 @@ object FixedRateJobHelper { VideoRecordUtils.commitVideoRecord() } - // 获取启动广告 (第一次不需要获取) - if (elapsedTime % STARTUP_AD == 0L && mExecuteCount != 0) { - AdHelper.getSettingAdCache() - } - mExecuteCount++ } } diff --git a/app/src/main/java/com/gh/common/constant/Config.java b/app/src/main/java/com/gh/common/constant/Config.java index 6d908ca463..0583b9ec4f 100644 --- a/app/src/main/java/com/gh/common/constant/Config.java +++ b/app/src/main/java/com/gh/common/constant/Config.java @@ -459,7 +459,6 @@ public class Config { DarkModeUtils.INSTANCE.updateFollowSystemDarkModeToSp(true); DarkModeUtils.INSTANCE.initDarkMode(); } - AdHelper.prefetchStartUpAd(mNewApiSettingsEntity); SPUtils.setString(Constants.SP_NEW_API_SETTINGS, GsonUtils.toJson(data)); } }); diff --git a/app/src/main/java/com/gh/common/util/AdHelper.kt b/app/src/main/java/com/gh/common/util/AdHelper.kt index 4a7a27a4d9..b648c08814 100644 --- a/app/src/main/java/com/gh/common/util/AdHelper.kt +++ b/app/src/main/java/com/gh/common/util/AdHelper.kt @@ -19,44 +19,11 @@ object AdHelper { const val LOCATION_SUGGESTION_FUNCTION = "suggestion_function" const val LOCATION_SIMULATOR_GAME = "simulator_game" - @JvmStatic - fun getStartUpAd(): StartupAdEntity? { - return Config.getNewApiSettingsEntity()?.startAd - } - @JvmStatic fun getStartUp(): StartupAdEntity? { return Config.getNewApiSettingsEntity()?.startup } - @JvmStatic - fun prefetchStartUpAd(settingsEntity: NewApiSettingsEntity) { - if (settingsEntity.startAd != null && !settingsEntity.startAd?.img.isNullOrEmpty()) { - val screenWidth = DisplayUtils.getScreenWidth() - val transformedUrl = ImageUtils.getTransformedUrl(settingsEntity.startAd?.img, screenWidth) ?: return - ImageUtils.prefetchToDiskCache(transformedUrl) - } - } - - fun getSettingAdCache() { - RetrofitManager.getInstance().newApi - .getSettingAdCache(HaloApp.getInstance().channel) - .compose(observableToMain()) - .subscribe(object : Response() { - override fun onResponse(response: NewApiSettingsEntity?) { - super.onResponse(response) - val settings = Config.getNewApiSettingsEntity() - if (settings != null) { - settings.startAd = response?.startAd - Config.updateNewApiSettings(settings) - if (response != null) { - prefetchStartUpAd(response) - } - } - } - }) - } - fun getAd(location: String): SettingsEntity.AD? { val adList = Config.getSettings()?.adList ?: return null diff --git a/app/src/main/java/com/gh/common/util/DirectUtils.kt b/app/src/main/java/com/gh/common/util/DirectUtils.kt index 1d0677a9a9..523704eaad 100644 --- a/app/src/main/java/com/gh/common/util/DirectUtils.kt +++ b/app/src/main/java/com/gh/common/util/DirectUtils.kt @@ -99,53 +99,6 @@ import kotlin.math.roundToInt */ object DirectUtils { - /** - * 跳转到特定页面,根据 [type] 决定跳转页面,[path] 为跳转前的页面名称 - */ - @JvmStatic - fun directToSpecificPage( - context: Context, - type: String, - link: String, - text: String? = "", - entrance: String? = null, - path: String? = null - ) { - when (type) { - HOST_ARTICLE -> directToArticle(context, id = link, entrance = entrance) - - HOST_GAME -> directToGameDetail(context, id = link, entrance = entrance) - - HOST_GAME_DOWNLOAD -> directToGameDetail(context, id = link, entrance = entrance, autoDownload = true) - - HOST_COLUMN -> directToSubject(context, id = link, subjectName = text, entrance = entrance) - - HOST_QUESTION -> directToQuestionDetail(context, id = link, entrance = entrance, path = path) - - HOST_ANSWER -> directToAnswerDetail(context, id = link, entrance = entrance, path = path) - - HOST_WEB -> directToWebView(context, url = link, entrance = entrance) - - HOST_DOWNLOAD -> directToDownloadManagerAndStartDownload( - context, - gameId = link, - packageName = text, - entrance = entrance - ) - - HOST_UPDATE -> directToDownloadManagerAndStartUpdate( - context, - gameId = link, - packageName = text, - entrance = entrance - ) - - HOST_LIBAO -> directToGiftDetail(context, giftId = link, entrance = entrance) - - HOST_COMMUNITY -> directToCommunity(context, CommunityEntity(link, text!!)) - } - } - @JvmStatic fun directToLinkPage(context: Context, linkEntity: LinkEntity, entrance: String, path: String) { directToLinkPage(context, linkEntity, entrance, path, null) diff --git a/app/src/main/java/com/gh/gamecenter/MainActivity.java b/app/src/main/java/com/gh/gamecenter/MainActivity.java index 6b2f7b3579..6238f6110d 100644 --- a/app/src/main/java/com/gh/gamecenter/MainActivity.java +++ b/app/src/main/java/com/gh/gamecenter/MainActivity.java @@ -21,7 +21,6 @@ import android.app.Activity; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; -import android.content.pm.ApplicationInfo; import android.graphics.Color; import android.os.Build; import android.os.Bundle; @@ -46,7 +45,6 @@ import androidx.lifecycle.ViewModelProviders; import com.facebook.drawee.view.SimpleDraweeView; import com.gh.ad.AdDelegateHelper; -import com.gh.common.DefaultUrlHandler; import com.gh.common.constant.Config; import com.gh.common.exposure.ExposureManager; import com.gh.common.filter.RegionSettingHelper; @@ -92,7 +90,6 @@ import com.gh.gamecenter.common.utils.ImageUtils; import com.gh.gamecenter.common.utils.NotificationHelper; import com.gh.gamecenter.common.utils.ShareUtils; import com.gh.gamecenter.core.AppExecutor; -import com.gh.gamecenter.core.provider.IAdProvider; import com.gh.gamecenter.core.utils.ClassUtils; import com.gh.gamecenter.core.utils.DisplayUtils; import com.gh.gamecenter.core.utils.GsonUtils; @@ -102,8 +99,6 @@ import com.gh.gamecenter.core.utils.SentryHelper; import com.gh.gamecenter.core.utils.TimeUtils; import com.gh.gamecenter.core.utils.ToastUtils; import com.gh.gamecenter.core.utils.UrlFilterUtils; -import com.gh.gamecenter.download.DownloadFragment; -import com.gh.gamecenter.entity.InnerMetaInfoEntity; import com.gh.gamecenter.entity.StartupAdEntity; import com.gh.gamecenter.eventbus.EBSkip; import com.gh.gamecenter.feature.entity.GameEntity; @@ -141,22 +136,16 @@ import org.greenrobot.eventbus.ThreadMode; import org.jetbrains.annotations.NotNull; import org.json.JSONObject; -import java.io.BufferedReader; import java.io.File; import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; import java.lang.reflect.Field; import java.util.ArrayList; -import java.util.Enumeration; import java.util.HashMap; import java.util.List; import java.util.Locale; import java.util.Map; import java.util.Timer; import java.util.TimerTask; -import java.util.zip.ZipEntry; -import java.util.zip.ZipFile; import io.reactivex.SingleSource; import io.reactivex.android.schedulers.AndroidSchedulers; @@ -174,7 +163,7 @@ public class MainActivity extends BaseActivity { public static final String SWITCH_TO_COMMUNITY = "switch_to_community"; public static final String SWITCH_TO_VIDEO = "switch_to_video"; public static final String SHOW_AD = "show_ad"; - private static final int COUNTDOWN_AD = 100; + public static final int COUNTDOWN_AD = 100; private static final int COUNTDOWN_MAX_COUNT = 3; private int countdownCount = 0; @@ -190,11 +179,11 @@ public class MainActivity extends BaseActivity { public static boolean isNewFirstLaunch; private final Handler handler = new Handler(); - public boolean showAd = false; // 是否显示广告 + private boolean mShouldShowAd = false; // 是否显示广告 @Override protected void onCreate(Bundle savedInstanceState) { - showAd = getIntent().getBooleanExtra(SHOW_AD, false) && savedInstanceState == null; + mShouldShowAd = getIntent().getBooleanExtra(SHOW_AD, false) && savedInstanceState == null; HaloApp.getInstance().isAlreadyUpAndRunning = true; @@ -228,7 +217,6 @@ public class MainActivity extends BaseActivity { } } }, 2000L); - getPluginUpdate(); sp.edit().putBoolean("isNewFirstLaunchV" + PackageUtils.getGhVersionName(), false).apply(); // 记录曾安装过的版本 + 渠道 @@ -282,8 +270,6 @@ public class MainActivity extends BaseActivity { } } -// checkTinkerPath(); // 看情况是否需要显示补丁弹窗 - // 必须放在这里,否则会导致获取 baseActivity 不是本应用包名 DownloadManager.getInstance().initDownloadService(); @@ -300,17 +286,16 @@ public class MainActivity extends BaseActivity { doSkip(); } + // debug 模式下的快速跳转页面 if (BuildConfig.DEBUG) { - handler.postDelayed(() -> { - EntranceUtils.jumpShortcut(this); - }, 500); + handler.postDelayed(() -> EntranceUtils.jumpShortcut(this), 500); } - if (showAd) { - observeStartUp(); + if (mShouldShowAd) { + showAd(); } else { - hideStartUp(); - hideStartUpAd(); + hideTextAd(); + hideSplashAd(); } // 默认配置为空时重试 @@ -473,7 +458,7 @@ public class MainActivity extends BaseActivity { protected void onDestroy() { super.onDestroy(); - AdDelegateHelper.INSTANCE.cancelStartUpAd(this); + AdDelegateHelper.INSTANCE.cancelSplashAd(this); handler.removeCallbacksAndMessages(null); releaseExoSourceCache(); @@ -497,86 +482,56 @@ public class MainActivity extends BaseActivity { } } - private void observeStartUp() { - if (!showAd) { - hideStartUp(); - hideStartUpAd(); + /** + * 显示广告 (包括文本广告和开屏广告) + */ + private void showAd() { + if (!mShouldShowAd) { + hideTextAd(); + hideSplashAd(); return; } - final StartupAdEntity startUp = AdHelper.getStartUp(); - if (startUp != null) { - showStartUp(startUp); + final StartupAdEntity textAd = AdHelper.getStartUp(); + if (textAd != null) { + showTextAd(textAd); AppExecutor.getUiExecutor().executeWithDelay(() -> { - hideStartUp(); - initStartUpAd(); + hideTextAd(); + showSplashAd(); }, 2000); } else { - initStartUpAd(); + showSplashAd(); } } - private void initStartUpAd() { + /** + * 显示开屏广告 + */ + private void showSplashAd() { if (AdDelegateHelper.INSTANCE.shouldShowStartUpAd()) { - initSDKStartUpAd(); - } else { - observeStartUpAd(); - } - } + ViewGroup startAdContainer = findViewById(R.id.startAdContainer); + ViewGroup sdkStartAdContainer = findViewById(R.id.sdkStartAdContainer); + FrameLayout adsFl = findViewById(R.id.adsFl); - private void observeStartUpAd() { - final StartupAdEntity startUpAd = AdHelper.getStartUpAd(); + int screenWidthInPx = DisplayUtils.getScreenWidth(this); + int screenHeightInPx = DisplayUtils.getScreenHeight(this) + DisplayUtils.getStatusBarHeight(this.getResources()); + float screenWidthInDp = DisplayUtils.px2dip(this, screenWidthInPx); + float screenHeightInDp = DisplayUtils.px2dip(this, screenHeightInPx); - if (startUpAd != null && !TextUtils.isEmpty(startUpAd.getImg())) { - // 根据接口返回的广告时间,判断该图片广告是否还有必要显示 - boolean isAdValid = false; - if (startUpAd.getTime() != null) { - long currentTimeInSecond = System.currentTimeMillis() / 1000; - if (currentTimeInSecond > startUpAd.getTime().getStart() - && currentTimeInSecond < startUpAd.getTime().getEnd()) { - isAdValid = true; - } - } else { - // 接口没有返回开始和结束时间时,默认有效 - isAdValid = true; - } - - // 图片广告无效,跳过 - if (!isAdValid) { - hideStartUpAd(); - return; - } - - final String showedTodayTimestamp = SPUtils.getString(Constants.SP_STARTUP_AD_TIMESTAMP, ""); - final String rule = startUpAd.getRule(); - switch (rule) { - case "each": - showStartUpAd(startUpAd); - break; - case "once": - if (TextUtils.isEmpty(showedTodayTimestamp) - || !showedTodayTimestamp.contains(startUpAd.getId())) { - showStartUpAd(startUpAd); - } else { - hideStartUpAd(); + AdDelegateHelper.requestSplashAd( + this, + screenWidthInPx, + screenHeightInPx, + screenWidthInDp, + screenHeightInDp, + startAdContainer, + sdkStartAdContainer, + adsFl, + (BaseHandler) mBaseHandler, + () -> { + hideSplashAd(); + return null; } - break; - case "everyday": - final String today = TimeUtils.getToday(); - if (TextUtils.isEmpty(showedTodayTimestamp) - || !showedTodayTimestamp.contains(today) - || !showedTodayTimestamp.contains(startUpAd.getId())) { - showStartUpAd(startUpAd); - } else { - hideStartUpAd(); - } - break; - default: - hideStartUpAd(); - break; - } - SPUtils.setString(Constants.SP_STARTUP_AD_TIMESTAMP, startUpAd.getId() + TimeUtils.getToday()); - } else { - hideStartUpAd(); + ); } } @@ -588,14 +543,17 @@ public class MainActivity extends BaseActivity { TextView jumpBtn = findViewById(R.id.jumpBtn); jumpBtn.setText(String.format(Locale.CHINA, "跳过 %d", COUNTDOWN_MAX_COUNT - countdownCount)); if (COUNTDOWN_MAX_COUNT < countdownCount) { - hideStartUpAd(); + hideSplashAd(); } else { mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_AD, 1000); } } } - private void hideStartUp() { + /** + * 隐藏开屏文案 + */ + private void hideTextAd() { View maskContainer = findViewById(R.id.maskContainer); if (maskContainer != null) { maskContainer.setVisibility(View.GONE); @@ -603,8 +561,11 @@ public class MainActivity extends BaseActivity { } } - private void hideStartUpAd() { - showAd = false; + /** + * 隐藏开屏广告 + */ + private void hideSplashAd() { + mShouldShowAd = false; getIntent().putExtra(SHOW_AD, false); View startAdContainer = findViewById(R.id.startAdContainer); if (startAdContainer != null) { @@ -615,7 +576,7 @@ public class MainActivity extends BaseActivity { if (startSdkAdContainer != null) { startSdkAdContainer.setVisibility(View.GONE); ExtensionsKt.removeFromParent(startSdkAdContainer); - AdDelegateHelper.INSTANCE.cancelStartUpAd(this); + AdDelegateHelper.INSTANCE.cancelSplashAd(this); } checkDialog(); } @@ -631,74 +592,10 @@ public class MainActivity extends BaseActivity { }); } - private void showStartUpAd(StartupAdEntity ad) { - View startAdContainer = findViewById(R.id.startAdContainer); - View jumpBtn = findViewById(R.id.jumpBtn); - TextView jumpDetailBtn = findViewById(R.id.jumpDetailBtn); - SimpleDraweeView adImage = findViewById(R.id.adImage); - startAdContainer.setVisibility(View.VISIBLE); - jumpDetailBtn.setText(ad.getDesc()); - ExtensionsKt.setDrawableEnd(jumpDetailBtn, AppCompatResources.getDrawable(this, R.drawable.ic_startup_ad_arrow), null, null); - ImageUtils.display(adImage, ad.getImg()); - startAdContainer.setOnClickListener(v -> { - // do nothing 只是为了点击拦截事件,避免传递到下面的页面 - }); - jumpBtn.setOnClickListener(v -> { - mBaseHandler.removeMessages(COUNTDOWN_AD); - hideStartUpAd(); - LinkEntity linkEntity = ad.getJump(); - NewFlatLogUtils.logOpenScreenAdSkip( - ad.getId(), - linkEntity.getText() != null ? linkEntity.getText() : "", - linkEntity.getType() != null ? linkEntity.getType() : "", - linkEntity.getLink() != null ? linkEntity.getLink() : "" - ); - }); - List sources = new ArrayList<>(); - sources.add(new ExposureSource("开屏广告", ad.getId())); - final ExposureEvent event = ExposureEvent.createEvent(null, sources, null, ExposureType.EXPOSURE); - ExposureManager.INSTANCE.log(event); - if (ad.getButton()) { - jumpDetailBtn.setOnClickListener(v -> { - DirectUtils.directToLinkPage(this, ad.getJump(), "(启动广告)", "", event); - v.postDelayed(() -> { - mBaseHandler.removeMessages(COUNTDOWN_AD); - hideStartUpAd(); - }, 1000); - }); - jumpDetailBtn.setVisibility(View.VISIBLE); - LogUtils.logStartAd("watch_start_ads", ad); - } else { - LogUtils.logStartAd("start_ads", ad); - } - mBaseHandler.sendEmptyMessageDelayed(COUNTDOWN_AD, 1000); - } - - private void initSDKStartUpAd() { - ViewGroup startAdContainer = findViewById(R.id.sdkStartAdContainer); - startAdContainer.setVisibility(View.VISIBLE); - FrameLayout adsFl = findViewById(R.id.adsFl); - - float screenWidthInDp = DisplayUtils.getScreenWidth(this); - float screenHeightInDp = DisplayUtils.getScreenHeight(this); - int screenWidthInPx = DisplayUtils.px2dip(this, screenWidthInDp); - int screenHeightInPx = DisplayUtils.px2dip(this, screenHeightInDp); - - AdDelegateHelper.requestStartUpAd( - this, - "888381490", - screenWidthInPx, - screenHeightInPx, - screenWidthInDp, - screenHeightInDp, - startAdContainer, - () -> { - hideStartUpAd(); - return null; - }); - } - - private void showStartUp(StartupAdEntity ad) { + /** + * 显示文本广告 + */ + private void showTextAd(StartupAdEntity ad) { TextView adContentTv = findViewById(R.id.adContentTv); View containerView = findViewById(R.id.maskContainer); containerView.setVisibility(View.VISIBLE); @@ -706,7 +603,7 @@ public class MainActivity extends BaseActivity { containerView.setElevation(500F); } containerView.setOnClickListener(v -> { - // do nothing 只是为了点击拦截事件,避免传递到下面的页面 + // 拦截点击事件,避免传递到下面的页面 }); adContentTv.setText(ad.getDesc()); adContentTv.setVisibility(View.VISIBLE); @@ -756,7 +653,6 @@ public class MainActivity extends BaseActivity { } else { Intent skipIntent = new Intent(MainActivity.this, clazz); skipIntent.putExtras(bundle); -// startActivity(skipIntent); AvoidOnResultManager.Companion.getInstance(this) .startForResult(skipIntent, (resultCode, data) -> { Bundle nextToBundle = getIntent().getBundleExtra(KEY_NEXT_TO); @@ -878,7 +774,7 @@ public class MainActivity extends BaseActivity { @Override public Boolean invoke(Integer code) { if (code == 404001) { - if (showAd) { + if (mShouldShowAd) { AppExecutor.getUiExecutor().executeWithDelay(() -> { toast("抱歉,暂未找到相关内容"); }, 1000); @@ -989,7 +885,7 @@ public class MainActivity extends BaseActivity { @Override protected int getLayoutId() { - if (showAd) { + if (mShouldShowAd) { return R.layout.activity_main; } else { return R.layout.layout_wrapper_activity; @@ -1012,71 +908,6 @@ public class MainActivity extends BaseActivity { } } - // 获取META-INF中的plugin_update 文件,判断是否从游戏插件中下载的app,是则获取游戏id,启动游戏更新,下载该游戏 - private void getPluginUpdate() { - AppExecutor.getIoExecutor().execute(() -> { - ApplicationInfo appinfo = getApplicationInfo(); - String sourceDir = appinfo.sourceDir; - ZipFile zipfile = null; - - try { - zipfile = new ZipFile(sourceDir); - Enumeration entries = zipfile.entries(); - - while (entries.hasMoreElements()) { - ZipEntry entry = ((ZipEntry) entries.nextElement()); - String entryName = entry.getName(); - if (entryName.contains("gh_assist")) { - String packageName = entryName.substring(entryName.lastIndexOf("_") + 1); - startActivity(DownloadManagerActivity.getDownloadMangerIntent(MainActivity.this, - packageName, DownloadFragment.INDEX_UPDATE, "(游戏插件)")); - break; - } else if (entryName.contains("halo_skip.json")) { - InputStream in = zipfile.getInputStream(entry); - if (in != null) { - final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - - InnerMetaInfoEntity info = GsonUtils.getGson().fromJson(reader, InnerMetaInfoEntity.class); - if (info != null) { - if (EntranceConsts.HOST_COMMUNITY.equals(info.getType())) { - runOnUiThread(() -> mMainWrapperFragment.setCurrentItem(MainWrapperFragment.INDEX_BBS)); - } else { - DirectUtils.directToSpecificPage(this, - info.getType(), - info.getLink(), - info.getText(), - EntranceConsts.KEY_PLUGIN, - "特定包启动跳转"); - } - } - } - } else if (entryName.contains("halo_skip.dat")) { - InputStream in = zipfile.getInputStream(entry); - if (in != null) { - final BufferedReader reader = new BufferedReader(new InputStreamReader(in)); - String content = ""; - for (String line; (line = reader.readLine()) != null; content += line) ; - - if (!TextUtils.isEmpty(content)) { - DefaultUrlHandler.interceptUrl(this, content, "(特定包启动跳转)"); - } - } - } - } - } catch (Exception e) { - e.printStackTrace(); - } finally { - if (zipfile != null) { - try { - zipfile.close(); - } catch (IOException e) { - e.printStackTrace(); - } - } - } - }); - } - // 连接上网络事件 @Subscribe(threadMode = ThreadMode.MAIN) public void onEventMainThread(EBNetworkState busNetworkState) { diff --git a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt index 51d19222ce..5ddf9f322b 100644 --- a/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt +++ b/app/src/main/java/com/gh/gamecenter/SplashScreenActivity.kt @@ -119,18 +119,13 @@ class SplashScreenActivity : BaseActivity() { "这个弹窗只会在右上角有环境标签的测试包出现\n进入应用以后还可以到关于我们页面长按应用图标重新选择", "正式环境", "测试环境", - object : EmptyCallback { - override fun onCallback() { - SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, false) - showPrivacyDialog(guideLayout) - } + { + SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, false) + showPrivacyDialog(guideLayout) }, - object : EmptyCallback { - override fun onCallback() { - SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, true) - showPrivacyDialog(guideLayout) - } - + { + SPUtils.setBoolean(Constants.SP_IS_DEV_ENV, true) + showPrivacyDialog(guideLayout) }, false, "", @@ -286,8 +281,6 @@ class SplashScreenActivity : BaseActivity() { overridePendingTransition(0, 0) startActivity(intent) doFlavorInit() - // 初始化广告 SDK - AdDelegateHelper.init(HaloApp.getInstance()) logAppLaunch() finish() } diff --git a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt index 201cc923e8..c3b3631ecd 100644 --- a/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/download/DownloadFragment.kt @@ -9,15 +9,18 @@ import android.view.ViewGroup import android.widget.LinearLayout import android.widget.TextView import androidx.fragment.app.Fragment +import com.gh.ad.AdDelegateHelper import com.gh.common.util.HomePluggableHelper import com.gh.common.util.PackageInstaller import com.gh.download.DownloadManager import com.gh.gamecenter.DownloadManagerActivity import com.gh.gamecenter.R import com.gh.gamecenter.common.base.fragment.BaseFragment_TabLayout +import com.gh.gamecenter.common.databinding.FragmentDownloadBinding import com.gh.gamecenter.common.eventbus.EBMiPush import com.gh.gamecenter.common.utils.dip2px import com.gh.gamecenter.common.utils.visibleIf +import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.databinding.TabItemDownloadNumberBinding import com.gh.gamecenter.entity.HomePluggableFilterEntity import com.gh.gamecenter.feature.entity.PluginLocation @@ -42,12 +45,17 @@ class DownloadFragment : BaseFragment_TabLayout() { private var mPermanentInactivePluggableApkList: List? = null + private val mBinding by lazy { FragmentDownloadBinding.inflate(layoutInflater) } + override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) mPermanentInactivePluggableApkList = HomePluggableHelper.getPermanentInactivePluggablePackage() } + override fun getLayoutId() = 0 + override fun getInflatedLayout() = mBinding.root + override fun initFragmentList(fragments: MutableList) { fragments.add(GameDownloadFragment()) fragments.add(UpdatableGameFragment()) @@ -94,6 +102,18 @@ class DownloadFragment : BaseFragment_TabLayout() { mDownloadManager.markDownloadedTaskAsRead() } + val ad = AdDelegateHelper.getDownloadManagerAd()?.thirdPartyAd + if (ad != null) { + AdDelegateHelper.requestBannerAd( + this, + mBinding.adContainer, + ad, + DisplayUtils.getScreenWidthInDp(requireActivity()) + ) { + + } + } + val pathOfPackageToInstall = activity?.intent?.getStringExtra(KEY_PATH_OF_PACKAGE_TO_INSTALL) if (!TextUtils.isEmpty(pathOfPackageToInstall)) { PackageInstaller.install(requireContext(), false, pathOfPackageToInstall) @@ -179,6 +199,7 @@ class DownloadFragment : BaseFragment_TabLayout() { mDownloadNumberTv.setBackgroundColor(Color.TRANSPARENT) mDownloadNumberTv.text = downloadingCount.toString() } + mDownloadManager.isContainsUnreadDownloadedTask -> { layoutParams.width = 6F.dip2px() layoutParams.height = 6F.dip2px() @@ -186,6 +207,7 @@ class DownloadFragment : BaseFragment_TabLayout() { mDownloadNumberTv.setBackgroundResource(R.drawable.oval_hint_red_bg) mDownloadNumberTv.text = "" } + else -> mDownloadNumberTv.visibility = View.GONE } mDownloadNumberTv.layoutParams = layoutParams diff --git a/app/src/main/java/com/gh/gamecenter/entity/AdConfig.kt b/app/src/main/java/com/gh/gamecenter/entity/AdConfig.kt new file mode 100644 index 0000000000..8b31219142 --- /dev/null +++ b/app/src/main/java/com/gh/gamecenter/entity/AdConfig.kt @@ -0,0 +1,35 @@ +package com.gh.gamecenter.entity + +import com.google.gson.annotations.SerializedName + +class AdConfig( + val name: String, + val location: String, // 广告插入位置。光环启动:halo_launch 下载管理:download_manager 游戏搜索:game_search 助手启动:helper_launch + val type: String, // 广告位类型。开屏广告:launch 信息流广告:native banner 广告:banner 插屏广告:interstitial + val position: Int, // 定位,不存在的时候返回:-1 + @SerializedName("display_rules") + val displayRule: DisplayRule, + @SerializedName("third_party_ads") + val thirdPartyAd: ThirdPartyAd? = null, + @SerializedName("owner_ads") + val ownerAd: StartupAdEntity +) { + + class DisplayRule( + @SerializedName("ad_source") + val adSource: String, // 广告位获取广告源。第三方广告:third_party_ads 自有广告:owner_ads + @SerializedName("on_failed") + val onFailedAction: String, // 第三方广告获取失败时。显示自有广告:show 隐藏广告位:hide + ) + + class ThirdPartyAd( + @SerializedName("source_name") + val sourceName: String, // 广告源名。穿山甲、倍孜 + @SerializedName("ad_size") + val displaySize: String, + @SerializedName("code_name") + val slotName: String, + @SerializedName("code_id") + val slotId: String + ) +} \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/entity/NewApiSettingsEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/NewApiSettingsEntity.kt index 3f5dfeb510..13914820c9 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/NewApiSettingsEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/NewApiSettingsEntity.kt @@ -8,8 +8,6 @@ class NewApiSettingsEntity( @SerializedName("night_mode") var nightMode: NightMode? = null, var simulator: SimulatorEntity? = null, - @SerializedName("start_ad") - var startAd: StartupAdEntity? = null,//开屏图片广告 var startup: StartupAdEntity? = null,//启动文案广告 @SerializedName("user_interested_game") var userInterestedGame: Boolean = false, //偏好设置状态开关 diff --git a/app/src/main/java/com/gh/gamecenter/entity/StartupAdEntity.kt b/app/src/main/java/com/gh/gamecenter/entity/StartupAdEntity.kt index 255b199098..967c78fbd7 100644 --- a/app/src/main/java/com/gh/gamecenter/entity/StartupAdEntity.kt +++ b/app/src/main/java/com/gh/gamecenter/entity/StartupAdEntity.kt @@ -4,7 +4,8 @@ import com.gh.gamecenter.common.entity.LinkEntity import com.google.gson.annotations.SerializedName class StartupAdEntity( - @SerializedName("_id") + // 接口返回了个狗屎 code_id 过来,只能用 alternate 来 map 一下了 + @SerializedName(value = "_id", alternate = ["code_id"]) val id: String, val desc: String, val button: Boolean, diff --git a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt index de4986423d..1fef3d64fc 100644 --- a/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/home/HomeFragment.kt @@ -27,7 +27,7 @@ import com.gh.gamecenter.common.utils.viewModelProviderFromParent import com.gh.gamecenter.common.view.OffsetLinearLayoutManager import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.provider.IFloatingWindowProvider -import com.gh.gamecenter.core.provider.IAdProvider +import com.gh.gamecenter.core.provider.ICsjAdProvider import com.gh.gamecenter.core.utils.MD5Utils import com.gh.gamecenter.core.utils.MtaHelper import com.gh.gamecenter.core.utils.SPUtils @@ -105,7 +105,7 @@ class HomeFragment : LazyFragment() { } mListAdapter.setLoadStatus(it) val startUpAdProvider = - ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? IAdProvider + ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? ICsjAdProvider val startAdContainerView = requireActivity().findViewById(if (startUpAdProvider != null) R.id.sdkStartAdContainer else R.id.startAdContainer) if (it == LoadStatus.INIT_LOADED && startAdContainerView == null) { @@ -224,7 +224,7 @@ class HomeFragment : LazyFragment() { */ private fun listenStartAdViewRemoved() { val startUpAdProvider = - ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? IAdProvider + ARouter.getInstance().build(RouteConsts.provider.beiziAd).navigation() as? ICsjAdProvider val startAdContainerView = requireActivity().findViewById(if (startUpAdProvider != null) R.id.sdkStartAdContainer else R.id.startAdContainer) val parentView = startAdContainerView?.parent as? ViewGroup diff --git a/app/src/main/java/com/gh/gamecenter/receiver/NetworkStateReceiver.java b/app/src/main/java/com/gh/gamecenter/receiver/NetworkStateReceiver.java index d19a85d8c0..1f60831215 100644 --- a/app/src/main/java/com/gh/gamecenter/receiver/NetworkStateReceiver.java +++ b/app/src/main/java/com/gh/gamecenter/receiver/NetworkStateReceiver.java @@ -4,6 +4,7 @@ import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; +import com.gh.ad.AdDelegateHelper; import com.gh.gamecenter.common.constant.Constants; import com.gh.common.filter.RegionSettingHelper; import com.gh.common.repository.ReservationRepository; @@ -23,6 +24,7 @@ public class NetworkStateReceiver extends BroadcastReceiver { // 奇怪,初次注册监听就会有回调,会导致部分接口短时间内触发两次调用 ExtensionsKt.doOnMainProcessOnly(context, () -> { if (NetworkUtils.isNetworkConnected(context)) { + AdDelegateHelper.INSTANCE.requestAdConfig(context, true); RegionSettingHelper.getRegionSetting(); ReservationRepository.refreshReservationsIfNeeded(); EventBus.getDefault().post(new EBNetworkState(NetworkUtils.isNetworkConnected(context))); diff --git a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java index 2dd88666f9..e668adf007 100644 --- a/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java +++ b/app/src/main/java/com/gh/gamecenter/retrofit/service/ApiService.java @@ -7,6 +7,7 @@ import com.gh.gamecenter.common.entity.SimpleGameEntity; import com.gh.gamecenter.common.entity.ToolBoxEntity; import com.gh.gamecenter.common.entity.WechatConfigEntity; import com.gh.gamecenter.entity.ActivityLabelEntity; +import com.gh.gamecenter.entity.AdConfig; import com.gh.gamecenter.entity.AddonsUnreadEntity; import com.gh.gamecenter.entity.AmwayCommentEntity; import com.gh.gamecenter.entity.AppEntity; @@ -2962,12 +2963,6 @@ public interface ApiService { @POST("home/explore/games/{game_id}/feedback") Single discorveryFeedback(@Path("game_id") String gameId, @Body RequestBody body); - /** - * 启动广告缓存信息读取 - */ - @GET("/setting_cache") - Observable getSettingAdCache(@Query("channel") String channel); - /** * 评论游戏-获取草稿 */ @@ -3135,4 +3130,10 @@ public interface ApiService { */ @GET("blocks/{block_id}/tabs") Single> getBlockTab(@Path("block_id") String blockId); + + /** + * 获取广告配置 + */ + @GET("ad_spaces") + Single> getAdConfig(); } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt index 95bf119cf7..c28250f981 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexAdapter.kt @@ -352,10 +352,6 @@ class SearchGameIndexAdapter( EventBus.getDefault().post(EBSearch("click", gameEntity.id, gameEntity.name)) } - if (SearchType.fromString(type) == SearchType.AUTO) { - // MtaHelper.onEvent("游戏搜索", "自动搜索", key) - } - GameDetailActivity.startGameDetailActivity( mContext, gameEntity, StringUtils.buildString( @@ -418,7 +414,6 @@ class SearchGameIndexAdapter( if (SearchType.fromString(type) == SearchType.AUTO) { dao.add(key) - // MtaHelper.onEvent("游戏搜索", "自动搜索", key) } LogUtils.uploadSearchClick( diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt index 796533ca4a..603df32749 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameIndexFragment.kt @@ -30,6 +30,7 @@ import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.feature.entity.SettingsEntity import com.gh.gamecenter.help.HelpAndFeedbackBridge +import com.gh.gamecenter.pkg.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import com.lightgame.utils.Util_System_Keyboard @@ -61,7 +62,14 @@ class SearchGameIndexFragment : ListFragment().apply { key = mKey } + override fun provideListViewModel() = + viewModelProvider( + SearchGameResultViewModel.Factory( + HaloApp.getInstance(), + mKey, + false + ) + ) override fun provideListAdapter(): SearchGameIndexAdapter { if (mAdapter == null) { @@ -104,6 +112,7 @@ class SearchGameIndexFragment : ListFragment { Util_System_Keyboard.hideSoftKeyboard(requireActivity()) LogUtils.uploadSearchGame("ask_more_games", "搜索页", mKey, SearchType.fromString(mType).toChinese()) @@ -165,8 +174,8 @@ class SearchGameIndexFragment : ListFragment = ArrayMap() private var exposureEventArray: SparseArray? = null - private val ITEM_SEARCH_FOOTER = 109 var key: String = "" @@ -126,10 +131,17 @@ class SearchGameResultAdapter( val itemView = mLayoutInflater.inflate(R.layout.search_game_index_item, viewGroup, false) SearchGameIndexItemViewHolder(SearchGameIndexItemBinding.bind(itemView)) } + + ITEM_AD -> { + val itemView = mLayoutInflater.inflate(R.layout.search_game_ad_item, viewGroup, false) + SearchGameAdItemViewHolder(SearchGameAdItemBinding.bind(itemView)) + } + ItemViewType.ITEM_FOOTER -> { val itemView = mLayoutInflater.inflate(R.layout.refresh_footerview, viewGroup, false) FooterViewHolder(itemView) } + else -> { val view = mLayoutInflater.inflate(R.layout.search_game_footer, viewGroup, false) SearchGameFooterViewHolder(SearchGameFooterBinding.bind(view)) @@ -167,11 +179,29 @@ class SearchGameResultAdapter( } holder.bindSubjectItem(mContext, mEntityList[position], SearchType.fromString(type).toChinese(), key) } + is SearchGameIndexItemViewHolder -> { // val padTop= if (position == 0) 16F.dip2px() else 12F.dip2px() // holder.itemView.setPadding(16F.dip2px(), padTop, 16F.dip2px(), 12F.dip2px()) bindGameItem(holder) } + + is SearchGameAdItemViewHolder -> { + val adEntity = mEntityList[position].ad + val slotId = adEntity?.slotId ?: "" + val adContainer = holder.binding.adContainer + val screenWidthInDp = DisplayUtils.getScreenWidthInDp(fragment.activity) + + if (adContainer.tag == slotId) { + // 广告 slotId 没有变,不管它 + } else { + adContainer.tag = slotId + AdDelegateHelper.requestFlowAd(fragment, slotId, adContainer, screenWidthInDp) { + + } + } + } + is FooterViewHolder -> holder.initFooterViewHolder(listViewModel, mIsLoading, mIsNetworkError, mIsOver) is SearchGameFooterViewHolder -> bindFooterItem(holder) } @@ -441,11 +471,6 @@ class SearchGameResultAdapter( gameEntity.getMirrorPosition(), gameEntity.adIconActive ) - - if (SearchType.fromString(type) == SearchType.AUTO) { -// MtaHelper.onEvent("游戏搜索", "自动搜索", key) - } - } if (SPUtils.getBoolean(Constants.SP_TEENAGER_MODE)) { @@ -494,10 +519,6 @@ class SearchGameResultAdapter( gameEntity.getMirrorPosition(), gameEntity.adIconActive ) - - if (SearchType.fromString(type) == SearchType.AUTO) { -// MtaHelper.onEvent("游戏搜索", "自动搜索", key) - } } } ) @@ -545,6 +566,8 @@ class SearchGameResultAdapter( val data = mEntityList[position] if (data.subject != null) { ItemViewType.GAME_SUBJECT + } else if (data.ad != null) { + ITEM_AD } else { ItemViewType.ITEM_BODY } @@ -589,4 +612,12 @@ class SearchGameResultAdapter( fun isLoadOver(): Boolean { return mIsOver } + + companion object { + private const val ITEM_SEARCH_FOOTER = 109 + private const val ITEM_AD = 923 + } + + class SearchGameAdItemViewHolder(var binding: SearchGameAdItemBinding) : RecyclerView.ViewHolder(binding.root) + } \ No newline at end of file diff --git a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt index cbc4c80f7c..9d09420755 100644 --- a/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt +++ b/app/src/main/java/com/gh/gamecenter/search/SearchGameResultFragment.kt @@ -33,6 +33,7 @@ import com.gh.gamecenter.eventbus.EBDownloadStatus import com.gh.gamecenter.eventbus.EBPackage import com.gh.gamecenter.feature.entity.GameEntity import com.gh.gamecenter.help.HelpAndFeedbackBridge +import com.gh.gamecenter.pkg.HaloApp import com.lightgame.download.DataWatcher import com.lightgame.download.DownloadEntity import com.lightgame.utils.Util_System_Keyboard @@ -70,7 +71,8 @@ class SearchGameResultFragment : ListFragment().apply { key = mKey } + override fun provideListViewModel() = + viewModelProvider(SearchGameResultViewModel.Factory(HaloApp.getInstance(), mKey, true)) override fun provideListAdapter(): SearchGameResultAdapter { if (mAdapter == null) { @@ -319,8 +321,8 @@ class SearchGameResultFragment : ListFragment(application) { +class SearchGameResultViewModel( + application: Application, + private var mSearchKey: String?, + private var mIsManuallySearch: Boolean) : ListViewModel(application) { private var mApi = RetrofitManager.getInstance().api private var mPage = 0 - var mSearchSubjects = arrayListOf() - var key = "" + private var mSearchSubjects = arrayListOf() + + // 游戏广告列表,需要先本地按插入 position 排序,避免后续问题 + private val mGameSearchAdList by lazy { AdDelegateHelper.getGameSearchAdList().apply { sortBy { it.position } } } + private val mAdPositionSet: HashSet by lazy { + val adPositionSet = hashSetOf() + val adConfigList = mGameSearchAdList + if (adConfigList.isNotEmpty()) { + for (config in adConfigList) { + adPositionSet.add(config.position) + } + } + adPositionSet + } + + fun updateSearchKey(searchKey: String) { + mSearchKey = searchKey + } + + fun clearSearchSubjects() { + mSearchSubjects.clear() + } - @SuppressLint("CheckResult") override fun mergeResultLiveData() { mResultLiveData.addSource(mListLiveData) { list -> - val itemDataList = ArrayList(list.map { SearchItemData(game = it) }) + decorateListAndPost(list) + } + } - mApi.getSearchSubject(key, mPage) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ mutableList -> - mSearchSubjects.addAll(mutableList) - mSearchSubjects.forEach { - itemDataList.add( - if (it.location <= 0 || it.location >= itemDataList.size) 0 else it.location - 1, - SearchItemData(subject = it) - ) - } - // 处理初始化列表且游戏列表size为0的情况 - handleLoadStatusWhenGameListIsEmpty(list, itemDataList) - mResultLiveData.postValue(itemDataList) + @SuppressLint("CheckResult") + private fun decorateListAndPost(list: MutableList) { + var itemDataList = ArrayList(list.map { SearchItemData(game = it) }) + mApi.getSearchSubject(mSearchKey, mPage) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ mutableList -> + mSearchSubjects.addAll(mutableList) + mSearchSubjects.forEach { + itemDataList.add( + if (it.location <= 0 || it.location >= itemDataList.size) 0 else it.location - 1, + SearchItemData(subject = it) + ) + } + // 处理初始化列表且游戏列表size为0的情况 + handleLoadStatusWhenGameListIsEmpty(list, itemDataList) - val trackEvent = JSONObject() - try { - trackEvent.put("search_result", !(mPage == 1 && list.isEmpty())) - } catch (e: JSONException) { - e.printStackTrace() - } - SensorsBridge.trackEvent("SearchResultReturn", trackEvent) - }, { - it.printStackTrace() - handleLoadStatusWhenGameListIsEmpty(list, itemDataList) - mResultLiveData.postValue(itemDataList) - }) + if (mIsManuallySearch) { + itemDataList = decorateListWithAd(itemDataList) + } + + mResultLiveData.postValue(itemDataList) + + val trackEvent = JSONObject() + try { + trackEvent.put("search_result", !(mPage == 1 && list.isEmpty())) + } catch (e: JSONException) { + e.printStackTrace() + } + SensorsBridge.trackEvent("SearchResultReturn", trackEvent) + }, { + it.printStackTrace() + handleLoadStatusWhenGameListIsEmpty(list, itemDataList) + mResultLiveData.postValue(itemDataList) + }) + } + + private fun decorateListWithAd(itemDataList: ArrayList): ArrayList { + if (mAdPositionSet.isNotEmpty()) { + val decoratedItemDataList = ArrayList(itemDataList) + + // 位置已经排过序,若插入位置大于列表的 size 那么直接 break 放弃 + for ((index, position) in mAdPositionSet.withIndex()) { + if (position < itemDataList.size + index + 1) { + decoratedItemDataList.add(position - 1, SearchItemData(ad = mGameSearchAdList[index].thirdPartyAd)) + } else { + break + } + } + + return decoratedItemDataList + } else { + return itemDataList } } @@ -92,13 +145,13 @@ class SearchGameResultViewModel(application: Application) : ListViewModel> { // 可能会有特殊字符,需要 encode 处理 - val encodedKey = URLEncoder.encode(key, "utf-8") + val encodedKey = URLEncoder.encode(mSearchKey, "utf-8") mPage = page return mApi.getSearchGame( Config.API_HOST + "games:search?keyword=" + @@ -107,4 +160,15 @@ class SearchGameResultViewModel(application: Application) : ListViewModel + + + + + \ No newline at end of file diff --git a/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdHelper.kt b/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdHelper.kt index ed4a679044..2d77c7ebb3 100644 --- a/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdHelper.kt +++ b/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdHelper.kt @@ -12,7 +12,7 @@ import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.core.utils.DisplayUtils import com.gh.gamecenter.core.utils.SPUtils -object BeiziAdHelper { +internal object BeiziAdHelper { private const val BEIZI_APP_ID = "21425" private const val BEIZI_START_UP_AD_ID = "105706" @@ -31,12 +31,16 @@ object BeiziAdHelper { }) } - fun initBeiziStartUpAd(startAdContainer: View, adsFl: FrameLayout, showAd: Boolean, hideCallback: () -> Unit) { + fun requestSplashAd(startAdContainer: View, + adsFl: FrameLayout, + adViewWidth: Int, + adViewHeight: Int, + callback: (isSuccess: Boolean) -> Unit) { startAdContainer.visibility = View.VISIBLE mSplashAd = SplashAd(startAdContainer.context, null, BEIZI_START_UP_AD_ID, object : AdListener { override fun onAdLoaded() { - if (!showAd || mSplashAd == null) { - hideCallback() + if (mSplashAd == null) { + callback.invoke(false) mSplashAd = null } else { mSplashAd?.show(adsFl) @@ -48,12 +52,12 @@ object BeiziAdHelper { } override fun onAdFailedToLoad(i: Int) { - hideCallback() + callback.invoke(false) mSplashAd = null } override fun onAdClosed() { - hideCallback() + callback.invoke(true) mSplashAd = null } @@ -65,7 +69,7 @@ object BeiziAdHelper { // do nothing } }, START_UP_AD_TOTAL_TIME) - mSplashAd?.loadAd(DisplayUtils.getScreenWidth(), DisplayUtils.getScreenHeight()) + mSplashAd?.loadAd(adViewWidth, adViewHeight) } fun cancelBeiziStartUpAd(context: Context) { diff --git a/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdProviderImpl.kt b/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdProviderImpl.kt index 37e3ad42a3..8d7d849738 100644 --- a/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdProviderImpl.kt +++ b/feature/beizi_startup_ad/src/main/java/com/gh/gamecenter/beiziad/BeiziAdProviderImpl.kt @@ -1,34 +1,27 @@ package com.gh.gamecenter.beiziad -import android.app.Activity import android.content.Context import android.view.View -import android.view.ViewGroup import android.widget.FrameLayout import com.alibaba.android.arouter.facade.annotation.Route import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.core.provider.IAdProvider +import com.gh.gamecenter.core.provider.IBeiziAdProvider @Route(path = RouteConsts.provider.beiziAd, name = "beizi广告暴露服务") -class BeiziAdProviderImpl : IAdProvider { +class BeiziAdProviderImpl : IBeiziAdProvider { override fun initSDK(context: Context) { BeiziAdHelper.initBeiziSDK(context) } - override fun requestStartUpAd( - activity: Activity, - slotId: String, - adViewWidthInPx: Int, - adViewHeightInPx: Int, - adViewWidthInDp: Float, - adViewHeightInDp: Float, - startAdContainer: ViewGroup, - hideCallback: () -> Unit - ) { -// BeiziAdHelper.initBeiziStartUpAd(startAdContainer, adsFl, showAd, hideCallback) + override fun requestSplashAd(startAdContainer: View, + adsFl: FrameLayout, + adViewWidthInPx: Int, + adViewHeightInPx: Int, + callback: (isSuccess: Boolean) -> Unit) { + BeiziAdHelper.requestSplashAd(startAdContainer, adsFl, adViewWidthInPx, adViewHeightInPx, callback) } - override fun cancelStartUpAd(context: Context) { + override fun cancelSplashAd(context: Context) { BeiziAdHelper.cancelBeiziStartUpAd(context) } diff --git a/feature/csj_ad/proguard-rules.pro b/feature/csj_ad/proguard-rules.pro index d225f396c5..481bb43481 100644 --- a/feature/csj_ad/proguard-rules.pro +++ b/feature/csj_ad/proguard-rules.pro @@ -18,10 +18,4 @@ # If you keep the line number information, uncomment this to # hide the original source file name. -#-renamesourcefileattribute SourceFile - -# 倍孜广告SDK混淆 --dontwarn com.beizi.fusion.** --dontwarn com.beizi.ad.** --keep class com.beizi.fusion.** {*; } --keep class com.beizi.ad.** {*; } \ No newline at end of file +#-renamesourcefileattribute SourceFile \ No newline at end of file diff --git a/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdHelper.kt b/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdHelper.kt index 97c9e80c96..6fe55e2ddc 100644 --- a/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdHelper.kt +++ b/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdHelper.kt @@ -2,20 +2,26 @@ package com.gh.gamecenter.csj import android.app.Activity import android.content.Context +import android.view.View import android.view.ViewGroup +import androidx.fragment.app.Fragment import com.bytedance.sdk.openadsdk.* import com.bytedance.sdk.openadsdk.CSJSplashAd.SplashAdListener +import com.bytedance.sdk.openadsdk.TTAdDislike.DislikeInteractionCallback import com.bytedance.sdk.openadsdk.TTAdNative.CSJSplashAdListener +import com.bytedance.sdk.openadsdk.TTAdNative.NativeExpressAdListener import com.bytedance.sdk.openadsdk.TTAdSdk.InitCallback +import com.bytedance.sdk.openadsdk.TTNativeExpressAd.ExpressAdInteractionListener import com.gh.gamecenter.common.utils.PackageFlavorHelper +import com.gh.gamecenter.common.utils.doOnFragmentDestroy import com.lightgame.utils.Utils object CsjAdHelper { const val TAG = "CsjAdHelper" - fun init(context: Context) { - //强烈建议在应用对应的Application#onCreate()方法中调用,避免出现content为null的异常 + fun init(context: Context, oaid: String) { + // 强烈建议在应用对应的Application#onCreate()方法中调用,避免出现content为null的异常 TTAdSdk.init( context, TTAdConfig.Builder() @@ -28,6 +34,35 @@ object CsjAdHelper { .directDownloadNetworkType(TTAdConstant.NETWORK_STATE_WIFI) // 允许直接下载的网络状态集合,没有设置的网络下点击下载apk会有二次确认弹窗,弹窗中会披露应用信息 .supportMultiProcess(false) // 是否支持多进程,true支持 .asyncInit(true) // 是否异步初始化sdk,设置为true可以减少SDK初始化耗时。3450版本开始废弃~~ + .customController(object : TTCustomController() { + /** + * 是否允许SDK主动使用地理位置信息 + * + * @return true可以获取,false禁止获取。默认为true + */ + override fun isCanUseLocation(): Boolean = false + + /** + * 是否允许SDK主动使用手机硬件参数,如:imei + * + * @return true可以使用,false禁止使用。默认为true + */ + override fun isCanUsePhoneState() = false + + /** + * 是否允许SDK主动使用WRITE_EXTERNAL_STORAGE权限 + * + * @return true可以使用,false禁止使用。默认为true + */ + override fun isCanUseWriteExternal(): Boolean = false + + /** + * 开发者可以传入oaid + * + * @return oaid + */ + override fun getDevOaid(): String = oaid + }) .build(), object : InitCallback { override fun success() { @@ -38,16 +73,17 @@ object CsjAdHelper { Utils.log(TAG, "穿山甲初始化失败, $p0 $p1") } }) - //如果明确某个进程不会使用到广告SDK,可以只针对特定进程初始化广告SDK的content - //if (PROCESS_NAME_XXXX.equals(processName)) { - // TTAdSdk.init(context, config); - //} + } + + fun updateThemeStatus(isDarkMode: Boolean) { + val adManager = TTAdSdk.getAdManager() + adManager.themeStatus = if (isDarkMode) 1 else 0 } /** * 请求启动广告 */ - fun requestStartUpAD( + fun requestSplashAd( activity: Activity, slotId: String, adViewWidthInPx: Int, @@ -55,19 +91,18 @@ object CsjAdHelper { adViewWidthInDp: Float, adViewHeightInDp: Float, startAdContainer: ViewGroup, - hideCallback: () -> Unit + callback: (isSuccess: Boolean) -> Unit, ) { val mTTAdNative = TTAdSdk.getAdManager().createAdNative(activity) val adSlot = AdSlot.Builder() .setCodeId(slotId) - //不区分渲染方式,要求开发者同时设置setImageAcceptedSize(单位:px)和setExpressViewAcceptedSize(单位:dp )接口,不同时设置可能会导致展示异常。 + // 不区分渲染方式,要求开发者同时设置setImageAcceptedSize(单位:px)和setExpressViewAcceptedSize(单位:dp )接口,不同时设置可能会导致展示异常。 .setImageAcceptedSize(adViewWidthInPx, adViewHeightInPx) .setExpressViewAcceptedSize(adViewWidthInDp, adViewHeightInDp) - .setAdLoadType(TTAdLoadType.LOAD)//推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略 + .setAdLoadType(TTAdLoadType.LOAD)// 推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略 .build() - mTTAdNative.loadSplashAd(adSlot, object : CSJSplashAdListener { override fun onSplashLoadSuccess() { Utils.log(TAG, "开屏广告加载成功") @@ -75,13 +110,13 @@ object CsjAdHelper { override fun onSplashLoadFail(p0: CSJAdError?) { Utils.log(TAG, "开屏广告加载失败") - hideCallback.invoke() + callback.invoke(false) } override fun onSplashRenderSuccess(p0: CSJSplashAd?) { Utils.log(TAG, "开屏广告渲染成功 $p0") if (p0 == null) { - hideCallback.invoke() + callback.invoke(false) return } if (!activity.isFinishing) { @@ -93,6 +128,7 @@ object CsjAdHelper { override fun onSplashAdClick(p0: CSJSplashAd?) { Utils.log(TAG, "点击开屏广告") + callback.invoke(true) } override fun onSplashAdClose(ad: CSJSplashAd?, closeType: Int) { @@ -103,22 +139,200 @@ object CsjAdHelper { } else if (closeType == CSJSplashCloseType.CLICK_JUMP) { Utils.log(TAG, "点击跳转") } - hideCallback.invoke() + callback.invoke(true) } }) // 把SplashView 添加到ViewGroup中,注意开屏广告view:width =屏幕宽;height >=75%屏幕高 p0.showSplashView(startAdContainer) } else { // 开发者处理跳转到APP主页面逻辑 - hideCallback.invoke() + callback.invoke(false) } } override fun onSplashRenderFail(p0: CSJSplashAd?, p1: CSJAdError?) { Utils.log(TAG, "开屏广告渲染失败 $p0") - hideCallback.invoke() + callback.invoke(false) } }, 3500) } + fun requestFlowAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewAcceptedSize: Float, + callback: (isSuccess: Boolean) -> Unit + ) { + val isBoundedActivityValid = + fragment.isAdded && fragment.activity != null && !fragment.requireActivity().isFinishing + + if (!isBoundedActivityValid) { + Utils.log(TAG, "requestFlowAd but activity is invalid.") + } + + val boundedActivity = fragment.activity + + val mTTAdNative = TTAdSdk.getAdManager().createAdNative(boundedActivity) + + val adSlot = AdSlot.Builder() + .setCodeId(slotId) // 广告位id + .setSupportDeepLink(true) + .setAdCount(1) // 请求广告数量为1到3条 + .setExpressViewAcceptedSize(expressViewAcceptedSize, 0F) // 期望模板广告view的size,宽度最低为375,单位dp + .setAdLoadType(TTAdLoadType.LOAD) // 推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略 + .build() + + var activeAd: TTNativeExpressAd? = null + + mTTAdNative.loadNativeExpressAd(adSlot, object : NativeExpressAdListener { + // 广告请求失败 + override fun onError(code: Int, message: String) { + Utils.log(TAG, "requestFlowAd 广告请求失败 $message") + callback.invoke(false) + } + + // 广告请求成功 + override fun onNativeExpressAdLoad(ads: List) { + activeAd = ads.firstOrNull() + + activeAd?.setExpressInteractionListener(object : ExpressAdInteractionListener { + // 广告点击回调 + override fun onAdClicked(view: View, type: Int) { + Utils.log(TAG, "requestFlowAd 广告点击 $type") + } + + // 广告展示回调 + override fun onAdShow(view: View, type: Int) { + Utils.log(TAG, "requestFlowAd 广告展示") + + callback.invoke(true) + } + + // 广告渲染失败回调 + override fun onRenderFail(view: View, msg: String, code: Int) { + Utils.log(TAG, "requestFlowAd 广告渲染失败 $code $msg") + + callback.invoke(false) + } + + // 广告渲染成功回调 + override fun onRenderSuccess(view: View, width: Float, height: Float) { + Utils.log(TAG, "requestFlowAd 广告渲染成功 $width $height") + containerView.removeAllViews() + containerView.addView(view) + } + }) + activeAd?.setDislikeCallback(boundedActivity, object: DislikeInteractionCallback { + override fun onShow() { + Utils.log(TAG, "requestFlowAd 用户点击关闭广告") + } + + override fun onSelected(p0: Int, p1: String?, p2: Boolean) { + Utils.log(TAG, "requestFlowAd 用户不喜欢广告,理由是 $p1") + containerView.removeAllViews() + } + + override fun onCancel() { + Utils.log(TAG, "requestFlowAd 取消不喜欢弹窗") + } + }) + activeAd?.render() + } + }) + + fragment.doOnFragmentDestroy { + activeAd?.destroy() + } + } + + fun requestBannerAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewWidthInDp: Float, + expressViewHeightInDp: Float, + callback: (isSuccess: Boolean) -> Unit + ) { + + // 创建TTAdNative对象,createAdNative(Context context) context需要传入Activity对象 + val mTTAdNative = TTAdSdk.getAdManager().createAdNative(fragment.activity) + + val isBoundedActivityValid = + fragment.isAdded && fragment.activity != null && !fragment.requireActivity().isFinishing + + if (!isBoundedActivityValid) { + Utils.log(TAG, "requestBannerAd but activity is invalid.") + return + } + + val adSlot = AdSlot.Builder() + .setCodeId(slotId) // 广告位id + .setSupportDeepLink(true) + .setAdCount(1) // 请求广告数量为1 + .setExpressViewAcceptedSize(expressViewWidthInDp, expressViewHeightInDp) // 期望模板广告view的size,单位dp + .setAdLoadType(TTAdLoadType.LOAD) // 推荐使用,用于标注此次的广告请求用途为预加载(当做缓存)还是实时加载,方便后续为开发者优化相关策略 + .build() + + mTTAdNative.loadBannerExpressAd(adSlot, object : NativeExpressAdListener { + // 请求失败回调 + override fun onError(code: Int, message: String) { + Utils.log(TAG, "banner $code 请求失败 $message") + callback.invoke(false) + } + + // 请求成功回调 + override fun onNativeExpressAdLoad(ads: List) { + val ad = ads.firstOrNull() + ad?.setSlideIntervalTime(30 * 1000) // 轮播时间 + ad?.setExpressInteractionListener(object : ExpressAdInteractionListener { + // 广告点击回调 + override fun onAdClicked(view: View, type: Int) { + Utils.log(TAG, "banner 广告点击 $type") + } + + // 广告展示回调 + override fun onAdShow(view: View, type: Int) { + Utils.log(TAG, "banner 广告展示 $type") + } + + // 广告渲染失败回调 + override fun onRenderFail(view: View, msg: String, code: Int) { + Utils.log(TAG, "banner 广告渲染失败 $code $msg") + callback.invoke(false) + } + + // 广告渲染成功回调 + override fun onRenderSuccess(view: View, width: Float, height: Float) { + Utils.log(TAG, "banner 广告渲染成功 $width $height") + + containerView.removeAllViews() + containerView.addView(view) + + callback.invoke(true) + } + }) + ad?.setDislikeCallback(fragment.activity, object: DislikeInteractionCallback { + override fun onShow() { + Utils.log(TAG, "banner 用户点击关闭广告") + } + + override fun onSelected(p0: Int, p1: String?, p2: Boolean) { + Utils.log(TAG, "banner 用户不喜欢广告,理由是 $p1") + containerView.removeAllViews() + } + + override fun onCancel() { + Utils.log(TAG, "banner 取消不喜欢弹窗") + } + }) + ad?.render() + + fragment.doOnFragmentDestroy { + ad?.destroy() + } + } + }) + } + } \ No newline at end of file diff --git a/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdProviderImpl.kt b/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdProviderImpl.kt index 4d8bfa04f5..35e559d09c 100644 --- a/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdProviderImpl.kt +++ b/feature/csj_ad/src/main/java/com/gh/gamecenter/csj/CsjAdProviderImpl.kt @@ -3,17 +3,18 @@ package com.gh.gamecenter.csj import android.app.Activity import android.content.Context import android.view.ViewGroup +import androidx.fragment.app.Fragment import com.alibaba.android.arouter.facade.annotation.Route import com.gh.gamecenter.common.constant.RouteConsts -import com.gh.gamecenter.core.provider.IAdProvider +import com.gh.gamecenter.core.provider.ICsjAdProvider @Route(path = RouteConsts.provider.csjAd, name = "csj广告暴露服务") -class CsjAdProviderImpl : IAdProvider { - override fun initSDK(context: Context) { - CsjAdHelper.init(context) +class CsjAdProviderImpl : ICsjAdProvider { + override fun initSDK(context: Context, oaid: String) { + CsjAdHelper.init(context, oaid) } - override fun requestStartUpAd( + override fun requestSplashAd( activity: Activity, slotId: String, adViewWidthInPx: Int, @@ -21,9 +22,9 @@ class CsjAdProviderImpl : IAdProvider { adViewWidthInDp: Float, adViewHeightInDp: Float, startAdContainer: ViewGroup, - hideCallback: () -> Unit + callback: (isSuccess: Boolean) -> Unit, ) { - CsjAdHelper.requestStartUpAD( + CsjAdHelper.requestSplashAd( activity, slotId, adViewWidthInPx, @@ -31,12 +32,37 @@ class CsjAdProviderImpl : IAdProvider { adViewWidthInDp, adViewHeightInDp, startAdContainer, - hideCallback + callback, ) } - override fun cancelStartUpAd(context: Context) { + override fun requestFlowAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewAcceptedSize: Float, + callback: (isSuccess: Boolean) -> Unit, + ) { + CsjAdHelper.requestFlowAd(fragment, containerView, slotId, expressViewAcceptedSize, callback) + } + override fun requestBannerAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewWidthInDp: Float, + expressViewHeightInDp: Float, + callback: (isSuccess: Boolean) -> Unit + ) { + CsjAdHelper.requestBannerAd(fragment, containerView, slotId, expressViewWidthInDp, expressViewHeightInDp, callback) + } + + override fun updateThemeStatus(context: Context, isDarkMode: Boolean) { + CsjAdHelper.updateThemeStatus(isDarkMode) + } + + override fun cancelSplashAd(context: Context) { + // 穿山甲没有 cancel 的方法提供... } override fun shouldEnableSDK(channel: String): Boolean { diff --git a/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt b/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt index e6bfdeffef..08fb6461f2 100644 --- a/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt +++ b/feature/floating-window/src/demo/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt @@ -60,11 +60,7 @@ class QaFeedbackDialogFragment : BaseDialogFragment() { mLoadingDialog?.dismiss() } - mIsDarkModeOn = if (BuildConfig.IS_DARK_MODE_ON) { - isDarkModeOn(requireContext()) - } else { - false - } + mIsDarkModeOn = isDarkModeOn(requireContext()) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) { diff --git a/module_common/build.gradle b/module_common/build.gradle index f74c66134a..cfac735508 100644 --- a/module_common/build.gradle +++ b/module_common/build.gradle @@ -32,7 +32,6 @@ android { * IS_NIGHT_MODE_ON 供区分深色模式功能是否启用 */ buildConfigField "long", "BUILD_TIME", "0" - buildConfigField "boolean", "IS_DARK_MODE_ON", "true" } buildFeatures { diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java index 5aedf2b9e7..0ee7a6e360 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/activity/BaseActivity.java @@ -115,7 +115,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy protected final Handler mBaseHandler = new BaseHandler(this); - protected static class BaseHandler extends Handler { + public static class BaseHandler extends Handler { private final WeakReference mActivityWeakReference; @@ -200,8 +200,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy super.onResume(); startPageTime = System.currentTimeMillis(); - if (BuildConfig.IS_DARK_MODE_ON - && !DarkModeUtils.INSTANCE.isFollowSystemDarkModeFromSp() + if (!DarkModeUtils.INSTANCE.isFollowSystemDarkModeFromSp() && mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(this)) { if (mShouldApplyNewUiModeOnNextResume) { @@ -578,7 +577,7 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy breadcrumb.setLevel(SentryLevel.INFO); Sentry.addBreadcrumb(breadcrumb); - if (BuildConfig.IS_DARK_MODE_ON && mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(this)) { + if (mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(this)) { // 当且仅当页面处于可见状态时响应新的 UiMode,并将它传递下去 // (在不可见的 activity 调用 onConfigurationChanged 方法有一定概率触发 ANR) if (getLifecycle().getCurrentState().isAtLeast(Lifecycle.State.STARTED)) { @@ -611,19 +610,17 @@ public abstract class BaseActivity extends BaseAppCompatActivity implements Easy } protected void onDarkModeChanged() { - if (BuildConfig.IS_DARK_MODE_ON) { - TextView tv = findViewById(ID_NIGHT_INDICATOR); - if (tv != null) { - tv.setText(DarkModeUtils.INSTANCE.isDarkModeOn(this) ? "深色模式" : "浅色模式"); - tv.setAlpha(DarkModeUtils.INSTANCE.isDarkModeOn(this) ? 0.8F : 0.15F); - } - if (isAutoResetViewBackgroundEnabled()) { - updateStaticViewBackground(getWindow().getDecorView()); - } - if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { - getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(this, R.color.background)); - getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.background_white)); - } + TextView tv = findViewById(ID_NIGHT_INDICATOR); + if (tv != null) { + tv.setText(DarkModeUtils.INSTANCE.isDarkModeOn(this) ? "深色模式" : "浅色模式"); + tv.setAlpha(DarkModeUtils.INSTANCE.isDarkModeOn(this) ? 0.8F : 0.15F); + } + if (isAutoResetViewBackgroundEnabled()) { + updateStaticViewBackground(getWindow().getDecorView()); + } + if (android.os.Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + getWindow().getDecorView().setBackgroundColor(ContextCompat.getColor(this, R.color.background)); + getWindow().setNavigationBarColor(ContextCompat.getColor(this, R.color.background_white)); } } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java index f6e55259c6..43603eac9d 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java +++ b/module_common/src/main/java/com/gh/gamecenter/common/base/fragment/BaseFragment.java @@ -162,11 +162,7 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB if (addSyncPageObserver()) { initSyncPageObserver(); } - if (BuildConfig.IS_DARK_MODE_ON) { - mIsDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(requireContext()); - } else { - mIsDarkModeOn = false; - } + mIsDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(requireContext()); } private void initSyncPageObserver() { @@ -240,8 +236,7 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB isEverPause = false; startPageTime = System.currentTimeMillis(); - if (BuildConfig.IS_DARK_MODE_ON - && !DarkModeUtils.INSTANCE.isFollowSystemDarkModeFromSp() + if (!DarkModeUtils.INSTANCE.isFollowSystemDarkModeFromSp() && mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(requireContext())) { onDarkModeChanged(); } @@ -358,7 +353,7 @@ public abstract class BaseFragment extends Fragment implements OnRequestCallB public void onConfigurationChanged(@NonNull Configuration newConfig) { super.onConfigurationChanged(newConfig); - if (BuildConfig.IS_DARK_MODE_ON && mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(requireContext())) { + if (mIsDarkModeOn != DarkModeUtils.INSTANCE.isDarkModeOn(requireContext())) { mIsDarkModeOn = DarkModeUtils.INSTANCE.isDarkModeOn(requireContext()); onDarkModeChanged(); } diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/DarkModeUtils.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/DarkModeUtils.kt index 650d19d205..54fcdecf0d 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/DarkModeUtils.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/DarkModeUtils.kt @@ -3,22 +3,21 @@ package com.gh.gamecenter.common.utils import android.content.Context import android.content.res.Configuration import androidx.appcompat.app.AppCompatDelegate -import com.gh.gamecenter.common.BuildConfig import com.gh.gamecenter.common.constant.Constants +import com.gh.gamecenter.core.AppExecutor import com.gh.gamecenter.core.utils.SPUtils object DarkModeUtils { + + private val mModeChangeListenerList: ArrayList<() -> Unit> by lazy { arrayListOf() } + /** * 当前应用的深色模式是否生效中 */ fun isDarkModeOn(context: Context): Boolean { // 仅配置开启的包才提供深色模式功能 - return if (BuildConfig.IS_DARK_MODE_ON) { - val uiMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK - uiMode == Configuration.UI_MODE_NIGHT_YES - } else { - false - } + val uiMode = context.resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK + return uiMode == Configuration.UI_MODE_NIGHT_YES } /** @@ -47,18 +46,31 @@ object DarkModeUtils { * @param followSystem 是否是跟随系统 * @param darkModeIsOn 是否是深色模式 */ - fun initDarkMode(followSystem: Boolean, darkModeIsOn: Boolean) { + private fun initDarkMode(followSystem: Boolean, darkModeIsOn: Boolean) { // 仅配置开启的包才提供深色模式功能 - if (BuildConfig.IS_DARK_MODE_ON) { - if (followSystem) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + if (followSystem) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_FOLLOW_SYSTEM) + } else { + if (darkModeIsOn) { + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) } else { - if (darkModeIsOn) { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_YES) - } else { - AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) - } + AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO) } } + + // 通知 APP 的颜色显示模式可能有变更 + AppExecutor.uiExecutor.executeWithDelay({ + for (listener in mModeChangeListenerList) { + listener.invoke() + } + }, 500) + } + + fun registerModeChangeListener(listener: () -> Unit) { + mModeChangeListenerList.add(listener) + } + + fun unRegisterModeChangeListener(listener: () -> Unit) { + mModeChangeListenerList.remove(listener) } } \ No newline at end of file diff --git a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt index 666bb45abe..4a621c78f9 100644 --- a/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt +++ b/module_common/src/main/java/com/gh/gamecenter/common/utils/Extensions.kt @@ -29,6 +29,7 @@ import androidx.core.content.ContextCompat import androidx.core.text.HtmlCompat import androidx.fragment.app.Fragment import androidx.fragment.app.FragmentActivity +import androidx.fragment.app.FragmentManager import androidx.lifecycle.* import androidx.lifecycle.Observer import androidx.recyclerview.widget.LinearSmoothScroller @@ -186,6 +187,23 @@ inline fun Fragment.fragmentFromParentFragment() = T::class.java.canonicalName ) as T +/** + * 注册 fragmentDestroy 回调 + */ +fun Fragment.doOnFragmentDestroy(onDestroyAction: () -> Unit) { + if (!isAdded) return + + fragmentManager?.registerFragmentLifecycleCallbacks(object : FragmentManager.FragmentLifecycleCallbacks() { + override fun onFragmentDestroyed(fm: FragmentManager, f: Fragment) { + super.onFragmentDestroyed(fm, f) + if (this@doOnFragmentDestroy == f) { + this@doOnFragmentDestroy.fragmentManager?.unregisterFragmentLifecycleCallbacks(this) + } + + onDestroyAction.invoke() + } + }, false) +} /** * RecyclerView Extensions @@ -710,58 +728,16 @@ fun PopupWindow.showAutoOrientation(anchorView: View, distanceY: Int = 0, distan /** * 权限相关 */ -fun Fragment.checkReadPhoneStateAndStoragePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction( - requireContext(), - object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) -} - -fun Fragment.checkReadPhoneStatePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkReadPhoneStatePermissionBeforeAction( - requireContext(), - object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) -} - fun Fragment.checkStoragePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkStoragePermissionBeforeAction(requireContext(), object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) -} - -fun FragmentActivity.checkReadPhoneStateAndStoragePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkReadPhoneStateAndStoragePermissionBeforeAction( - this, - object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) -} - -fun FragmentActivity.checkReadPhoneStatePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkReadPhoneStatePermissionBeforeAction(this, object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) + PermissionHelper.checkStoragePermissionBeforeAction(requireContext()) { + action.invoke() + } } fun FragmentActivity.checkStoragePermissionBeforeAction(action: (() -> Unit)) { - PermissionHelper.checkStoragePermissionBeforeAction(this, object : EmptyCallback { - override fun onCallback() { - action.invoke() - } - }) + PermissionHelper.checkStoragePermissionBeforeAction(this) { + action.invoke() + } } /** @@ -1394,7 +1370,7 @@ fun WebView.enableForceDark(isDarkModeOn: Boolean) { throwable.printStackTrace() false } - if (BuildConfig.IS_DARK_MODE_ON && isFeatureSupported) { + if (isFeatureSupported) { WebSettingsCompat.setForceDark( settings, if (isDarkModeOn) WebSettingsCompat.FORCE_DARK_ON else WebSettingsCompat.FORCE_DARK_OFF @@ -1414,12 +1390,10 @@ fun WebView.setTransparentBackground() { * 深色模式切换更新状态栏底色 */ fun Activity.updateStatusBarColor(@ColorRes nightColor: Int, @ColorRes dayColor: Int) { - if (BuildConfig.IS_DARK_MODE_ON) { - DisplayUtils.setStatusBarColor( - this, - if (DarkModeUtils.isDarkModeOn(this)) nightColor else dayColor - ) - } + DisplayUtils.setStatusBarColor( + this, + if (DarkModeUtils.isDarkModeOn(this)) nightColor else dayColor + ) } /** @@ -1461,9 +1435,11 @@ fun WebView.fixUiModeIfNeeded() { configurationNightMode == Configuration.UI_MODE_NIGHT_NO && appCompatNightMode == AppCompatDelegate.MODE_NIGHT_YES -> { Configuration.UI_MODE_NIGHT_YES or (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) } + configurationNightMode == Configuration.UI_MODE_NIGHT_YES && appCompatNightMode == AppCompatDelegate.MODE_NIGHT_NO -> { Configuration.UI_MODE_NIGHT_NO or (configuration.uiMode and Configuration.UI_MODE_NIGHT_MASK.inv()) } + else -> null } diff --git a/module_common/src/main/res/layout/fragment_download.xml b/module_common/src/main/res/layout/fragment_download.xml new file mode 100644 index 0000000000..e22e0f0333 --- /dev/null +++ b/module_common/src/main/res/layout/fragment_download.xml @@ -0,0 +1,53 @@ + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IAdProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IAdProvider.kt deleted file mode 100644 index 292c97405f..0000000000 --- a/module_core/src/main/java/com/gh/gamecenter/core/provider/IAdProvider.kt +++ /dev/null @@ -1,26 +0,0 @@ -package com.gh.gamecenter.core.provider - -import android.app.Activity -import android.content.Context -import android.view.ViewGroup -import com.alibaba.android.arouter.facade.template.IProvider - -interface IAdProvider : IProvider { - - fun initSDK(context: Context) - - fun requestStartUpAd( - activity: Activity, - slotId: String, - adViewWidthInPx: Int, - adViewHeightInPx: Int, - adViewWidthInDp: Float, - adViewHeightInDp: Float, - startAdContainer: ViewGroup, - hideCallback: () -> Unit - ) - - fun cancelStartUpAd(context: Context) - - fun shouldEnableSDK(channel: String): Boolean -} \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/IBeiziAdProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBeiziAdProvider.kt new file mode 100644 index 0000000000..4b4f816ef8 --- /dev/null +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/IBeiziAdProvider.kt @@ -0,0 +1,23 @@ +package com.gh.gamecenter.core.provider + +import android.content.Context +import android.view.View +import android.widget.FrameLayout +import com.alibaba.android.arouter.facade.template.IProvider + +interface IBeiziAdProvider : IProvider { + + fun initSDK(context: Context) + + fun requestSplashAd( + startAdContainer: View, + adsFl: FrameLayout, + adViewWidthInPx: Int, + adViewHeightInPx: Int, + callback: (isSuccess: Boolean) -> Unit, + ) + + fun cancelSplashAd(context: Context) + + fun shouldEnableSDK(channel: String): Boolean +} \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/provider/ICsjAdProvider.kt b/module_core/src/main/java/com/gh/gamecenter/core/provider/ICsjAdProvider.kt new file mode 100644 index 0000000000..6854019e46 --- /dev/null +++ b/module_core/src/main/java/com/gh/gamecenter/core/provider/ICsjAdProvider.kt @@ -0,0 +1,46 @@ +package com.gh.gamecenter.core.provider + +import android.app.Activity +import android.content.Context +import android.view.ViewGroup +import androidx.fragment.app.Fragment +import com.alibaba.android.arouter.facade.template.IProvider + +interface ICsjAdProvider : IProvider { + + fun initSDK(context: Context, oaid: String) + + fun requestSplashAd( + activity: Activity, + slotId: String, + adViewWidthInPx: Int, + adViewHeightInPx: Int, + adViewWidthInDp: Float, + adViewHeightInDp: Float, + startAdContainer: ViewGroup, + callback: (isSuccess: Boolean) -> Unit + ) + + fun requestFlowAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewAcceptedSize: Float, + callback: (isSuccess: Boolean) -> Unit + ) + + fun requestBannerAd( + fragment: Fragment, + containerView: ViewGroup, + slotId: String, + expressViewWidthInDp: Float, + expressViewHeightInDp: Float, + callback: (isSuccess: Boolean) -> Unit + ) + + fun updateThemeStatus(context: Context, isDarkMode: Boolean) + + fun cancelSplashAd(context: Context) + + fun shouldEnableSDK(channel: String): Boolean +} \ No newline at end of file diff --git a/module_core/src/main/java/com/gh/gamecenter/core/utils/DisplayUtils.java b/module_core/src/main/java/com/gh/gamecenter/core/utils/DisplayUtils.java index 61a5a296e0..c33781defb 100644 --- a/module_core/src/main/java/com/gh/gamecenter/core/utils/DisplayUtils.java +++ b/module_core/src/main/java/com/gh/gamecenter/core/utils/DisplayUtils.java @@ -333,6 +333,13 @@ public class DisplayUtils { return metrics.heightPixels; } + public static float getScreenWidthInDp(Activity activity) { + WindowManager manager = (WindowManager) activity.getSystemService(Context.WINDOW_SERVICE); + DisplayMetrics metrics = new DisplayMetrics(); + manager.getDefaultDisplay().getMetrics(metrics); + return DisplayUtils.px2dip(activity, metrics.widthPixels); + } + public static int getToastOffset() { try { int i = Resources.getSystem().getIdentifier("toast_y_offset", "dimen", "android"); diff --git a/module_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt b/module_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt index 978c10e45c..10ecf30e56 100644 --- a/module_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt +++ b/module_feedback/src/main/java/com/gh/gamecenter/feedback/view/help/QaFeedbackDialogFragment.kt @@ -11,7 +11,6 @@ import androidx.fragment.app.FragmentTransaction import androidx.lifecycle.Lifecycle import androidx.recyclerview.widget.GridLayoutManager import com.alibaba.android.arouter.launcher.ARouter -import com.gh.gamecenter.common.BuildConfig import com.gh.gamecenter.common.constant.Constants import com.gh.gamecenter.common.constant.RouteConsts import com.gh.gamecenter.common.entity.SuggestType @@ -59,11 +58,7 @@ class QaFeedbackDialogFragment : BaseDialogFragment() { mLoadingDialog?.dismiss() } - mIsDarkModeOn = if (BuildConfig.IS_DARK_MODE_ON) { - isDarkModeOn(requireContext()) - } else { - false - } + mIsDarkModeOn = isDarkModeOn(requireContext()) } override fun onViewCreated(view: View, savedInstanceState: Bundle?) {