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")
}
}