添加admob广告

This commit is contained in:
ocean 2024-06-14 14:03:28 +08:00
parent e82f165c06
commit 459b6716b4
16 changed files with 483 additions and 13 deletions

10
.idea/deploymentTargetSelector.xml generated Normal file
View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="deploymentTargetSelector">
<selectionStates>
<SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" />
</SelectionState>
</selectionStates>
</component>
</project>

Binary file not shown.

View File

@ -0,0 +1,34 @@
-keep class androidx.multidex.** { *; }
-keep class androidx.browser.customtabs.CustomTabsIntent { *; }
-keep class androidx.** {
*** startActivityForResult(***);
*** startActivity(***);
}
-keep class android.support.multidex.** { *; }
-keep class android.support.v4.app.** { *; }
-keep class com.google.android.gms.location.FusedLocationProviderApi { *; }
-keep class com.google.android.gms.location.LocationListener { *; }
-keep class io.fabric.sdk.android.** { *; }
-keep class okio.** { *; }
-keep class retrofit2.** { *; }
-keep class okhttp3.** { *; }
-keep class com.squareup.okhttp.** { *; }
-keep class com.android.volley.** { *; }
-keep class com.flurry.** { *; }
-keep class org.apache.** { *; }
-keep class com.applovin.** { *; }
-keep class com.google.android.gms.ads.** { *; }
-keep class com.ironsource.** { *; }
-keep class com.fyber.inneractive.** { *; }
-keep class com.vungle.** { *; }
-keep class com.unity3d.ads.** { *; }
-keep class com.unity3d.services.** { *; }
-keep class com.mintegral.msdk.** { *; }
-keep class com.mbridge.msdk.** { *; }
-keep class com.adcolony.sdk.** { *; }
-keep class com.inmobi.** { *; }
-keep class com.five_corp.** { *; }
-keep class com.bytedance.** { *; }
-keep class com.smaato.** { *; }
-keep class com.safedk.** { *; }
-keep class com.applovin.quality.** { *; }

View File

@ -21,7 +21,10 @@ android {
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
setProperty("archivesBaseName", "HiMelody_${defaultConfig.versionName}(${defaultConfig.versionCode})")
setProperty(
"archivesBaseName",
"HiMelody_${defaultConfig.versionName}(${defaultConfig.versionCode})"
)
}
buildTypes {
@ -55,14 +58,14 @@ android {
}
dependencies {
implementation("androidx.core:core-ktx:1.12.0")
implementation("androidx.appcompat:appcompat:1.6.1")
implementation("com.google.android.material:material:1.11.0")
implementation(fileTree(mapOf("dir" to "libs", "include" to listOf("*.aar"))))
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
implementation("com.google.android.material:material:1.12.0")
implementation("androidx.constraintlayout:constraintlayout:2.1.4")
implementation("androidx.media3:media3-session:1.3.1")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.0")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.3")
implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-android:1.8.0")
testImplementation("junit:junit:4.13.2")
androidTestImplementation("androidx.test.ext:junit:1.1.5")
@ -95,14 +98,40 @@ dependencies {
implementation("com.google.android.flexbox:flexbox:3.0.0")
implementation("io.github.scwang90:refresh-layout-kernel:2.1.0")
implementation("io.github.scwang90:refresh-footer-ball:2.1.0")
implementation ("com.google.code.gson:gson:2.10.1")
implementation("com.google.code.gson:gson:2.10.1")
//google
// implementation("com.google.android.gms:play-services-ads-identifier:18.0.1")
implementation("com.google.android.gms:play-services-ads-identifier:18.1.0")
// Import the Firebase BoM
implementation(platform("com.google.firebase:firebase-bom:32.3.1"))
implementation(platform("com.google.firebase:firebase-bom:33.1.0"))
implementation("com.google.firebase:firebase-analytics-ktx")
implementation("com.google.firebase:firebase-crashlytics-ktx")
implementation("com.google.firebase:firebase-config")
//google ads
implementation("com.google.android.gms:play-services-ads:23.1.0")
implementation("com.google.ads.mediation:adcolony:4.8.0.2")
implementation("com.google.ads.mediation:applovin:12.5.0.0")
implementation("com.google.ads.mediation:vungle:6.12.0.0")
implementation("com.google.ads.mediation:facebook:6.17.0.0")
implementation("com.google.ads.mediation:mintegral:16.7.51.0")
implementation("com.google.ads.mediation:pangle:6.0.0.5.0")
implementation("com.unity3d.ads:unity-ads:4.6.1")
implementation("com.google.ads.mediation:unity:4.12.0.0")
implementation("com.google.ads.mediation:ironsource:8.1.0.0")
//max
implementation("com.applovin:applovin-sdk:12.5.0")
implementation("com.applovin.mediation:google-adapter:22.1.0.0")
implementation("com.applovin.mediation:facebook-adapter:6.11.0.5")
implementation("com.applovin.mediation:adcolony-adapter:4.8.0.2")
implementation("com.applovin.mediation:vungle-adapter:6.12.0.0")
implementation("com.applovin.mediation:bytedance-adapter:4.7.0.8.0")
implementation("com.applovin.mediation:mintegral-adapter:16.2.31.0")
implementation("androidx.recyclerview:recyclerview:1.3.2")//mintegral 需要
implementation("com.applovin.mediation:unityads-adapter:4.4.1.0")
implementation("com.applovin.mediation:smaato-adapter:21.8.5.0")
implementation("com.applovin.mediation:tapjoy-adapter:12.11.0.0")
implementation("com.applovin.mediation:ironsource-adapter:7.3.1.1.0")
}

Binary file not shown.

View File

@ -29,6 +29,11 @@
android:supportsRtl="true"
android:theme="@style/Theme.HiMelody"
tools:targetApi="31">
<meta-data
android:name="com.google.android.gms.ads.APPLICATION_ID"
android:value="ca-app-pub-3940256099942544~3347511713" />
<activity
android:name=".activity.LaunchActivity"
android:exported="true"

View File

@ -2,8 +2,12 @@ package melody.offline.music
import android.app.Application
import android.content.Context
import android.util.Log
import androidx.annotation.OptIn
import androidx.media3.common.util.UnstableApi
import com.google.android.gms.ads.MobileAds
import com.lol.apex.ok.google.adlibrary.LoLAds
import com.lol.apex.ok.google.adlibrary.bean.constants.TestMode
import melody.offline.music.bean.Audio
import melody.offline.music.bean.CurrentPlayingAudio
import melody.offline.music.bean.ResourcesList
@ -23,6 +27,7 @@ import melody.offline.music.database.AppFavoriteDBManager
import melody.offline.music.firebase.RemoteConfig
import melody.offline.music.http.CommonIpInfoUtil
import melody.offline.music.http.UploadEventName
import melody.offline.music.sp.AppStore
import melody.offline.music.util.AnalysisUtil
import melody.offline.music.util.AppLifecycleHandler
import java.io.BufferedReader
@ -118,6 +123,7 @@ class App : Application() {
AnalysisUtil.logEvent(AnalysisUtil.USER_LAUNCH)
app = this
AppLifecycleHandler(this)
initAd()
CommonIpInfoUtil.shared.initIPInfo()
UploadEventName.shared.init(this)
RemoteConfig.instance.init(this)
@ -133,4 +139,13 @@ class App : Application() {
CacheManager.initializeCaches(this)
DownloadUtil.getDownloadManager(this)
}
private fun initAd() {
LoLAds.initialize(this)
LoLAds.setLogEnable(BuildConfig.DEBUG)//是否打开⽇志和遮罩层颜色release要设置成false
LoLAds.setTestEnable(BuildConfig.DEBUG)//false使用 setAdConfig 中的广告配置
LoLAds.setTestMode(TestMode.REMOTE_TEST)//setTestEnable为true时生效LOCAL_TEST: 会使用sdk内的测试广告; REMOTE_TEST: 会将 setAdConfig 添加的配置中的广告id替换成测试id
LoLAds.setAdConfigKey("Music")//服务器端中⼴告json的key⻅⼴告配置json详情
LoLAds.setAdConfig(AppStore(this).adJson)//设置自定义广告配置json
}
}

View File

@ -3,13 +3,18 @@ package melody.offline.music.activity
import android.content.Intent
import android.os.CountDownTimer
import com.gyf.immersionbar.ktx.immersionBar
import com.lol.apex.ok.google.adlibrary.LoLAds
import com.lol.apex.ok.google.adlibrary.base.listener.LolLoadError
import com.lol.apex.ok.google.adlibrary.base.listener.LolShowError
import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.LolAdWrapper
import melody.offline.music.databinding.ActivityLaunchBinding
import melody.offline.music.util.AnalysisUtil
class LaunchActivity : MoBaseActivity() {
private lateinit var binding: ActivityLaunchBinding
private val totalTime = 8000L // 5秒
private val interval = 50L // 更新间隔,毫秒
private var interval = 50L // 更新间隔,毫秒
private val steps = totalTime / interval
private val progressPerStep = 100f / steps.toFloat()
@ -17,7 +22,7 @@ class LaunchActivity : MoBaseActivity() {
binding = ActivityLaunchBinding.inflate(layoutInflater)
setContentView(binding.root)
initTimer()
loadAd()
immersionBar {
fullScreen(true)
statusBarDarkFont(false)
@ -33,7 +38,7 @@ class LaunchActivity : MoBaseActivity() {
override fun onFinish() {
progressBar.setProgress(100f)
toMainActivity()
showAd()
}
}
@ -49,4 +54,30 @@ class LaunchActivity : MoBaseActivity() {
AnalysisUtil.logEvent(AnalysisUtil.LAUNCH_PV)
finish()
}
private fun loadAd() {
LolAdWrapper.shared.loadAd(
this,
AdPlacement.INST_SPLASH,
object : LolAdWrapper.LoLLoadListener {
override fun loadFailed(error: LolLoadError?) {
}
})
}
private fun showAd() {
LolAdWrapper.shared.showAdIfCached(
this,
AdPlacement.INST_SPLASH,
object : LolAdWrapper.LolShowListener {
override fun closed() {
toMainActivity()
}
override fun showFailed(error: LolShowError?) {
toMainActivity()
}
})
}
}

View File

@ -0,0 +1,5 @@
package melody.offline.music.ads
object AdPlacement {
const val INST_SPLASH = "inst_splash"//启动页广告
}

View File

@ -0,0 +1,9 @@
package melody.offline.music.ads
object AnalysisAdState {
const val AD_LOAD_FAILED = 1//广告load失败
const val AD_LOADED = 2//广告load成功
const val AD_SHOW_CLOSED = 3//广告show成功后被关闭
const val AD_SHOW_FAILED = 4//广告show失败
const val AD_SHOWN = 5//广告show成功
}

View File

@ -0,0 +1,94 @@
package pkg.com.master.ae.safevpn.ads
import android.app.Activity
import android.content.Context
import com.google.android.ump.ConsentDebugSettings
import com.google.android.ump.ConsentForm.OnConsentFormDismissedListener
import com.google.android.ump.ConsentInformation
import com.google.android.ump.ConsentRequestParameters
import com.google.android.ump.FormError
import com.google.android.ump.UserMessagingPlatform
/**
* The Google Mobile Ads SDK provides the User Messaging Platform (Google's IAB Certified consent
* management platform) as one solution to capture consent for users in GDPR impacted countries.
* This is an example and you can choose another consent management platform to capture consent.
*/
class GoogleMobileAdsConsentManager private constructor(context: Context) {
private val consentInformation: ConsentInformation =
UserMessagingPlatform.getConsentInformation(context)
/** Interface definition for a callback to be invoked when consent gathering is complete. */
fun interface OnConsentGatheringCompleteListener {
fun consentGatheringComplete(error: FormError?)
}
fun reset(){
consentInformation.reset()
}
/** Helper variable to determine if the app can request ads. */
val canRequestAds: Boolean
get() = consentInformation.canRequestAds()
/** Helper variable to determine if the privacy options form is required. */
val isPrivacyOptionsRequired: Boolean
get() =
consentInformation.privacyOptionsRequirementStatus ==
ConsentInformation.PrivacyOptionsRequirementStatus.REQUIRED
/**
* Helper method to call the UMP SDK methods to request consent information and load/show a
* consent form if necessary.
*/
fun gatherConsent(
activity: Activity,
onConsentGatheringCompleteListener: OnConsentGatheringCompleteListener
) {
// For testing purposes, you can force a DebugGeography of EEA or NOT_EEA.
val debugSettings =
ConsentDebugSettings.Builder(activity)
// .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA)
// Check your logcat output for the hashed device ID e.g.
// "Use new ConsentDebugSettings.Builder().addTestDeviceHashedId("ABCDEF012345")" to use
// the debug functionality.
// .setDebugGeography(ConsentDebugSettings.DebugGeography.DEBUG_GEOGRAPHY_EEA)
// .addTestDeviceHashedId("20715045029186978F3E1F11B9C6B6DD")
.build()
val params = ConsentRequestParameters.Builder().setConsentDebugSettings(debugSettings).build()
// Requesting an update to consent information should be called on every app launch.
consentInformation.requestConsentInfoUpdate(
activity,
params,
{
UserMessagingPlatform.loadAndShowConsentFormIfRequired(activity) { formError ->
// Consent has been gathered.
onConsentGatheringCompleteListener.consentGatheringComplete(formError)
}
},
{ requestConsentError ->
onConsentGatheringCompleteListener.consentGatheringComplete(requestConsentError)
}
)
}
/** Helper method to call the UMP SDK method to show the privacy options form. */
fun showPrivacyOptionsForm(
activity: Activity,
onConsentFormDismissedListener: OnConsentFormDismissedListener
) {
UserMessagingPlatform.showPrivacyOptionsForm(activity, onConsentFormDismissedListener)
}
companion object {
@Volatile private var instance: GoogleMobileAdsConsentManager? = null
fun getInstance(context: Context) =
instance
?: synchronized(this) {
instance ?: GoogleMobileAdsConsentManager(context).also { instance = it }
}
}
}

View File

@ -0,0 +1,121 @@
package melody.offline.music.ads
import android.app.Activity
import com.lol.apex.ok.google.adlibrary.base.listener.AdLoadListener
import com.lol.apex.ok.google.adlibrary.base.listener.AdShowListener
import com.lol.apex.ok.google.adlibrary.base.listener.LolLoadError
import com.lol.apex.ok.google.adlibrary.base.listener.LolShowError
import com.lol.apex.ok.google.adlibrary.inst.LOLAdsInstDispatcher
import com.lol.apex.ok.google.adlibrary.rewarded.LOLAdsRewardedDispatcher
import melody.offline.music.App
import melody.offline.music.util.AnalysisUtil
class LolAdWrapper {
companion object {
val shared: LolAdWrapper by lazy { LolAdWrapper() }
}
interface LoLLoadListener {
fun loadFailed(error: LolLoadError?) {}
fun loaded() {}
}
interface LolShowListener {
fun shown() {}
fun showFailed(error: LolShowError?) {}
fun closed() {}
}
fun hasCache(placement: String): Boolean {
return LOLAdsInstDispatcher.canShow(placement, false)
}
fun hasRewardCache(placement: String): Boolean {
return LOLAdsRewardedDispatcher.canShow(placement)
}
fun loadAd(act: Activity, placement: String, listener: LoLLoadListener? = null) {
if (act.isFinishing) return
LOLAdsInstDispatcher.getLoader(act, placement, object : AdLoadListener {
override fun onAdLoadFailed(error: LolLoadError?) {
//load广告失败打点
val map = mutableMapOf(Pair(AnalysisUtil.PARAM_VALUE, "${error?.msg}"))
AnalysisUtil.placeToLogEvent(placement, AnalysisAdState.AD_LOAD_FAILED, map)
listener?.loadFailed(error)
}
override fun onAdLoaded() {
listener?.loaded()
}
}).loadAd()
}
fun loadRewardAd(act: Activity, placement: String, listener: LoLLoadListener? = null) {
if (act.isFinishing) return
LOLAdsRewardedDispatcher.getLoader(act, placement, object : AdLoadListener {
override fun onAdLoadFailed(error: LolLoadError?) {
val map = mutableMapOf(Pair(AnalysisUtil.PARAM_VALUE, "${error?.msg}"))
AnalysisUtil.placeToLogEvent(placement, AnalysisAdState.AD_LOAD_FAILED, map)
listener?.loadFailed(error)
}
override fun onAdLoaded() {
listener?.loaded()
}
}).loadAd()
}
fun loadAdIfNotCached(act: Activity, placement: String, listener: LoLLoadListener? = null) {
if (act.isFinishing || hasCache(placement)) return
loadAd(act, placement, listener)
}
fun showAd(act: Activity, placement: String, listener: LolShowListener? = null) {
if (act.isFinishing) return
LOLAdsInstDispatcher.getShower(act, placement, object : AdShowListener {
override fun onAdClicked() {
App.app.isAdShowing.set(true)
}
override fun onAdClosed() {
App.app.isAdShowing.set(false)
listener?.closed()
}
override fun onAdRewarded() {}
override fun onAdShowFailed(error: LolShowError?) {
App.app.isAdShowing.set(false)
//广告show失败打点
val map = mutableMapOf(Pair(AnalysisUtil.PARAM_VALUE, "${error?.msg}"))
AnalysisUtil.placeToLogEvent(placement, AnalysisAdState.AD_SHOW_FAILED, map)
listener?.showFailed(error)
}
override fun onAdShown() {
App.app.isAdShowing.set(true)
listener?.shown()
AnalysisUtil.placeToLogEvent(placement, AnalysisAdState.AD_SHOWN)
}
override fun onAfterClickClosed() {}
override fun onDelayClosed() {}
}).showAd()
}
fun showAdIfCached(act: Activity, placement: String, listener: LolShowListener? = null) {
if (act.isFinishing || !hasCache(placement)) {
val map = mutableMapOf(Pair(AnalysisUtil.PARAM_VALUE, "No cache for ads"))
AnalysisUtil.placeToLogEvent(placement, AnalysisAdState.AD_SHOW_FAILED, map)
listener?.showFailed(LolShowError("No cache for ads"))
} else {
showAd(act, placement, listener)
}
}
}

View File

@ -9,5 +9,63 @@ object Constants {
"enter": false
}
"""
const val KEY_AD_JSON = "music_key_ad_json"
const val DEFAULT_AD_JSON = """
{
"AD_SHOW_LIMIT": {
"admob_inst": 100,
"admob_native": 100,
"max_banner": 100,
"max_inst": 100,
"max_native": 100
},
"sounds_inst_show_interval": 25000,
"LOLAds_EXPIRE_HOURS_NEW_USER": 20,
"Music_inst_splash": {
"data": [{
"after_click": {
"admob_inst": "keep",
"max_inst": "keep"
},
"block": {
"admob_inst": {
"delay": 0,
"rate": 0
},
"max_inst": {
"delay": 0,
"rate": 0
}
},
"close": {
"admob_inst": {
"delay": 0,
"rate": 0
},
"max_inst": {
"delay": 0,
"rate": 0
}
},
"config": [
[
"admob_inst",
{
"ca-app-pub-3940256099942544/1033173712": 100
}
]
],
"limit": {
"admob_inst": 100,
"max_inst": 100
},
"cycle": 0,
"timeout": 15000,
"showIntervalEnable": false
}]
}
}
"""
}

View File

@ -58,6 +58,12 @@ class AppStore(context: Context) {
defaultValue = Constants.DEFAULT_SHOULD_ENTER_MUSIC_JSON
)
//广告json-firebase配置
var adJson: String by store.string(
key = Constants.KEY_AD_JSON,
defaultValue = Constants.DEFAULT_AD_JSON
)
companion object {
private const val FILE_NAME = "music_oo_app"
const val SEARCH_HISTORY = "search_history"

View File

@ -5,6 +5,8 @@ import android.text.TextUtils
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.analytics.ktx.analytics
import com.google.firebase.ktx.Firebase
import melody.offline.music.ads.AdPlacement
import melody.offline.music.ads.AnalysisAdState
object AnalysisUtil {
@ -30,6 +32,11 @@ object AnalysisUtil {
const val SEARCH_RESULT_PV = "search_result_pv"//搜索结果曝光
const val SEARCH_RESULT_SUCCESS_ACTION = "search_result_success_action"//搜索有结果
private const val AD_USER_OPEN_SUCCESS = "ad_user_open_success"//首页开屏广告展示成功
private const val AD_USER_OPEN_FAIL = "ad_user_open_fail"//首页开屏广告展示失败
private const val AD_USER_OPEN_LOAD_FAIL = "ad_user_open_load_fail"//首页开屏广告展示load失败
private var firebaseAnalytics: FirebaseAnalytics? = null
fun logEvent(eventName: String, myParam: Map<String, String>? = null) {
@ -57,4 +64,30 @@ object AnalysisUtil {
}
}
}
/**
* 统一广告打点方法
*/
fun placeToLogEvent(
place: String, state: Int, param: Map<String, String>? = null
) {
when (place) {//对应广告位,进行打点。
//开屏广告,启动页广告位
AdPlacement.INST_SPLASH -> {
when (state) {
AnalysisAdState.AD_LOAD_FAILED -> {
logEvent(AD_USER_OPEN_LOAD_FAIL, param)
}
AnalysisAdState.AD_SHOW_FAILED -> {
logEvent(AD_USER_OPEN_FAIL, param)
}
AnalysisAdState.AD_SHOWN -> {
logEvent(AD_USER_OPEN_SUCCESS)
}
}
}
}
}
}

View File

@ -3,6 +3,9 @@ pluginManagement {
google()
mavenCentral()
gradlePluginPortal()
flatDir {
dirs("libs")
}
}
}
dependencyResolutionManagement {
@ -11,6 +14,23 @@ dependencyResolutionManagement {
google()
mavenCentral()
maven("https://jitpack.io")
//admob 聚合 pangle
maven("https://artifact.bytedance.com/repository/pangle/")
//admob 聚合 tapjoy
maven("https://sdk.tapjoy.com/")
//admob 聚合 is
maven("https://android-sdk.is.com/")
//max 聚合 pangle
maven("https://artifact.bytedance.com/repository/pangle")
//max 聚合 mintegral
maven("https://dl-maven-android.mintegral.com/repository/mbridge_android_sdk_oversea")
//max 聚合 smaato
maven("https://s3.amazonaws.com/smaato-sdk-releases/")
//max 聚合 tapjoy
maven("https://sdk.tapjoy.com")
//max 聚合 ironsource
maven("https://android-sdk.is.com")
}
}