diff --git a/.idea/deploymentTargetSelector.xml b/.idea/deploymentTargetSelector.xml new file mode 100644 index 0000000..b268ef3 --- /dev/null +++ b/.idea/deploymentTargetSelector.xml @@ -0,0 +1,10 @@ + + + + + + + + + \ No newline at end of file diff --git a/.safedk/api/SafeDKAndroid-6.2.6.jar b/.safedk/api/SafeDKAndroid-6.2.6.jar new file mode 100644 index 0000000..aafde87 Binary files /dev/null and b/.safedk/api/SafeDKAndroid-6.2.6.jar differ diff --git a/.safedk/proguard-safedk.pro b/.safedk/proguard-safedk.pro new file mode 100644 index 0000000..b1e9563 --- /dev/null +++ b/.safedk/proguard-safedk.pro @@ -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.** { *; } diff --git a/app/build.gradle.kts b/app/build.gradle.kts index fcbfcb7..cc7a965 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -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") } \ No newline at end of file diff --git a/app/libs/adlibrary_20240614_1058_release.aar b/app/libs/adlibrary_20240614_1058_release.aar new file mode 100644 index 0000000..815b2e8 Binary files /dev/null and b/app/libs/adlibrary_20240614_1058_release.aar differ diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index dccc7a3..dbb928b 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -29,6 +29,11 @@ android:supportsRtl="true" android:theme="@style/Theme.HiMelody" tools:targetApi="31"> + + + + // 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 } + } + } +} \ No newline at end of file diff --git a/app/src/main/java/melody/offline/music/ads/LolAdWrapper.kt b/app/src/main/java/melody/offline/music/ads/LolAdWrapper.kt new file mode 100644 index 0000000..48dc3f2 --- /dev/null +++ b/app/src/main/java/melody/offline/music/ads/LolAdWrapper.kt @@ -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) + } + } +} + diff --git a/app/src/main/java/melody/offline/music/firebase/Constants.kt b/app/src/main/java/melody/offline/music/firebase/Constants.kt index d5c8247..3086668 100644 --- a/app/src/main/java/melody/offline/music/firebase/Constants.kt +++ b/app/src/main/java/melody/offline/music/firebase/Constants.kt @@ -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 + }] + } +} + """ } diff --git a/app/src/main/java/melody/offline/music/sp/AppStore.kt b/app/src/main/java/melody/offline/music/sp/AppStore.kt index 7a63267..6621233 100644 --- a/app/src/main/java/melody/offline/music/sp/AppStore.kt +++ b/app/src/main/java/melody/offline/music/sp/AppStore.kt @@ -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" diff --git a/app/src/main/java/melody/offline/music/util/AnalysisUtil.kt b/app/src/main/java/melody/offline/music/util/AnalysisUtil.kt index c1ad5b3..3f0abb9 100644 --- a/app/src/main/java/melody/offline/music/util/AnalysisUtil.kt +++ b/app/src/main/java/melody/offline/music/util/AnalysisUtil.kt @@ -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? = null) { @@ -57,4 +64,30 @@ object AnalysisUtil { } } } + + /** + * 统一广告打点方法 + */ + fun placeToLogEvent( + place: String, state: Int, param: Map? = 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) + } + } + } + } + } } \ No newline at end of file diff --git a/settings.gradle.kts b/settings.gradle.kts index f192500..90bda9c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") } }